1use std::{
2 cmp::min,
3 fmt::Display,
4 num::NonZeroUsize,
5 sync::{LazyLock, Mutex},
6};
7
8use crate::utils::keccak;
9use bytes::Bytes;
10use ethereum_types::{Address, H256, U256};
11use ethrex_crypto::{Crypto, CryptoError, NativeCrypto};
12use lru::LruCache;
13pub use mempool::MempoolTransaction;
14
15const MAX_SIGNER_CACHE_ENTRIES: usize = 100_000;
16
17pub static GLOBAL_SIGNER_CACHE: LazyLock<Mutex<LruCache<H256, Address>>> = LazyLock::new(|| {
28 Mutex::new(LruCache::new(
29 NonZeroUsize::new(MAX_SIGNER_CACHE_ENTRIES).expect("MAX_SIGNER_CACHE_ENTRIES is non-zero"),
30 ))
31});
32use rkyv::{Archive, Deserialize as RDeserialize, Serialize as RSerialize};
33use serde::{Serialize, ser::SerializeStruct};
34pub use serde_impl::{
35 AccessListEntry, AuthorizationTupleEntry, FrameEntry, GenericTransaction,
36 GenericTransactionError,
37};
38
39pub const EIP1559_DEFAULT_SERIALIZED_LENGTH: usize = 15;
41
42use ethrex_rlp::{
43 constants::RLP_NULL,
44 decode::{RLPDecode, decode_bytes, decode_rlp_item},
45 encode::{PayloadRLPEncode, RLPEncode},
46 error::RLPDecodeError,
47 structs::{Decoder, Encoder},
48};
49
50#[cfg(all(feature = "eip-8025", target_arch = "riscv64"))]
51use super::eip8025_cell::OnceCell;
52use crate::types::{AccessList, AuthorizationList, BlobsBundle};
53#[cfg(not(all(feature = "eip-8025", target_arch = "riscv64")))]
54use once_cell::sync::OnceCell;
55
56#[derive(Clone, Debug, PartialEq, Eq, Serialize, RSerialize, RDeserialize, Archive)]
65#[serde(untagged)]
66pub enum Transaction {
67 LegacyTransaction(LegacyTransaction),
68 EIP2930Transaction(EIP2930Transaction),
69 EIP1559Transaction(EIP1559Transaction),
70 EIP4844Transaction(EIP4844Transaction),
71 EIP7702Transaction(EIP7702Transaction),
72 PrivilegedL2Transaction(PrivilegedL2Transaction),
73 FeeTokenTransaction(FeeTokenTransaction),
74 FrameTransaction(FrameTransaction),
75}
76
77#[derive(Clone, Debug, PartialEq, Eq)]
81pub enum P2PTransaction {
82 LegacyTransaction(LegacyTransaction),
83 EIP2930Transaction(EIP2930Transaction),
84 EIP1559Transaction(EIP1559Transaction),
85 EIP4844TransactionWithBlobs(WrappedEIP4844Transaction),
86 EIP7702Transaction(EIP7702Transaction),
87 FeeTokenTransaction(FeeTokenTransaction),
88 FrameTransaction(FrameTransaction),
89}
90
91impl TryInto<Transaction> for P2PTransaction {
92 type Error = String;
93
94 fn try_into(self) -> Result<Transaction, Self::Error> {
95 match self {
96 P2PTransaction::LegacyTransaction(itx) => Ok(Transaction::LegacyTransaction(itx)),
97 P2PTransaction::EIP2930Transaction(itx) => Ok(Transaction::EIP2930Transaction(itx)),
98 P2PTransaction::EIP1559Transaction(itx) => Ok(Transaction::EIP1559Transaction(itx)),
99 P2PTransaction::EIP7702Transaction(itx) => Ok(Transaction::EIP7702Transaction(itx)),
100 P2PTransaction::FrameTransaction(itx) => Ok(Transaction::FrameTransaction(itx)),
101 _ => Err("Can't convert blob p2p transaction into regular transaction. Blob bundle would be lost.".to_string()),
102 }
103 }
104}
105
106impl RLPEncode for P2PTransaction {
107 fn encode(&self, buf: &mut dyn bytes::BufMut) {
108 match self {
109 P2PTransaction::LegacyTransaction(t) => t.encode(buf),
110 tx => <[u8] as RLPEncode>::encode(&tx.encode_canonical_to_vec(), buf),
111 };
112 }
113}
114
115impl RLPDecode for P2PTransaction {
116 fn decode_unfinished(rlp: &[u8]) -> Result<(Self, &[u8]), RLPDecodeError> {
117 let (is_list, payload, remainder) = decode_rlp_item(rlp)?;
118 if !is_list {
119 let tx_type = payload.first().ok_or(RLPDecodeError::InvalidLength)?;
120 let tx_encoding = &payload.get(1..).ok_or(RLPDecodeError::InvalidLength)?;
121 match *tx_type {
123 0x0 => LegacyTransaction::decode(tx_encoding)
125 .map(|tx| (P2PTransaction::LegacyTransaction(tx), remainder)), 0x1 => EIP2930Transaction::decode(tx_encoding)
128 .map(|tx| (P2PTransaction::EIP2930Transaction(tx), remainder)),
129 0x2 => EIP1559Transaction::decode(tx_encoding)
131 .map(|tx| (P2PTransaction::EIP1559Transaction(tx), remainder)),
132 0x3 => WrappedEIP4844Transaction::decode(tx_encoding)
134 .map(|tx| (P2PTransaction::EIP4844TransactionWithBlobs(tx), remainder)),
135 0x4 => EIP7702Transaction::decode(tx_encoding)
137 .map(|tx| (P2PTransaction::EIP7702Transaction(tx), remainder)),
138 0x7d => FeeTokenTransaction::decode(tx_encoding)
140 .map(|tx| (P2PTransaction::FeeTokenTransaction(tx), remainder)),
141 0x06 => FrameTransaction::decode(tx_encoding)
143 .map(|tx| (P2PTransaction::FrameTransaction(tx), remainder)),
144 ty => Err(RLPDecodeError::Custom(format!(
145 "Invalid transaction type: {ty}"
146 ))),
147 }
148 } else {
149 LegacyTransaction::decode_unfinished(rlp)
151 .map(|(tx, rem)| (P2PTransaction::LegacyTransaction(tx), rem))
152 }
153 }
154}
155
156#[derive(Clone, Debug, PartialEq, Eq)]
157pub struct WrappedEIP4844Transaction {
158 pub tx: EIP4844Transaction,
159 pub wrapper_version: Option<u8>,
160 pub blobs_bundle: BlobsBundle,
161}
162
163impl RLPEncode for WrappedEIP4844Transaction {
164 fn encode(&self, buf: &mut dyn bytes::BufMut) {
165 let encoder = Encoder::new(buf);
166 encoder
167 .encode_field(&self.tx)
168 .encode_optional_field(&self.wrapper_version)
169 .encode_field(&self.blobs_bundle.blobs)
170 .encode_field(&self.blobs_bundle.commitments)
171 .encode_field(&self.blobs_bundle.proofs)
172 .finish();
173 }
174}
175
176impl RLPDecode for WrappedEIP4844Transaction {
177 fn decode_unfinished(rlp: &[u8]) -> Result<(WrappedEIP4844Transaction, &[u8]), RLPDecodeError> {
178 let decoder = Decoder::new(rlp)?;
179 let Ok((tx, decoder)) = decoder.decode_field("tx") else {
180 let (tx, rest) = EIP4844Transaction::decode_unfinished(rlp)?;
182 return Ok((
183 WrappedEIP4844Transaction {
184 tx,
185 wrapper_version: None,
186 blobs_bundle: BlobsBundle::empty(),
188 },
189 rest,
190 ));
191 };
192
193 let (wrapper_version, decoder) = decoder.decode_optional_field();
194 let (blobs, decoder) = decoder.decode_field("blobs")?;
195 let (commitments, decoder) = decoder.decode_field("commitments")?;
196 let (proofs, decoder) = decoder.decode_field("proofs")?;
197
198 let wrapped = WrappedEIP4844Transaction {
199 tx,
200 wrapper_version,
201 blobs_bundle: BlobsBundle {
202 blobs,
203 commitments,
204 proofs,
205 version: wrapper_version.unwrap_or_default(),
206 },
207 };
208 Ok((wrapped, decoder.finish()?))
209 }
210}
211
212#[derive(Clone, Debug, PartialEq, Eq, Default, RSerialize, RDeserialize, Archive)]
213pub struct LegacyTransaction {
214 pub nonce: u64,
215 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
216 pub gas_price: U256,
217 pub gas: u64,
218 pub to: TxKind,
221 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
222 pub value: U256,
223 #[rkyv(with=crate::rkyv_utils::BytesWrapper)]
224 pub data: Bytes,
225 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
226 pub v: U256,
227 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
228 pub r: U256,
229 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
230 pub s: U256,
231 #[rkyv(with=rkyv::with::Skip)]
232 pub inner_hash: OnceCell<H256>,
233 #[rkyv(with=rkyv::with::Skip)]
234 pub sender_cache: OnceCell<Address>,
235}
236
237#[derive(Clone, Debug, PartialEq, Eq, Default, RSerialize, RDeserialize, Archive)]
238pub struct EIP2930Transaction {
239 pub chain_id: u64,
240 pub nonce: u64,
241 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
242 pub gas_price: U256,
243 pub gas_limit: u64,
244 pub to: TxKind,
245 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
246 pub value: U256,
247 #[rkyv(with=crate::rkyv_utils::BytesWrapper)]
248 pub data: Bytes,
249 #[rkyv(with=rkyv::with::Map<crate::rkyv_utils::AccessListItemWrapper>)]
250 pub access_list: AccessList,
251 pub signature_y_parity: bool,
252 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
253 pub signature_r: U256,
254 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
255 pub signature_s: U256,
256 #[rkyv(with=rkyv::with::Skip)]
257 pub inner_hash: OnceCell<H256>,
258 #[rkyv(with=rkyv::with::Skip)]
259 pub sender_cache: OnceCell<Address>,
260 #[rkyv(with=rkyv::with::Skip)]
261 pub cached_canonical: OnceCell<Vec<u8>>,
262}
263
264#[derive(Clone, Debug, PartialEq, Eq, Default, RSerialize, RDeserialize, Archive)]
265pub struct EIP1559Transaction {
266 pub chain_id: u64,
267 pub nonce: u64,
268 pub max_priority_fee_per_gas: u64,
269 pub max_fee_per_gas: u64,
270 pub gas_limit: u64,
271 pub to: TxKind,
272 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
273 pub value: U256,
274 #[rkyv(with=crate::rkyv_utils::BytesWrapper)]
275 pub data: Bytes,
276 #[rkyv(with=rkyv::with::Map<crate::rkyv_utils::AccessListItemWrapper>)]
277 pub access_list: AccessList,
278 pub signature_y_parity: bool,
279 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
280 pub signature_r: U256,
281 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
282 pub signature_s: U256,
283 #[rkyv(with=rkyv::with::Skip)]
284 pub inner_hash: OnceCell<H256>,
285 #[rkyv(with=rkyv::with::Skip)]
286 pub sender_cache: OnceCell<Address>,
287 #[rkyv(with=rkyv::with::Skip)]
288 pub cached_canonical: OnceCell<Vec<u8>>,
289}
290
291#[derive(Clone, Debug, PartialEq, Eq, Default, RSerialize, RDeserialize, Archive)]
292pub struct EIP4844Transaction {
293 pub chain_id: u64,
294 pub nonce: u64,
295 pub max_priority_fee_per_gas: u64,
296 pub max_fee_per_gas: u64,
297 pub gas: u64,
298 #[rkyv(with=crate::rkyv_utils::H160Wrapper)]
299 pub to: Address,
300 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
301 pub value: U256,
302 #[rkyv(with=crate::rkyv_utils::BytesWrapper)]
303 pub data: Bytes,
304 #[rkyv(with=rkyv::with::Map<crate::rkyv_utils::AccessListItemWrapper>)]
305 pub access_list: AccessList,
306 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
307 pub max_fee_per_blob_gas: U256,
308 #[rkyv(with=rkyv::with::Map<crate::rkyv_utils::H256Wrapper>)]
309 pub blob_versioned_hashes: Vec<H256>,
310 pub signature_y_parity: bool,
311 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
312 pub signature_r: U256,
313 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
314 pub signature_s: U256,
315 #[rkyv(with=rkyv::with::Skip)]
316 pub inner_hash: OnceCell<H256>,
317 #[rkyv(with=rkyv::with::Skip)]
318 pub sender_cache: OnceCell<Address>,
319 #[rkyv(with=rkyv::with::Skip)]
320 pub cached_canonical: OnceCell<Vec<u8>>,
321}
322
323#[derive(Clone, Debug, PartialEq, Eq, Default, RSerialize, RDeserialize, Archive)]
324pub struct EIP7702Transaction {
325 pub chain_id: u64,
326 pub nonce: u64,
327 pub max_priority_fee_per_gas: u64,
328 pub max_fee_per_gas: u64,
329 pub gas_limit: u64,
330 #[rkyv(with=crate::rkyv_utils::H160Wrapper)]
331 pub to: Address,
332 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
333 pub value: U256,
334 #[rkyv(with=crate::rkyv_utils::BytesWrapper)]
335 pub data: Bytes,
336 #[rkyv(with=rkyv::with::Map<crate::rkyv_utils::AccessListItemWrapper>)]
337 pub access_list: AccessList,
338 pub authorization_list: AuthorizationList,
339 pub signature_y_parity: bool,
340 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
341 pub signature_r: U256,
342 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
343 pub signature_s: U256,
344 #[rkyv(with=rkyv::with::Skip)]
345 pub inner_hash: OnceCell<H256>,
346 #[rkyv(with=rkyv::with::Skip)]
347 pub sender_cache: OnceCell<Address>,
348 #[rkyv(with=rkyv::with::Skip)]
349 pub cached_canonical: OnceCell<Vec<u8>>,
350}
351#[derive(Clone, Debug, PartialEq, Eq, Default, RSerialize, RDeserialize, Archive)]
352pub struct PrivilegedL2Transaction {
353 pub chain_id: u64,
354 pub nonce: u64,
355 pub max_priority_fee_per_gas: u64,
356 pub max_fee_per_gas: u64,
357 pub gas_limit: u64,
358 pub to: TxKind,
359 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
360 pub value: U256,
361 #[rkyv(with=crate::rkyv_utils::BytesWrapper)]
362 pub data: Bytes,
363 #[rkyv(with=rkyv::with::Map<crate::rkyv_utils::AccessListItemWrapper>)]
364 pub access_list: AccessList,
365 #[rkyv(with=crate::rkyv_utils::H160Wrapper)]
366 pub from: Address,
367 #[rkyv(with=rkyv::with::Skip)]
368 pub inner_hash: OnceCell<H256>,
369 #[rkyv(with=rkyv::with::Skip)]
370 pub sender_cache: OnceCell<Address>,
371 #[rkyv(with=rkyv::with::Skip)]
372 pub cached_canonical: OnceCell<Vec<u8>>,
373}
374
375#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
376pub enum TxType {
377 #[default]
378 Legacy = 0x00,
379 EIP2930 = 0x01,
380 EIP1559 = 0x02,
381 EIP4844 = 0x03,
382 EIP7702 = 0x04,
383 Frame = 0x06,
384 FeeToken = 0x7d,
385 Privileged = 0x7e,
388}
389
390impl From<TxType> for u8 {
391 fn from(val: TxType) -> Self {
392 match val {
393 TxType::Legacy => 0x00,
394 TxType::EIP2930 => 0x01,
395 TxType::EIP1559 => 0x02,
396 TxType::EIP4844 => 0x03,
397 TxType::EIP7702 => 0x04,
398 TxType::Frame => 0x06,
399 TxType::FeeToken => 0x7d,
400 TxType::Privileged => 0x7e,
401 }
402 }
403}
404
405impl Display for TxType {
406 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
407 match self {
408 TxType::Legacy => write!(f, "Legacy"),
409 TxType::EIP2930 => write!(f, "EIP2930"),
410 TxType::EIP1559 => write!(f, "EIP1559"),
411 TxType::EIP4844 => write!(f, "EIP4844"),
412 TxType::EIP7702 => write!(f, "EIP7702"),
413 TxType::Frame => write!(f, "Frame"),
414 TxType::Privileged => write!(f, "Privileged"),
415 TxType::FeeToken => write!(f, "FeeToken"),
416 }
417 }
418}
419
420impl Transaction {
421 pub fn tx_type(&self) -> TxType {
422 match self {
423 Transaction::LegacyTransaction(_) => TxType::Legacy,
424 Transaction::EIP2930Transaction(_) => TxType::EIP2930,
425 Transaction::EIP1559Transaction(_) => TxType::EIP1559,
426 Transaction::EIP4844Transaction(_) => TxType::EIP4844,
427 Transaction::EIP7702Transaction(_) => TxType::EIP7702,
428 Transaction::FeeTokenTransaction(_) => TxType::FeeToken,
429 Transaction::PrivilegedL2Transaction(_) => TxType::Privileged,
430 Transaction::FrameTransaction(_) => TxType::Frame,
431 }
432 }
433
434 fn calc_effective_gas_price(&self, base_fee_per_gas: Option<u64>) -> Option<U256> {
435 let base_fee = base_fee_per_gas?;
436 let max_fee = self.max_fee_per_gas()?;
437 if max_fee < base_fee {
438 return None;
440 }
441
442 let priority_fee_per_gas = min(self.max_priority_fee()?, max_fee.saturating_sub(base_fee));
443 Some(U256::from(priority_fee_per_gas) + U256::from(base_fee))
444 }
445
446 pub fn effective_gas_price(&self, base_fee_per_gas: Option<u64>) -> Option<U256> {
447 match self.tx_type() {
448 TxType::Legacy => Some(self.gas_price()),
449 TxType::EIP2930 => Some(self.gas_price()),
450 TxType::EIP1559 => self.calc_effective_gas_price(base_fee_per_gas),
451 TxType::EIP4844 => self.calc_effective_gas_price(base_fee_per_gas),
452 TxType::EIP7702 => self.calc_effective_gas_price(base_fee_per_gas),
453 TxType::Frame => self.calc_effective_gas_price(base_fee_per_gas),
454 TxType::FeeToken => self.calc_effective_gas_price(base_fee_per_gas),
455 TxType::Privileged => Some(self.gas_price()),
456 }
457 }
458
459 pub fn cost_without_base_fee(&self) -> Option<U256> {
460 let price = match self.tx_type() {
461 TxType::Legacy => self.gas_price(),
462 TxType::EIP2930 => self.gas_price(),
463 TxType::EIP1559 => U256::from(self.max_fee_per_gas()?),
464 TxType::EIP4844 => U256::from(self.max_fee_per_gas()?),
465 TxType::EIP7702 => U256::from(self.max_fee_per_gas()?),
466 TxType::Frame => U256::from(self.max_fee_per_gas()?),
467 TxType::FeeToken => U256::from(self.max_fee_per_gas()?),
468 TxType::Privileged => self.gas_price(),
469 };
470
471 let base = U256::saturating_add(
472 U256::saturating_mul(price, self.gas_limit().into()),
473 self.value(),
474 );
475
476 if let Transaction::EIP4844Transaction(tx) = self {
480 let blob_gas = U256::from(crate::constants::GAS_PER_BLOB)
481 .saturating_mul(U256::from(tx.blob_versioned_hashes.len() as u64));
482 let blob_cost = blob_gas.saturating_mul(tx.max_fee_per_blob_gas);
483 return Some(base.saturating_add(blob_cost));
484 }
485
486 Some(base)
487 }
488
489 pub fn fee_token(&self) -> Option<Address> {
490 if let Transaction::FeeTokenTransaction(tx) = self {
491 Some(tx.fee_token)
492 } else {
493 None
494 }
495 }
496
497 fn cached_canonical_cell(&self) -> Option<&OnceCell<Vec<u8>>> {
500 match self {
501 Transaction::LegacyTransaction(_) => None,
502 Transaction::EIP2930Transaction(t) => Some(&t.cached_canonical),
503 Transaction::EIP1559Transaction(t) => Some(&t.cached_canonical),
504 Transaction::EIP4844Transaction(t) => Some(&t.cached_canonical),
505 Transaction::EIP7702Transaction(t) => Some(&t.cached_canonical),
506 Transaction::PrivilegedL2Transaction(t) => Some(&t.cached_canonical),
507 Transaction::FeeTokenTransaction(t) => Some(&t.cached_canonical),
508 Transaction::FrameTransaction(t) => Some(&t.cached_canonical),
509 }
510 }
511}
512
513impl RLPEncode for Transaction {
514 fn encode(&self, buf: &mut dyn bytes::BufMut) {
519 match self {
520 Transaction::LegacyTransaction(t) => t.encode(buf),
521 _ => {
522 let canonical = self.encode_canonical_to_vec();
523 <[u8] as RLPEncode>::encode(canonical.as_slice(), buf)
524 }
525 };
526 }
527}
528
529impl RLPDecode for Transaction {
530 fn decode_unfinished(rlp: &[u8]) -> Result<(Self, &[u8]), RLPDecodeError> {
535 let (is_list, payload, remainder) = decode_rlp_item(rlp)?;
536 if !is_list {
537 let tx_type = payload.first().ok_or(RLPDecodeError::InvalidLength)?;
538 let tx_encoding = &payload.get(1..).ok_or(RLPDecodeError::InvalidLength)?;
539 match *tx_type {
541 0x0 => LegacyTransaction::decode(tx_encoding)
543 .map(|tx| (Transaction::LegacyTransaction(tx), remainder)), 0x1 => EIP2930Transaction::decode(tx_encoding)
546 .map(|tx| (Transaction::EIP2930Transaction(tx), remainder)),
547 0x2 => EIP1559Transaction::decode(tx_encoding)
549 .map(|tx| (Transaction::EIP1559Transaction(tx), remainder)),
550 0x3 => EIP4844Transaction::decode(tx_encoding)
552 .map(|tx| (Transaction::EIP4844Transaction(tx), remainder)),
553 0x4 => EIP7702Transaction::decode(tx_encoding)
555 .map(|tx| (Transaction::EIP7702Transaction(tx), remainder)),
556 0x7d => FeeTokenTransaction::decode(tx_encoding)
558 .map(|tx| (Transaction::FeeTokenTransaction(tx), remainder)),
559 0x6 => FrameTransaction::decode(tx_encoding)
561 .map(|tx| (Transaction::FrameTransaction(tx), remainder)),
562 0x7e => PrivilegedL2Transaction::decode(tx_encoding)
564 .map(|tx| (Transaction::PrivilegedL2Transaction(tx), remainder)),
565 ty => Err(RLPDecodeError::Custom(format!(
566 "Invalid transaction type: {ty}"
567 ))),
568 }
569 } else {
570 LegacyTransaction::decode_unfinished(rlp)
572 .map(|(tx, rem)| (Transaction::LegacyTransaction(tx), rem))
573 }
574 }
575}
576
577#[derive(Clone, Debug, PartialEq, Eq, Default, RSerialize, RDeserialize, Archive)]
579pub enum TxKind {
580 Call(#[rkyv(with=crate::rkyv_utils::H160Wrapper)] Address),
581 #[default]
582 Create,
583}
584
585impl RLPEncode for TxKind {
586 fn encode(&self, buf: &mut dyn bytes::BufMut) {
587 match self {
588 Self::Call(address) => address.encode(buf),
589 Self::Create => buf.put_u8(RLP_NULL),
590 }
591 }
592}
593
594impl RLPDecode for TxKind {
595 fn decode_unfinished(rlp: &[u8]) -> Result<(Self, &[u8]), RLPDecodeError> {
596 let first_byte = rlp.first().ok_or(RLPDecodeError::InvalidLength)?;
597 if *first_byte == RLP_NULL {
598 return Ok((Self::Create, &rlp[1..]));
599 }
600 Address::decode_unfinished(rlp).map(|(t, rest)| (Self::Call(t), rest))
601 }
602}
603
604impl RLPEncode for LegacyTransaction {
605 fn encode(&self, buf: &mut dyn bytes::BufMut) {
606 Encoder::new(buf)
607 .encode_field(&self.nonce)
608 .encode_field(&self.gas_price)
609 .encode_field(&self.gas)
610 .encode_field(&self.to)
611 .encode_field(&self.value)
612 .encode_field(&self.data)
613 .encode_field(&self.v)
614 .encode_field(&self.r)
615 .encode_field(&self.s)
616 .finish();
617 }
618}
619
620impl RLPEncode for EIP2930Transaction {
621 fn encode(&self, buf: &mut dyn bytes::BufMut) {
622 Encoder::new(buf)
623 .encode_field(&self.chain_id)
624 .encode_field(&self.nonce)
625 .encode_field(&self.gas_price)
626 .encode_field(&self.gas_limit)
627 .encode_field(&self.to)
628 .encode_field(&self.value)
629 .encode_field(&self.data)
630 .encode_field(&self.access_list)
631 .encode_field(&self.signature_y_parity)
632 .encode_field(&self.signature_r)
633 .encode_field(&self.signature_s)
634 .finish()
635 }
636}
637
638impl RLPEncode for EIP1559Transaction {
639 fn encode(&self, buf: &mut dyn bytes::BufMut) {
640 Encoder::new(buf)
641 .encode_field(&self.chain_id)
642 .encode_field(&self.nonce)
643 .encode_field(&self.max_priority_fee_per_gas)
644 .encode_field(&self.max_fee_per_gas)
645 .encode_field(&self.gas_limit)
646 .encode_field(&self.to)
647 .encode_field(&self.value)
648 .encode_field(&self.data)
649 .encode_field(&self.access_list)
650 .encode_field(&self.signature_y_parity)
651 .encode_field(&self.signature_r)
652 .encode_field(&self.signature_s)
653 .finish()
654 }
655}
656
657impl RLPEncode for EIP4844Transaction {
658 fn encode(&self, buf: &mut dyn bytes::BufMut) {
659 Encoder::new(buf)
660 .encode_field(&self.chain_id)
661 .encode_field(&self.nonce)
662 .encode_field(&self.max_priority_fee_per_gas)
663 .encode_field(&self.max_fee_per_gas)
664 .encode_field(&self.gas)
665 .encode_field(&self.to)
666 .encode_field(&self.value)
667 .encode_field(&self.data)
668 .encode_field(&self.access_list)
669 .encode_field(&self.max_fee_per_blob_gas)
670 .encode_field(&self.blob_versioned_hashes)
671 .encode_field(&self.signature_y_parity)
672 .encode_field(&self.signature_r)
673 .encode_field(&self.signature_s)
674 .finish()
675 }
676}
677
678impl RLPEncode for EIP7702Transaction {
679 fn encode(&self, buf: &mut dyn bytes::BufMut) {
680 Encoder::new(buf)
681 .encode_field(&self.chain_id)
682 .encode_field(&self.nonce)
683 .encode_field(&self.max_priority_fee_per_gas)
684 .encode_field(&self.max_fee_per_gas)
685 .encode_field(&self.gas_limit)
686 .encode_field(&self.to)
687 .encode_field(&self.value)
688 .encode_field(&self.data)
689 .encode_field(&self.access_list)
690 .encode_field(&self.authorization_list)
691 .encode_field(&self.signature_y_parity)
692 .encode_field(&self.signature_r)
693 .encode_field(&self.signature_s)
694 .finish()
695 }
696}
697
698impl RLPEncode for PrivilegedL2Transaction {
699 fn encode(&self, buf: &mut dyn bytes::BufMut) {
700 Encoder::new(buf)
701 .encode_field(&self.chain_id)
702 .encode_field(&self.nonce)
703 .encode_field(&self.max_priority_fee_per_gas)
704 .encode_field(&self.max_fee_per_gas)
705 .encode_field(&self.gas_limit)
706 .encode_field(&self.to)
707 .encode_field(&self.value)
708 .encode_field(&self.data)
709 .encode_field(&self.access_list)
710 .encode_field(&self.from)
711 .finish()
712 }
713}
714
715impl RLPEncode for FeeTokenTransaction {
716 fn encode(&self, buf: &mut dyn bytes::BufMut) {
717 Encoder::new(buf)
718 .encode_field(&self.chain_id)
719 .encode_field(&self.nonce)
720 .encode_field(&self.max_priority_fee_per_gas)
721 .encode_field(&self.max_fee_per_gas)
722 .encode_field(&self.gas_limit)
723 .encode_field(&self.to)
724 .encode_field(&self.value)
725 .encode_field(&self.data)
726 .encode_field(&self.access_list)
727 .encode_field(&self.fee_token)
728 .encode_field(&self.signature_y_parity)
729 .encode_field(&self.signature_r)
730 .encode_field(&self.signature_s)
731 .finish()
732 }
733}
734
735impl PayloadRLPEncode for Transaction {
736 fn encode_payload(&self, buf: &mut dyn bytes::BufMut) {
737 match self {
738 Transaction::LegacyTransaction(tx) => tx.encode_payload(buf),
739 Transaction::EIP1559Transaction(tx) => tx.encode_payload(buf),
740 Transaction::EIP2930Transaction(tx) => tx.encode_payload(buf),
741 Transaction::EIP4844Transaction(tx) => tx.encode_payload(buf),
742 Transaction::EIP7702Transaction(tx) => tx.encode_payload(buf),
743 Transaction::PrivilegedL2Transaction(tx) => tx.encode_payload(buf),
744 Transaction::FeeTokenTransaction(tx) => tx.encode_payload(buf),
745 Transaction::FrameTransaction(tx) => tx.encode_payload(buf),
746 }
747 }
748}
749
750impl PayloadRLPEncode for LegacyTransaction {
751 fn encode_payload(&self, buf: &mut dyn bytes::BufMut) {
752 Encoder::new(buf)
753 .encode_field(&self.nonce)
754 .encode_field(&self.gas_price)
755 .encode_field(&self.gas)
756 .encode_field(&self.to)
757 .encode_field(&self.value)
758 .encode_field(&self.data)
759 .finish();
760 }
761}
762
763impl PayloadRLPEncode for EIP1559Transaction {
764 fn encode_payload(&self, buf: &mut dyn bytes::BufMut) {
765 Encoder::new(buf)
766 .encode_field(&self.chain_id)
767 .encode_field(&self.nonce)
768 .encode_field(&self.max_priority_fee_per_gas)
769 .encode_field(&self.max_fee_per_gas)
770 .encode_field(&self.gas_limit)
771 .encode_field(&self.to)
772 .encode_field(&self.value)
773 .encode_field(&self.data)
774 .encode_field(&self.access_list)
775 .finish();
776 }
777}
778
779impl PayloadRLPEncode for EIP2930Transaction {
780 fn encode_payload(&self, buf: &mut dyn bytes::BufMut) {
781 Encoder::new(buf)
782 .encode_field(&self.chain_id)
783 .encode_field(&self.nonce)
784 .encode_field(&self.gas_price)
785 .encode_field(&self.gas_limit)
786 .encode_field(&self.to)
787 .encode_field(&self.value)
788 .encode_field(&self.data)
789 .encode_field(&self.access_list)
790 .finish();
791 }
792}
793
794impl PayloadRLPEncode for EIP4844Transaction {
795 fn encode_payload(&self, buf: &mut dyn bytes::BufMut) {
796 Encoder::new(buf)
797 .encode_field(&self.chain_id)
798 .encode_field(&self.nonce)
799 .encode_field(&self.max_priority_fee_per_gas)
800 .encode_field(&self.max_fee_per_gas)
801 .encode_field(&self.gas)
802 .encode_field(&self.to)
803 .encode_field(&self.value)
804 .encode_field(&self.data)
805 .encode_field(&self.access_list)
806 .encode_field(&self.max_fee_per_blob_gas)
807 .encode_field(&self.blob_versioned_hashes)
808 .finish();
809 }
810}
811
812impl PayloadRLPEncode for EIP7702Transaction {
813 fn encode_payload(&self, buf: &mut dyn bytes::BufMut) {
814 Encoder::new(buf)
815 .encode_field(&self.chain_id)
816 .encode_field(&self.nonce)
817 .encode_field(&self.max_priority_fee_per_gas)
818 .encode_field(&self.max_fee_per_gas)
819 .encode_field(&self.gas_limit)
820 .encode_field(&self.to)
821 .encode_field(&self.value)
822 .encode_field(&self.data)
823 .encode_field(&self.access_list)
824 .encode_field(&self.authorization_list)
825 .finish();
826 }
827}
828
829impl PayloadRLPEncode for PrivilegedL2Transaction {
830 fn encode_payload(&self, buf: &mut dyn bytes::BufMut) {
831 Encoder::new(buf)
832 .encode_field(&self.chain_id)
833 .encode_field(&self.nonce)
834 .encode_field(&self.max_priority_fee_per_gas)
835 .encode_field(&self.max_fee_per_gas)
836 .encode_field(&self.gas_limit)
837 .encode_field(&self.to)
838 .encode_field(&self.value)
839 .encode_field(&self.data)
840 .encode_field(&self.access_list)
841 .encode_field(&self.from)
842 .finish();
843 }
844}
845
846impl PayloadRLPEncode for FeeTokenTransaction {
847 fn encode_payload(&self, buf: &mut dyn bytes::BufMut) {
848 Encoder::new(buf)
849 .encode_field(&self.chain_id)
850 .encode_field(&self.nonce)
851 .encode_field(&self.max_priority_fee_per_gas)
852 .encode_field(&self.max_fee_per_gas)
853 .encode_field(&self.gas_limit)
854 .encode_field(&self.to)
855 .encode_field(&self.value)
856 .encode_field(&self.data)
857 .encode_field(&self.access_list)
858 .encode_field(&self.fee_token)
859 .finish();
860 }
861}
862
863fn decode_nonce_field(decoder: Decoder) -> Result<(u64, Decoder), RLPDecodeError> {
873 let wrap =
877 |err| RLPDecodeError::Custom(format!("Error decoding field 'nonce' of type u64: {err}"));
878 let (item, rest) = decoder.get_encoded_item_ref().map_err(wrap)?;
879 if let Ok((content, _)) = decode_bytes(item)
891 && content.len() > 8
892 && content.first() != Some(&0)
893 {
894 return Err(RLPDecodeError::Custom("Nonce is max".into()));
895 }
896 let (nonce, _) = u64::decode_unfinished(item).map_err(wrap)?;
897 Ok((nonce, rest))
898}
899
900impl RLPDecode for LegacyTransaction {
901 fn decode_unfinished(rlp: &[u8]) -> Result<(LegacyTransaction, &[u8]), RLPDecodeError> {
902 let decoder = Decoder::new(rlp)?;
903 let (nonce, decoder) = decode_nonce_field(decoder)?;
904 let (gas_price, decoder) = decoder.decode_field("gas_price")?;
905 let (gas, decoder) = decoder.decode_field("gas")?;
906 let (to, decoder) = decoder.decode_field("to")?;
907 let (value, decoder) = decoder.decode_field("value")?;
908 let (data, decoder) = decoder.decode_field("data")?;
909 let (v, decoder) = decoder.decode_field("v")?;
910 let (r, decoder) = decoder.decode_field("r")?;
911 let (s, decoder) = decoder.decode_field("s")?;
912 let inner_hash = OnceCell::new();
913 let sender_cache = OnceCell::new();
914
915 let tx = LegacyTransaction {
916 nonce,
917 gas_price,
918 gas,
919 to,
920 value,
921 data,
922 v,
923 r,
924 s,
925 inner_hash,
926 sender_cache,
927 };
928 Ok((tx, decoder.finish()?))
929 }
930}
931
932impl RLPDecode for EIP2930Transaction {
933 fn decode_unfinished(rlp: &[u8]) -> Result<(EIP2930Transaction, &[u8]), RLPDecodeError> {
934 let decoder = Decoder::new(rlp)?;
935 let (chain_id, decoder) = decoder.decode_field("chain_id")?;
936 let (nonce, decoder) = decode_nonce_field(decoder)?;
937 let (gas_price, decoder) = decoder.decode_field("gas_price")?;
938 let (gas_limit, decoder) = decoder.decode_field("gas_limit")?;
939 let (to, decoder) = decoder.decode_field("to")?;
940 let (value, decoder) = decoder.decode_field("value")?;
941 let (data, decoder) = decoder.decode_field("data")?;
942 let (access_list, decoder) = decoder.decode_field("access_list")?;
943 let (signature_y_parity, decoder) = decoder.decode_field("signature_y_parity")?;
944 let (signature_r, decoder) = decoder.decode_field("signature_r")?;
945 let (signature_s, decoder) = decoder.decode_field("signature_s")?;
946 let inner_hash = OnceCell::new();
947 let sender_cache = OnceCell::new();
948 let cached_canonical = OnceCell::new();
949
950 let tx = EIP2930Transaction {
951 chain_id,
952 nonce,
953 gas_price,
954 gas_limit,
955 to,
956 value,
957 data,
958 access_list,
959 signature_y_parity,
960 signature_r,
961 signature_s,
962 inner_hash,
963 sender_cache,
964 cached_canonical,
965 };
966 Ok((tx, decoder.finish()?))
967 }
968}
969
970impl RLPDecode for EIP1559Transaction {
971 fn decode_unfinished(rlp: &[u8]) -> Result<(EIP1559Transaction, &[u8]), RLPDecodeError> {
972 let decoder = Decoder::new(rlp)?;
973 let (chain_id, decoder) = decoder.decode_field("chain_id")?;
974 let (nonce, decoder) = decode_nonce_field(decoder)?;
975 let (max_priority_fee_per_gas, decoder) =
976 decoder.decode_field("max_priority_fee_per_gas")?;
977 let (max_fee_per_gas, decoder) = decoder.decode_field("max_fee_per_gas")?;
978 let (gas_limit, decoder) = decoder.decode_field("gas_limit")?;
979 let (to, decoder) = decoder.decode_field("to")?;
980 let (value, decoder) = decoder.decode_field("value")?;
981 let (data, decoder) = decoder.decode_field("data")?;
982 let (access_list, decoder) = decoder.decode_field("access_list")?;
983 let (signature_y_parity, decoder) = decoder.decode_field("signature_y_parity")?;
984 let (signature_r, decoder) = decoder.decode_field("signature_r")?;
985 let (signature_s, decoder) = decoder.decode_field("signature_s")?;
986 let inner_hash = OnceCell::new();
987 let sender_cache = OnceCell::new();
988 let cached_canonical = OnceCell::new();
989
990 let tx = EIP1559Transaction {
991 chain_id,
992 nonce,
993 max_priority_fee_per_gas,
994 max_fee_per_gas,
995 gas_limit,
996 to,
997 value,
998 data,
999 access_list,
1000 signature_y_parity,
1001 signature_r,
1002 signature_s,
1003 inner_hash,
1004 sender_cache,
1005 cached_canonical,
1006 };
1007 Ok((tx, decoder.finish()?))
1008 }
1009}
1010
1011impl RLPDecode for EIP4844Transaction {
1012 fn decode_unfinished(rlp: &[u8]) -> Result<(EIP4844Transaction, &[u8]), RLPDecodeError> {
1013 let decoder = Decoder::new(rlp)?;
1014 let (chain_id, decoder) = decoder.decode_field("chain_id")?;
1015 let (nonce, decoder) = decode_nonce_field(decoder)?;
1016 let (max_priority_fee_per_gas, decoder) =
1017 decoder.decode_field("max_priority_fee_per_gas")?;
1018 let (max_fee_per_gas, decoder) = decoder.decode_field("max_fee_per_gas")?;
1019 let (gas, decoder) = decoder.decode_field("gas")?;
1020 let (to, decoder) = decoder.decode_field("to")?;
1021 let (value, decoder) = decoder.decode_field("value")?;
1022 let (data, decoder) = decoder.decode_field("data")?;
1023 let (access_list, decoder) = decoder.decode_field("access_list")?;
1024 let (max_fee_per_blob_gas, decoder) = decoder.decode_field("max_fee_per_blob_gas")?;
1025 let (blob_versioned_hashes, decoder) = decoder.decode_field("blob_versioned_hashes")?;
1026 let (signature_y_parity, decoder) = decoder.decode_field("signature_y_parity")?;
1027 let (signature_r, decoder) = decoder.decode_field("signature_r")?;
1028 let (signature_s, decoder) = decoder.decode_field("signature_s")?;
1029 let inner_hash = OnceCell::new();
1030 let sender_cache = OnceCell::new();
1031 let cached_canonical = OnceCell::new();
1032
1033 let tx = EIP4844Transaction {
1034 chain_id,
1035 nonce,
1036 max_priority_fee_per_gas,
1037 max_fee_per_gas,
1038 gas,
1039 to,
1040 value,
1041 data,
1042 access_list,
1043 max_fee_per_blob_gas,
1044 blob_versioned_hashes,
1045 signature_y_parity,
1046 signature_r,
1047 signature_s,
1048 inner_hash,
1049 sender_cache,
1050 cached_canonical,
1051 };
1052 Ok((tx, decoder.finish()?))
1053 }
1054}
1055
1056impl RLPDecode for EIP7702Transaction {
1057 fn decode_unfinished(rlp: &[u8]) -> Result<(EIP7702Transaction, &[u8]), RLPDecodeError> {
1058 let decoder = Decoder::new(rlp)?;
1059 let (chain_id, decoder) = decoder.decode_field("chain_id")?;
1060 let (nonce, decoder) = decode_nonce_field(decoder)?;
1061 let (max_priority_fee_per_gas, decoder) =
1062 decoder.decode_field("max_priority_fee_per_gas")?;
1063 let (max_fee_per_gas, decoder) = decoder.decode_field("max_fee_per_gas")?;
1064 let (gas_limit, decoder) = decoder.decode_field("gas_limit")?;
1065 let (to, decoder) = decoder.decode_field("to")?;
1066 let (value, decoder) = decoder.decode_field("value")?;
1067 let (data, decoder) = decoder.decode_field("data")?;
1068 let (access_list, decoder) = decoder.decode_field("access_list")?;
1069 let (authorization_list, decoder) = decoder.decode_field("authorization_list")?;
1070 let (signature_y_parity, decoder) = decoder.decode_field("signature_y_parity")?;
1071 let (signature_r, decoder) = decoder.decode_field("signature_r")?;
1072 let (signature_s, decoder) = decoder.decode_field("signature_s")?;
1073 let inner_hash = OnceCell::new();
1074 let sender_cache = OnceCell::new();
1075 let cached_canonical = OnceCell::new();
1076
1077 let tx = EIP7702Transaction {
1078 chain_id,
1079 nonce,
1080 max_priority_fee_per_gas,
1081 max_fee_per_gas,
1082 gas_limit,
1083 to,
1084 value,
1085 data,
1086 access_list,
1087 authorization_list,
1088 signature_y_parity,
1089 signature_r,
1090 signature_s,
1091 inner_hash,
1092 sender_cache,
1093 cached_canonical,
1094 };
1095 Ok((tx, decoder.finish()?))
1096 }
1097}
1098
1099impl RLPDecode for PrivilegedL2Transaction {
1100 fn decode_unfinished(rlp: &[u8]) -> Result<(PrivilegedL2Transaction, &[u8]), RLPDecodeError> {
1101 let decoder = Decoder::new(rlp)?;
1102 let (chain_id, decoder) = decoder.decode_field("chain_id")?;
1103 let (nonce, decoder) = decode_nonce_field(decoder)?;
1104 let (max_priority_fee_per_gas, decoder) =
1105 decoder.decode_field("max_priority_fee_per_gas")?;
1106 let (max_fee_per_gas, decoder) = decoder.decode_field("max_fee_per_gas")?;
1107 let (gas_limit, decoder) = decoder.decode_field::<u64>("gas_limit")?;
1108 let (to, decoder) = decoder.decode_field("to")?;
1109 let (value, decoder) = decoder.decode_field("value")?;
1110 let (data, decoder) = decoder.decode_field("data")?;
1111 let (access_list, decoder) = decoder.decode_field("access_list")?;
1112 let (from, decoder) = decoder.decode_field("from")?;
1113 let inner_hash = OnceCell::new();
1114 let sender_cache = OnceCell::new();
1115 let cached_canonical = OnceCell::new();
1116
1117 let tx = PrivilegedL2Transaction {
1118 chain_id,
1119 nonce,
1120 max_priority_fee_per_gas,
1121 max_fee_per_gas,
1122 gas_limit,
1123 to,
1124 value,
1125 data,
1126 access_list,
1127 from,
1128 inner_hash,
1129 sender_cache,
1130 cached_canonical,
1131 };
1132 Ok((tx, decoder.finish()?))
1133 }
1134}
1135
1136impl RLPDecode for FeeTokenTransaction {
1137 fn decode_unfinished(rlp: &[u8]) -> Result<(FeeTokenTransaction, &[u8]), RLPDecodeError> {
1138 let decoder = Decoder::new(rlp)?;
1139 let (chain_id, decoder) = decoder.decode_field("chain_id")?;
1140 let (nonce, decoder) = decode_nonce_field(decoder)?;
1141 let (max_priority_fee_per_gas, decoder) =
1142 decoder.decode_field("max_priority_fee_per_gas")?;
1143 let (max_fee_per_gas, decoder) = decoder.decode_field("max_fee_per_gas")?;
1144 let (gas_limit, decoder) = decoder.decode_field("gas_limit")?;
1145 let (to, decoder) = decoder.decode_field("to")?;
1146 let (value, decoder) = decoder.decode_field("value")?;
1147 let (data, decoder) = decoder.decode_field("data")?;
1148 let (access_list, decoder) = decoder.decode_field("access_list")?;
1149 let (fee_token, decoder) = decoder.decode_field("fee_token")?;
1150 let (signature_y_parity, decoder) = decoder.decode_field("signature_y_parity")?;
1151 let (signature_r, decoder) = decoder.decode_field("signature_r")?;
1152 let (signature_s, decoder) = decoder.decode_field("signature_s")?;
1153 let inner_hash = OnceCell::new();
1154 let sender_cache = OnceCell::new();
1155 let cached_canonical = OnceCell::new();
1156
1157 let tx = FeeTokenTransaction {
1158 chain_id,
1159 nonce,
1160 max_priority_fee_per_gas,
1161 max_fee_per_gas,
1162 gas_limit,
1163 to,
1164 value,
1165 data,
1166 access_list,
1167 fee_token,
1168 signature_y_parity,
1169 signature_r,
1170 signature_s,
1171 inner_hash,
1172 sender_cache,
1173 cached_canonical,
1174 };
1175 Ok((tx, decoder.finish()?))
1176 }
1177}
1178
1179impl Transaction {
1180 pub fn sender(&self, crypto: &dyn Crypto) -> Result<Address, CryptoError> {
1181 if let Transaction::FrameTransaction(tx) = self {
1183 return Ok(tx.sender);
1184 }
1185 let sender_cache = match self {
1186 Transaction::LegacyTransaction(tx) => &tx.sender_cache,
1187 Transaction::EIP2930Transaction(tx) => &tx.sender_cache,
1188 Transaction::EIP1559Transaction(tx) => &tx.sender_cache,
1189 Transaction::EIP4844Transaction(tx) => &tx.sender_cache,
1190 Transaction::EIP7702Transaction(tx) => &tx.sender_cache,
1191 Transaction::PrivilegedL2Transaction(tx) => &tx.sender_cache,
1192 Transaction::FeeTokenTransaction(tx) => &tx.sender_cache,
1193 Transaction::FrameTransaction(_) => unreachable!(),
1194 };
1195 sender_cache
1196 .get_or_try_init(|| {
1197 let tx_hash = self.hash(crypto);
1198 let mut cache = GLOBAL_SIGNER_CACHE
1200 .lock()
1201 .unwrap_or_else(|e| e.into_inner());
1202 if let Some(&addr) = cache.get(&tx_hash) {
1203 return Ok(addr);
1204 }
1205 drop(cache);
1206 let sender = self.compute_sender(crypto)?;
1208 GLOBAL_SIGNER_CACHE
1210 .lock()
1211 .unwrap_or_else(|e| e.into_inner())
1212 .put(tx_hash, sender);
1213 Ok(sender)
1214 })
1215 .copied()
1216 }
1217
1218 fn compute_sender(&self, crypto: &dyn Crypto) -> Result<Address, CryptoError> {
1219 let (buf, sig) = match self {
1220 Transaction::LegacyTransaction(tx) => {
1221 let v = u64::try_from(tx.v).map_err(|_| CryptoError::InvalidSignature)?;
1222 let signature_y_parity = match self.chain_id() {
1227 Some(chain_id) => match v.checked_sub(35 + chain_id * 2) {
1228 Some(0) => false,
1229 Some(1) => true,
1230 _ => return Err(CryptoError::InvalidSignature),
1231 },
1232 None => match v {
1233 27 => false,
1234 28 => true,
1235 _ => return Err(CryptoError::InvalidSignature),
1236 },
1237 };
1238 let mut buf = vec![];
1239 match self.chain_id() {
1240 None => Encoder::new(&mut buf)
1241 .encode_field(&tx.nonce)
1242 .encode_field(&tx.gas_price)
1243 .encode_field(&tx.gas)
1244 .encode_field(&tx.to)
1245 .encode_field(&tx.value)
1246 .encode_field(&tx.data)
1247 .finish(),
1248 Some(chain_id) => Encoder::new(&mut buf)
1249 .encode_field(&tx.nonce)
1250 .encode_field(&tx.gas_price)
1251 .encode_field(&tx.gas)
1252 .encode_field(&tx.to)
1253 .encode_field(&tx.value)
1254 .encode_field(&tx.data)
1255 .encode_field(&chain_id)
1256 .encode_field(&0u8)
1257 .encode_field(&0u8)
1258 .finish(),
1259 }
1260 let mut sig = [0u8; 65];
1261 sig[..32].copy_from_slice(&tx.r.to_big_endian());
1262 sig[32..64].copy_from_slice(&tx.s.to_big_endian());
1263 sig[64] = signature_y_parity as u8;
1264 (buf, sig)
1265 }
1266 Transaction::EIP2930Transaction(tx) => {
1267 let mut buf = vec![self.tx_type() as u8];
1268 Encoder::new(&mut buf)
1269 .encode_field(&tx.chain_id)
1270 .encode_field(&tx.nonce)
1271 .encode_field(&tx.gas_price)
1272 .encode_field(&tx.gas_limit)
1273 .encode_field(&tx.to)
1274 .encode_field(&tx.value)
1275 .encode_field(&tx.data)
1276 .encode_field(&tx.access_list)
1277 .finish();
1278 let mut sig = [0u8; 65];
1279 sig[..32].copy_from_slice(&tx.signature_r.to_big_endian());
1280 sig[32..64].copy_from_slice(&tx.signature_s.to_big_endian());
1281 sig[64] = tx.signature_y_parity as u8;
1282 (buf, sig)
1283 }
1284 Transaction::EIP1559Transaction(tx) => {
1285 let mut buf = vec![self.tx_type() as u8];
1286 Encoder::new(&mut buf)
1287 .encode_field(&tx.chain_id)
1288 .encode_field(&tx.nonce)
1289 .encode_field(&tx.max_priority_fee_per_gas)
1290 .encode_field(&tx.max_fee_per_gas)
1291 .encode_field(&tx.gas_limit)
1292 .encode_field(&tx.to)
1293 .encode_field(&tx.value)
1294 .encode_field(&tx.data)
1295 .encode_field(&tx.access_list)
1296 .finish();
1297 let mut sig = [0u8; 65];
1298 sig[..32].copy_from_slice(&tx.signature_r.to_big_endian());
1299 sig[32..64].copy_from_slice(&tx.signature_s.to_big_endian());
1300 sig[64] = tx.signature_y_parity as u8;
1301 (buf, sig)
1302 }
1303 Transaction::EIP4844Transaction(tx) => {
1304 let mut buf = vec![self.tx_type() as u8];
1305 Encoder::new(&mut buf)
1306 .encode_field(&tx.chain_id)
1307 .encode_field(&tx.nonce)
1308 .encode_field(&tx.max_priority_fee_per_gas)
1309 .encode_field(&tx.max_fee_per_gas)
1310 .encode_field(&tx.gas)
1311 .encode_field(&tx.to)
1312 .encode_field(&tx.value)
1313 .encode_field(&tx.data)
1314 .encode_field(&tx.access_list)
1315 .encode_field(&tx.max_fee_per_blob_gas)
1316 .encode_field(&tx.blob_versioned_hashes)
1317 .finish();
1318 let mut sig = [0u8; 65];
1319 sig[..32].copy_from_slice(&tx.signature_r.to_big_endian());
1320 sig[32..64].copy_from_slice(&tx.signature_s.to_big_endian());
1321 sig[64] = tx.signature_y_parity as u8;
1322 (buf, sig)
1323 }
1324 Transaction::EIP7702Transaction(tx) => {
1325 let mut buf = vec![self.tx_type() as u8];
1326 Encoder::new(&mut buf)
1327 .encode_field(&tx.chain_id)
1328 .encode_field(&tx.nonce)
1329 .encode_field(&tx.max_priority_fee_per_gas)
1330 .encode_field(&tx.max_fee_per_gas)
1331 .encode_field(&tx.gas_limit)
1332 .encode_field(&tx.to)
1333 .encode_field(&tx.value)
1334 .encode_field(&tx.data)
1335 .encode_field(&tx.access_list)
1336 .encode_field(&tx.authorization_list)
1337 .finish();
1338 let mut sig = [0u8; 65];
1339 sig[..32].copy_from_slice(&tx.signature_r.to_big_endian());
1340 sig[32..64].copy_from_slice(&tx.signature_s.to_big_endian());
1341 sig[64] = tx.signature_y_parity as u8;
1342 (buf, sig)
1343 }
1344 Transaction::PrivilegedL2Transaction(tx) => return Ok(tx.from),
1345 Transaction::FrameTransaction(tx) => return Ok(tx.sender),
1346 Transaction::FeeTokenTransaction(tx) => {
1347 let mut buf = vec![self.tx_type() as u8];
1348 Encoder::new(&mut buf)
1349 .encode_field(&tx.chain_id)
1350 .encode_field(&tx.nonce)
1351 .encode_field(&tx.max_priority_fee_per_gas)
1352 .encode_field(&tx.max_fee_per_gas)
1353 .encode_field(&tx.gas_limit)
1354 .encode_field(&tx.to)
1355 .encode_field(&tx.value)
1356 .encode_field(&tx.data)
1357 .encode_field(&tx.access_list)
1358 .encode_field(&tx.fee_token)
1359 .finish();
1360 let mut sig = [0u8; 65];
1361 sig[..32].copy_from_slice(&tx.signature_r.to_big_endian());
1362 sig[32..64].copy_from_slice(&tx.signature_s.to_big_endian());
1363 sig[64] = tx.signature_y_parity as u8;
1364 (buf, sig)
1365 }
1366 };
1367 let msg = crypto.keccak256(&buf);
1368 crypto.recover_signer(&sig, &msg)
1369 }
1370
1371 pub fn gas_limit(&self) -> u64 {
1372 match self {
1373 Transaction::LegacyTransaction(tx) => tx.gas,
1374 Transaction::EIP2930Transaction(tx) => tx.gas_limit,
1375 Transaction::EIP1559Transaction(tx) => tx.gas_limit,
1376 Transaction::EIP7702Transaction(tx) => tx.gas_limit,
1377 Transaction::EIP4844Transaction(tx) => tx.gas,
1378 Transaction::PrivilegedL2Transaction(tx) => tx.gas_limit,
1379 Transaction::FeeTokenTransaction(tx) => tx.gas_limit,
1380 Transaction::FrameTransaction(tx) => tx.total_gas_limit(),
1381 }
1382 }
1383
1384 pub fn gas_price(&self) -> U256 {
1386 match self {
1387 Transaction::LegacyTransaction(tx) => tx.gas_price,
1388 Transaction::EIP2930Transaction(tx) => tx.gas_price,
1389 Transaction::EIP1559Transaction(tx) => U256::from(tx.max_fee_per_gas),
1390 Transaction::EIP7702Transaction(tx) => U256::from(tx.max_fee_per_gas),
1391 Transaction::EIP4844Transaction(tx) => U256::from(tx.max_fee_per_gas),
1392 Transaction::PrivilegedL2Transaction(tx) => U256::from(tx.max_fee_per_gas),
1393 Transaction::FeeTokenTransaction(tx) => U256::from(tx.max_fee_per_gas),
1394 Transaction::FrameTransaction(tx) => U256::from(tx.max_fee_per_gas),
1395 }
1396 }
1397
1398 pub fn to(&self) -> TxKind {
1399 match self {
1400 Transaction::LegacyTransaction(tx) => tx.to.clone(),
1401 Transaction::EIP2930Transaction(tx) => tx.to.clone(),
1402 Transaction::EIP1559Transaction(tx) => tx.to.clone(),
1403 Transaction::EIP4844Transaction(tx) => TxKind::Call(tx.to),
1404 Transaction::EIP7702Transaction(tx) => TxKind::Call(tx.to),
1405 Transaction::PrivilegedL2Transaction(tx) => tx.to.clone(),
1406 Transaction::FeeTokenTransaction(tx) => tx.to.clone(),
1407 Transaction::FrameTransaction(tx) => TxKind::Call(tx.sender),
1408 }
1409 }
1410
1411 pub fn value(&self) -> U256 {
1412 match self {
1413 Transaction::LegacyTransaction(tx) => tx.value,
1414 Transaction::EIP2930Transaction(tx) => tx.value,
1415 Transaction::EIP1559Transaction(tx) => tx.value,
1416 Transaction::EIP4844Transaction(tx) => tx.value,
1417 Transaction::EIP7702Transaction(tx) => tx.value,
1418 Transaction::PrivilegedL2Transaction(tx) => tx.value,
1419 Transaction::FeeTokenTransaction(tx) => tx.value,
1420 Transaction::FrameTransaction(_) => U256::zero(),
1421 }
1422 }
1423
1424 pub fn max_priority_fee(&self) -> Option<u64> {
1425 match self {
1426 Transaction::LegacyTransaction(_tx) => None,
1427 Transaction::EIP2930Transaction(_tx) => None,
1428 Transaction::EIP1559Transaction(tx) => Some(tx.max_priority_fee_per_gas),
1429 Transaction::EIP4844Transaction(tx) => Some(tx.max_priority_fee_per_gas),
1430 Transaction::EIP7702Transaction(tx) => Some(tx.max_priority_fee_per_gas),
1431 Transaction::PrivilegedL2Transaction(tx) => Some(tx.max_priority_fee_per_gas),
1432 Transaction::FeeTokenTransaction(tx) => Some(tx.max_priority_fee_per_gas),
1433 Transaction::FrameTransaction(tx) => Some(tx.max_priority_fee_per_gas),
1434 }
1435 }
1436
1437 pub fn chain_id(&self) -> Option<u64> {
1438 match self {
1439 Transaction::LegacyTransaction(tx) => derive_legacy_chain_id(tx.v),
1440 Transaction::EIP2930Transaction(tx) => Some(tx.chain_id),
1441 Transaction::EIP1559Transaction(tx) => Some(tx.chain_id),
1442 Transaction::EIP4844Transaction(tx) => Some(tx.chain_id),
1443 Transaction::EIP7702Transaction(tx) => Some(tx.chain_id),
1444 Transaction::PrivilegedL2Transaction(tx) => Some(tx.chain_id),
1445 Transaction::FeeTokenTransaction(tx) => Some(tx.chain_id),
1446 Transaction::FrameTransaction(tx) => Some(tx.chain_id),
1447 }
1448 }
1449
1450 pub fn access_list(&self) -> &AccessList {
1451 static EMPTY_ACCESS_LIST: AccessList = Vec::new();
1452 match self {
1453 Transaction::LegacyTransaction(_tx) => &EMPTY_ACCESS_LIST,
1454 Transaction::EIP2930Transaction(tx) => &tx.access_list,
1455 Transaction::EIP1559Transaction(tx) => &tx.access_list,
1456 Transaction::EIP4844Transaction(tx) => &tx.access_list,
1457 Transaction::EIP7702Transaction(tx) => &tx.access_list,
1458 Transaction::PrivilegedL2Transaction(tx) => &tx.access_list,
1459 Transaction::FeeTokenTransaction(tx) => &tx.access_list,
1460 Transaction::FrameTransaction(_) => &EMPTY_ACCESS_LIST,
1461 }
1462 }
1463 pub fn authorization_list(&self) -> Option<&AuthorizationList> {
1464 match self {
1465 Transaction::LegacyTransaction(_) => None,
1466 Transaction::EIP2930Transaction(_) => None,
1467 Transaction::EIP1559Transaction(_) => None,
1468 Transaction::EIP4844Transaction(_) => None,
1469 Transaction::EIP7702Transaction(tx) => Some(&tx.authorization_list),
1470 Transaction::PrivilegedL2Transaction(_) => None,
1471 Transaction::FeeTokenTransaction(_) => None,
1472 Transaction::FrameTransaction(_) => None,
1473 }
1474 }
1475
1476 pub fn nonce(&self) -> u64 {
1477 match self {
1478 Transaction::LegacyTransaction(tx) => tx.nonce,
1479 Transaction::EIP2930Transaction(tx) => tx.nonce,
1480 Transaction::EIP1559Transaction(tx) => tx.nonce,
1481 Transaction::EIP4844Transaction(tx) => tx.nonce,
1482 Transaction::EIP7702Transaction(tx) => tx.nonce,
1483 Transaction::PrivilegedL2Transaction(tx) => tx.nonce,
1484 Transaction::FeeTokenTransaction(tx) => tx.nonce,
1485 Transaction::FrameTransaction(tx) => tx.nonce,
1486 }
1487 }
1488
1489 pub fn data(&self) -> &Bytes {
1490 match self {
1491 Transaction::LegacyTransaction(tx) => &tx.data,
1492 Transaction::EIP2930Transaction(tx) => &tx.data,
1493 Transaction::EIP1559Transaction(tx) => &tx.data,
1494 Transaction::EIP4844Transaction(tx) => &tx.data,
1495 Transaction::EIP7702Transaction(tx) => &tx.data,
1496 Transaction::PrivilegedL2Transaction(tx) => &tx.data,
1497 Transaction::FeeTokenTransaction(tx) => &tx.data,
1498 Transaction::FrameTransaction(_) => {
1499 static EMPTY_DATA: Bytes = Bytes::new();
1500 &EMPTY_DATA
1501 }
1502 }
1503 }
1504
1505 pub fn blob_versioned_hashes(&self) -> Vec<H256> {
1506 match self {
1507 Transaction::LegacyTransaction(_) => Vec::new(),
1508 Transaction::EIP2930Transaction(_) => Vec::new(),
1509 Transaction::EIP1559Transaction(_) => Vec::new(),
1510 Transaction::EIP4844Transaction(tx) => tx.blob_versioned_hashes.clone(),
1511 Transaction::EIP7702Transaction(_) => Vec::new(),
1512 Transaction::PrivilegedL2Transaction(_) => Vec::new(),
1513 Transaction::FeeTokenTransaction(_) => Vec::new(),
1514 Transaction::FrameTransaction(tx) => tx.blob_versioned_hashes.clone(),
1515 }
1516 }
1517
1518 pub fn max_fee_per_blob_gas(&self) -> Option<U256> {
1519 match self {
1520 Transaction::LegacyTransaction(_) => None,
1521 Transaction::EIP2930Transaction(_) => None,
1522 Transaction::EIP1559Transaction(_) => None,
1523 Transaction::EIP4844Transaction(tx) => Some(tx.max_fee_per_blob_gas),
1524 Transaction::EIP7702Transaction(_) => None,
1525 Transaction::PrivilegedL2Transaction(_) => None,
1526 Transaction::FeeTokenTransaction(_) => None,
1527 Transaction::FrameTransaction(tx) => {
1528 if tx.blob_versioned_hashes.is_empty() {
1529 None
1530 } else {
1531 Some(tx.max_fee_per_blob_gas)
1532 }
1533 }
1534 }
1535 }
1536
1537 pub fn is_contract_creation(&self) -> bool {
1538 match &self {
1539 Transaction::LegacyTransaction(t) => matches!(t.to, TxKind::Create),
1540 Transaction::EIP2930Transaction(t) => matches!(t.to, TxKind::Create),
1541 Transaction::EIP1559Transaction(t) => matches!(t.to, TxKind::Create),
1542 Transaction::EIP4844Transaction(_) => false,
1543 Transaction::EIP7702Transaction(_) => false,
1544 Transaction::PrivilegedL2Transaction(t) => matches!(t.to, TxKind::Create),
1545 Transaction::FeeTokenTransaction(t) => matches!(t.to, TxKind::Create),
1546 Transaction::FrameTransaction(_) => false,
1547 }
1548 }
1549
1550 pub fn is_privileged(&self) -> bool {
1551 matches!(self, Transaction::PrivilegedL2Transaction(_))
1552 }
1553
1554 pub fn max_fee_per_gas(&self) -> Option<u64> {
1555 match self {
1556 Transaction::LegacyTransaction(_tx) => None,
1557 Transaction::EIP2930Transaction(_tx) => None,
1558 Transaction::EIP1559Transaction(tx) => Some(tx.max_fee_per_gas),
1559 Transaction::EIP4844Transaction(tx) => Some(tx.max_fee_per_gas),
1560 Transaction::EIP7702Transaction(tx) => Some(tx.max_fee_per_gas),
1561 Transaction::PrivilegedL2Transaction(tx) => Some(tx.max_fee_per_gas),
1562 Transaction::FeeTokenTransaction(tx) => Some(tx.max_fee_per_gas),
1563 Transaction::FrameTransaction(tx) => Some(tx.max_fee_per_gas),
1564 }
1565 }
1566
1567 fn compute_hash(&self, crypto: &dyn Crypto) -> H256 {
1568 if let Transaction::PrivilegedL2Transaction(tx) = self {
1569 return tx.get_privileged_hash().unwrap_or_default();
1570 }
1571 H256(crypto.keccak256(&self.encode_canonical_to_vec()))
1572 }
1573
1574 pub fn hash(&self, crypto: &dyn Crypto) -> H256 {
1575 let inner_hash = match self {
1576 Transaction::LegacyTransaction(tx) => &tx.inner_hash,
1577 Transaction::EIP2930Transaction(tx) => &tx.inner_hash,
1578 Transaction::EIP1559Transaction(tx) => &tx.inner_hash,
1579 Transaction::EIP4844Transaction(tx) => &tx.inner_hash,
1580 Transaction::EIP7702Transaction(tx) => &tx.inner_hash,
1581 Transaction::PrivilegedL2Transaction(tx) => &tx.inner_hash,
1582 Transaction::FeeTokenTransaction(tx) => &tx.inner_hash,
1583 Transaction::FrameTransaction(tx) => &tx.inner_hash,
1584 };
1585
1586 *inner_hash.get_or_init(|| self.compute_hash(crypto))
1587 }
1588
1589 pub fn gas_tip_cap(&self) -> U256 {
1590 self.max_priority_fee()
1591 .map(U256::from)
1592 .unwrap_or_else(|| self.gas_price())
1593 }
1594
1595 pub fn gas_fee_cap(&self) -> U256 {
1596 self.max_fee_per_gas()
1597 .map(U256::from)
1598 .unwrap_or_else(|| self.gas_price())
1599 }
1600
1601 pub fn effective_gas_tip(&self, base_fee: Option<u64>) -> Option<U256> {
1605 let tip_cap = self.gas_tip_cap();
1606 let Some(base_fee) = base_fee else {
1607 return Some(tip_cap);
1608 };
1609 let base_fee = U256::from(base_fee);
1610 let fee_cap = self.gas_fee_cap();
1611 let tip = fee_cap.checked_sub(base_fee)?;
1612 Some(min(tip, tip_cap))
1613 }
1614
1615 pub fn protected(&self) -> bool {
1618 match self {
1619 Transaction::LegacyTransaction(tx) if tx.v.bits() <= 8 => {
1620 let v = tx.v.low_u64();
1621 v != 27 && v != 28 && v != 1 && v != 0
1622 }
1623 _ => true,
1624 }
1625 }
1626}
1627
1628fn derive_legacy_chain_id(v: U256) -> Option<u64> {
1629 let v = u64::try_from(v).ok()?;
1630 if v < 35 { None } else { Some((v - 35) / 2) }
1635}
1636
1637impl TxType {
1638 pub fn from_u8(value: u8) -> Option<Self> {
1639 match value {
1640 0x00 => Some(Self::Legacy),
1641 0x01 => Some(Self::EIP2930),
1642 0x02 => Some(Self::EIP1559),
1643 0x03 => Some(Self::EIP4844),
1644 0x04 => Some(Self::EIP7702),
1645 0x06 => Some(Self::Frame),
1646 0x7d => Some(Self::FeeToken),
1647 0x7e => Some(Self::Privileged),
1648 _ => None,
1649 }
1650 }
1651
1652 pub fn is_l2_only(self) -> bool {
1660 match self {
1661 Self::Legacy
1662 | Self::EIP2930
1663 | Self::EIP1559
1664 | Self::EIP4844
1665 | Self::EIP7702
1666 | Self::Frame => false,
1667 Self::FeeToken | Self::Privileged => true,
1668 }
1669 }
1670}
1671
1672impl PrivilegedL2Transaction {
1673 pub fn get_privileged_hash(&self) -> Option<H256> {
1677 let to = match self.to {
1679 TxKind::Call(to) => to,
1680 _ => return None,
1681 };
1682
1683 let value = self.value.to_big_endian();
1684
1685 let u256_nonce = U256::from(self.nonce);
1688 let nonce = u256_nonce.to_big_endian();
1689
1690 Some(crate::utils::keccak(
1691 [
1692 U256::from(self.chain_id).to_big_endian().as_ref(),
1693 self.from.as_bytes(),
1694 to.as_bytes(),
1695 &nonce,
1696 &value,
1697 &U256::from(self.gas_limit).to_big_endian(),
1698 keccak(&self.data).as_bytes(),
1699 ]
1700 .concat(),
1701 ))
1702 }
1703}
1704
1705#[derive(Clone, Debug, PartialEq, Eq, Default, RSerialize, RDeserialize, Archive)]
1706pub struct FeeTokenTransaction {
1707 pub chain_id: u64,
1708 pub nonce: u64,
1709 pub max_priority_fee_per_gas: u64,
1710 pub max_fee_per_gas: u64,
1711 pub gas_limit: u64,
1712 pub to: TxKind,
1713 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
1714 pub value: U256,
1715 #[rkyv(with=crate::rkyv_utils::BytesWrapper)]
1716 pub data: Bytes,
1717 #[rkyv(with=rkyv::with::Map<crate::rkyv_utils::AccessListItemWrapper>)]
1718 pub access_list: AccessList,
1719 #[rkyv(with=crate::rkyv_utils::H160Wrapper)]
1720 pub fee_token: Address,
1721 pub signature_y_parity: bool,
1722 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
1723 pub signature_r: U256,
1724 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
1725 pub signature_s: U256,
1726 #[rkyv(with=rkyv::with::Skip)]
1727 pub inner_hash: OnceCell<H256>,
1728 #[rkyv(with=rkyv::with::Skip)]
1729 pub sender_cache: OnceCell<Address>,
1730 #[rkyv(with=rkyv::with::Skip)]
1731 pub cached_canonical: OnceCell<Vec<u8>>,
1732}
1733
1734#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, RSerialize, RDeserialize, Archive)]
1736#[repr(u8)]
1737pub enum FrameMode {
1738 #[default]
1739 Default = 0,
1740 Verify = 1,
1741 Sender = 2,
1742}
1743
1744impl FrameMode {
1745 pub fn from_u8(val: u8) -> Option<Self> {
1748 match val {
1749 0 => Some(FrameMode::Default),
1750 1 => Some(FrameMode::Verify),
1751 2 => Some(FrameMode::Sender),
1752 _ => None,
1753 }
1754 }
1755}
1756
1757impl From<FrameMode> for u8 {
1758 fn from(mode: FrameMode) -> u8 {
1759 match mode {
1760 FrameMode::Default => 0,
1761 FrameMode::Verify => 1,
1762 FrameMode::Sender => 2,
1763 }
1764 }
1765}
1766
1767#[derive(Clone, Debug, PartialEq, Eq, Default, RSerialize, RDeserialize, Archive)]
1773pub struct Frame {
1774 pub mode: u8,
1775 pub flags: u8,
1776 #[rkyv(with=rkyv::with::Map<crate::rkyv_utils::H160Wrapper>)]
1777 pub target: Option<Address>,
1778 pub gas_limit: u64,
1779 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
1783 pub value: U256,
1784 #[rkyv(with=crate::rkyv_utils::BytesWrapper)]
1785 pub data: Bytes,
1786}
1787
1788impl Frame {
1789 pub fn execution_mode(&self) -> FrameMode {
1791 FrameMode::from_u8(self.mode).unwrap_or_default()
1792 }
1793
1794 pub fn scope_restriction(&self) -> u8 {
1796 self.flags & 0x03
1797 }
1798
1799 pub fn is_atomic_batch(&self) -> bool {
1801 (self.flags >> 2) & 1 == 1
1802 }
1803
1804 pub fn is_expiry_verifier(&self) -> bool {
1807 self.execution_mode() == FrameMode::Verify
1808 && self.target == Some(frame_tx_expiry_verifier())
1809 }
1810}
1811
1812impl RLPEncode for Frame {
1813 fn encode(&self, buf: &mut dyn bytes::BufMut) {
1814 let target_kind = match self.target {
1816 Some(addr) => TxKind::Call(addr),
1817 None => TxKind::Create, };
1819 Encoder::new(buf)
1820 .encode_field(&(self.mode as u64))
1821 .encode_field(&(self.flags as u64))
1822 .encode_field(&target_kind)
1823 .encode_field(&self.gas_limit)
1824 .encode_field(&self.value)
1825 .encode_field(&self.data)
1826 .finish();
1827 }
1828}
1829
1830impl RLPDecode for Frame {
1831 fn decode_unfinished(rlp: &[u8]) -> Result<(Self, &[u8]), RLPDecodeError> {
1832 let decoder = Decoder::new(rlp)?;
1833 let (mode_u64, decoder): (u64, _) = decoder.decode_field("mode")?;
1834 let mode = u8::try_from(mode_u64)
1835 .map_err(|_| RLPDecodeError::Custom(format!("Frame mode too large: {mode_u64}")))?;
1836 let (flags_u64, decoder): (u64, _) = decoder.decode_field("flags")?;
1837 let flags = u8::try_from(flags_u64)
1838 .map_err(|_| RLPDecodeError::Custom(format!("Frame flags too large: {flags_u64}")))?;
1839 let (target_kind, decoder): (TxKind, _) = decoder.decode_field("target")?;
1840 let target = match target_kind {
1841 TxKind::Call(addr) => Some(addr),
1842 TxKind::Create => None,
1843 };
1844 let (gas_limit, decoder) = decoder.decode_field("gas_limit")?;
1845 let (value, decoder): (U256, _) = decoder.decode_field("value")?;
1846 let (data, decoder) = decoder.decode_field("data")?;
1847 let frame = Frame {
1848 mode,
1849 flags,
1850 target,
1851 gas_limit,
1852 value,
1853 data,
1854 };
1855 Ok((frame, decoder.finish()?))
1856 }
1857}
1858
1859#[derive(Clone, Debug, PartialEq, Eq, Default, RSerialize, RDeserialize, Archive)]
1864pub struct FrameSignature {
1865 pub scheme: u8,
1866 #[rkyv(with=crate::rkyv_utils::H160Wrapper)]
1867 pub signer: Address,
1868 #[rkyv(with=crate::rkyv_utils::BytesWrapper)]
1869 pub msg: Bytes,
1870 #[rkyv(with=crate::rkyv_utils::BytesWrapper)]
1871 pub signature: Bytes,
1872}
1873
1874impl RLPEncode for FrameSignature {
1875 fn encode(&self, buf: &mut dyn bytes::BufMut) {
1876 Encoder::new(buf)
1877 .encode_field(&self.scheme)
1878 .encode_field(&self.signer)
1879 .encode_field(&self.msg)
1880 .encode_field(&self.signature)
1881 .finish();
1882 }
1883}
1884
1885impl RLPDecode for FrameSignature {
1886 fn decode_unfinished(rlp: &[u8]) -> Result<(FrameSignature, &[u8]), RLPDecodeError> {
1887 let decoder = Decoder::new(rlp)?;
1888 let (scheme, decoder) = decoder.decode_field("scheme")?;
1889 let (signer, decoder) = decoder.decode_field("signer")?;
1890 let (msg, decoder) = decoder.decode_field("msg")?;
1891 let (signature, decoder) = decoder.decode_field("signature")?;
1892 Ok((
1893 FrameSignature {
1894 scheme,
1895 signer,
1896 msg,
1897 signature,
1898 },
1899 decoder.finish()?,
1900 ))
1901 }
1902}
1903
1904#[derive(Clone, Debug, PartialEq, Eq, Default, RSerialize, RDeserialize, Archive)]
1908pub struct FrameTransaction {
1909 pub chain_id: u64,
1910 pub nonce: u64,
1911 #[rkyv(with=crate::rkyv_utils::H160Wrapper)]
1912 pub sender: Address,
1913 pub frames: Vec<Frame>,
1914 pub signatures: Vec<FrameSignature>,
1917 pub max_priority_fee_per_gas: u64,
1918 pub max_fee_per_gas: u64,
1919 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
1920 pub max_fee_per_blob_gas: U256,
1921 #[rkyv(with=rkyv::with::Map<crate::rkyv_utils::H256Wrapper>)]
1922 pub blob_versioned_hashes: Vec<H256>,
1923 #[rkyv(with=rkyv::with::Skip)]
1924 pub inner_hash: OnceCell<H256>,
1925 #[rkyv(with=rkyv::with::Skip)]
1926 pub cached_canonical: OnceCell<Vec<u8>>,
1927}
1928
1929pub const FRAME_TX_INTRINSIC_COST: u64 = 15000;
1931pub const FRAME_TX_PER_FRAME_COST: u64 = 475;
1933pub const FRAME_TX_ENTRY_POINT_U64: u64 = 0xaa;
1935pub const FRAME_TX_MAX_FRAMES: usize = 64;
1937pub const FRAME_SIG_SCHEME_SECP256K1: u8 = 0;
1939pub const FRAME_SIG_SCHEME_P256: u8 = 1;
1940pub const FRAME_TX_MAX_VERIFY_GAS: u64 = 100_000;
1946pub const APPROVE_PAYMENT: u8 = 0x1;
1949pub const APPROVE_EXECUTION: u8 = 0x2;
1950pub const APPROVE_EXECUTION_AND_PAYMENT: u8 = 0x3;
1951pub const FRAME_TX_MAX_PENDING_NONCANONICAL_PAYMASTER: u8 = 1;
1956
1957pub fn frame_tx_entry_point() -> Address {
1960 Address::from_low_u64_be(FRAME_TX_ENTRY_POINT_U64)
1961}
1962
1963pub const FRAME_TX_EXPIRY_VERIFIER_U64: u64 = 0x8141;
1965pub const FRAME_TX_EXPIRY_DATA_LENGTH: usize = 8;
1967
1968pub fn frame_tx_expiry_verifier() -> Address {
1970 Address::from_low_u64_be(FRAME_TX_EXPIRY_VERIFIER_U64)
1971}
1972
1973impl FrameTransaction {
1974 pub fn compute_sig_hash(&self) -> H256 {
1980 let mut buf = vec![TxType::Frame as u8];
1981 let elided_signatures: Vec<FrameSignature> = self
1982 .signatures
1983 .iter()
1984 .map(|s| {
1985 if s.msg.is_empty() {
1986 FrameSignature {
1987 scheme: s.scheme,
1988 signer: s.signer,
1989 msg: s.msg.clone(),
1990 signature: Bytes::new(),
1991 }
1992 } else {
1993 s.clone()
1994 }
1995 })
1996 .collect();
1997 Encoder::new(&mut buf)
1999 .encode_field(&self.chain_id)
2000 .encode_field(&self.nonce)
2001 .encode_field(&self.sender)
2002 .encode_field(&self.frames)
2003 .encode_field(&elided_signatures)
2004 .encode_field(&self.max_priority_fee_per_gas)
2005 .encode_field(&self.max_fee_per_gas)
2006 .encode_field(&self.max_fee_per_blob_gas)
2007 .encode_field(&self.blob_versioned_hashes)
2008 .finish();
2009 keccak(&buf)
2010 }
2011
2012 pub fn signature_verification_cost(&self) -> u64 {
2016 self.signatures
2017 .iter()
2018 .map(|s| match s.scheme {
2019 FRAME_SIG_SCHEME_SECP256K1 => 2800u64,
2020 FRAME_SIG_SCHEME_P256 => 6700u64,
2021 _ => 0,
2022 })
2023 .sum()
2024 }
2025
2026 pub fn total_gas_limit(&self) -> u64 {
2029 let mut calldata_gas: u64 = 0;
2030 let mut frames_buf = Vec::new();
2032 self.frames.encode(&mut frames_buf);
2033 for byte in &frames_buf {
2034 calldata_gas = calldata_gas.saturating_add(if *byte == 0 { 4 } else { 16 });
2035 }
2036 let mut sigs_buf = Vec::new();
2038 self.signatures.encode(&mut sigs_buf);
2039 for byte in &sigs_buf {
2040 calldata_gas = calldata_gas.saturating_add(if *byte == 0 { 4 } else { 16 });
2041 }
2042 FRAME_TX_INTRINSIC_COST
2043 .saturating_add((self.frames.len() as u64).saturating_mul(FRAME_TX_PER_FRAME_COST))
2044 .saturating_add(calldata_gas)
2045 .saturating_add(self.signature_verification_cost())
2046 .saturating_add(
2047 self.frames
2048 .iter()
2049 .map(|f| f.gas_limit)
2050 .fold(0u64, |acc, g| acc.saturating_add(g)),
2051 )
2052 }
2053
2054 pub fn expiry_deadline(&self) -> Option<u64> {
2057 self.frames
2058 .iter()
2059 .find(|f| f.is_expiry_verifier())
2060 .and_then(|f| {
2061 let bytes: [u8; FRAME_TX_EXPIRY_DATA_LENGTH] = f.data.as_ref().try_into().ok()?;
2062 Some(u64::from_be_bytes(bytes))
2063 })
2064 }
2065
2066 pub fn validate_static_constraints(&self) -> Result<(), String> {
2069 if self.sender == Address::zero() {
2071 return Err("tx.sender must not be zero address".to_string());
2072 }
2073 if self.frames.is_empty() || self.frames.len() > FRAME_TX_MAX_FRAMES {
2074 return Err(format!(
2075 "Frame count must be between 1 and {FRAME_TX_MAX_FRAMES}"
2076 ));
2077 }
2078 for (i, sig) in self.signatures.iter().enumerate() {
2081 if sig.scheme != FRAME_SIG_SCHEME_SECP256K1 && sig.scheme != FRAME_SIG_SCHEME_P256 {
2082 return Err(format!("Signature {i}: unsupported scheme {}", sig.scheme));
2083 }
2084 match sig.msg.len() {
2085 0 => {}
2086 32 => {
2087 if sig.msg.iter().all(|&b| b == 0) {
2088 return Err(format!(
2089 "Signature {i}: explicit msg must not be zero digest"
2090 ));
2091 }
2092 }
2093 other => {
2094 return Err(format!(
2095 "Signature {i}: msg must be empty or 32 bytes, got {other}"
2096 ));
2097 }
2098 }
2099 }
2100 let mut total_frame_gas: u128 = 0;
2103 let mut expiry_frame_count: usize = 0;
2104 for (i, frame) in self.frames.iter().enumerate() {
2105 if frame.mode >= 3 {
2107 return Err(format!("Frame {i}: reserved execution mode {}", frame.mode));
2108 }
2109 if frame.flags >= 8 {
2111 return Err(format!(
2112 "Frame {i}: reserved flag bits must be zero (flags={:#04x})",
2113 frame.flags
2114 ));
2115 }
2116 if frame.is_expiry_verifier() {
2121 expiry_frame_count = expiry_frame_count.saturating_add(1);
2122 if expiry_frame_count > 1 {
2123 return Err(format!("Frame {i}: more than one expiry verifier frame"));
2124 }
2125 if frame.flags != 0 {
2126 return Err(format!(
2127 "Frame {i}: expiry verifier frame must have flags == 0"
2128 ));
2129 }
2130 if frame.data.len() != FRAME_TX_EXPIRY_DATA_LENGTH {
2131 return Err(format!(
2132 "Frame {i}: expiry verifier frame data must be {FRAME_TX_EXPIRY_DATA_LENGTH} bytes"
2133 ));
2134 }
2135 }
2136 if frame.mode != FrameMode::Sender as u8 && !frame.value.is_zero() {
2140 return Err(format!(
2141 "Frame {i}: non-zero value only allowed in SENDER mode (mode={}, value={})",
2142 frame.mode, frame.value
2143 ));
2144 }
2145 if frame.gas_limit > i64::MAX as u64 {
2153 return Err(format!(
2154 "Frame {i}: gas_limit {} exceeds 2**63-1",
2155 frame.gas_limit
2156 ));
2157 }
2158 total_frame_gas = total_frame_gas
2159 .checked_add(frame.gas_limit as u128)
2160 .ok_or_else(|| format!("Frame {i}: cumulative gas_limit overflow"))?;
2161 if total_frame_gas > i64::MAX as u128 {
2162 return Err(format!(
2163 "Frame {i}: cumulative frame gas_limit {} exceeds 2**63-1",
2164 total_frame_gas
2165 ));
2166 }
2167 if frame.is_atomic_batch() && i + 1 >= self.frames.len() {
2171 return Err(format!("Frame {i}: atomic batch flag on last frame"));
2172 }
2173 }
2174 Ok(())
2175 }
2176
2177 pub fn validation_prefix(&self) -> Result<ValidationPrefix, FrameValidationError> {
2193 let non_expiry: Vec<usize> = self
2195 .frames
2196 .iter()
2197 .enumerate()
2198 .filter(|(_, f)| !f.is_expiry_verifier())
2199 .map(|(i, _)| i)
2200 .collect();
2201
2202 let frame = |pos: usize| -> Option<&Frame> {
2204 non_expiry.get(pos).and_then(|&idx| self.frames.get(idx))
2205 };
2206
2207 let is_default = |pos: usize| -> bool {
2208 frame(pos).is_some_and(|f| f.execution_mode() == FrameMode::Default)
2209 };
2210 let is_verify = |pos: usize| -> bool {
2211 frame(pos).is_some_and(|f| f.execution_mode() == FrameMode::Verify)
2212 };
2213 let scope_of = |pos: usize| -> u8 { frame(pos).map_or(0, |f| f.scope_restriction()) };
2214
2215 if non_expiry.is_empty() {
2216 return Err(FrameValidationError::UnrecognizedPrefix);
2217 }
2218
2219 if is_default(0)
2223 && is_verify(1)
2224 && scope_of(1) == APPROVE_EXECUTION
2225 && is_verify(2)
2226 && scope_of(2) == APPROVE_PAYMENT
2227 {
2228 return Ok(ValidationPrefix {
2229 shape: PrefixShape::DeployOnlyVerifyPay,
2230 frame_indices: vec![non_expiry[0], non_expiry[1], non_expiry[2]],
2231 deploy_index: Some(non_expiry[0]),
2232 pay_index: Some(non_expiry[2]),
2233 });
2234 }
2235
2236 if is_default(0) && is_verify(1) && scope_of(1) == APPROVE_EXECUTION_AND_PAYMENT {
2238 return Ok(ValidationPrefix {
2239 shape: PrefixShape::DeploySelfVerify,
2240 frame_indices: vec![non_expiry[0], non_expiry[1]],
2241 deploy_index: Some(non_expiry[0]),
2242 pay_index: Some(non_expiry[1]),
2243 });
2244 }
2245
2246 if is_verify(0)
2248 && scope_of(0) == APPROVE_EXECUTION
2249 && is_verify(1)
2250 && scope_of(1) == APPROVE_PAYMENT
2251 {
2252 return Ok(ValidationPrefix {
2253 shape: PrefixShape::OnlyVerifyPay,
2254 frame_indices: vec![non_expiry[0], non_expiry[1]],
2255 deploy_index: None,
2256 pay_index: Some(non_expiry[1]),
2257 });
2258 }
2259
2260 if is_verify(0) && scope_of(0) == APPROVE_EXECUTION_AND_PAYMENT {
2262 return Ok(ValidationPrefix {
2263 shape: PrefixShape::SelfVerify,
2264 frame_indices: vec![non_expiry[0]],
2265 deploy_index: None,
2266 pay_index: Some(non_expiry[0]),
2267 });
2268 }
2269
2270 Err(FrameValidationError::UnrecognizedPrefix)
2271 }
2272
2273 pub fn validate_prefix_structure(
2287 &self,
2288 prefix: &ValidationPrefix,
2289 ) -> Result<(), FrameValidationError> {
2290 let mut deploy_count = 0usize;
2291
2292 for &idx in &prefix.frame_indices {
2293 let frame = &self.frames[idx];
2294
2295 if frame.is_atomic_batch() {
2296 return Err(FrameValidationError::AtomicBatchInPrefix { frame_index: idx });
2297 }
2298
2299 match prefix.deploy_index {
2300 Some(deploy_idx) if deploy_idx == idx => {
2301 deploy_count += 1;
2303 if deploy_count > 1 {
2304 return Err(FrameValidationError::MultipleDeploys { frame_index: idx });
2305 }
2306 if prefix.frame_indices.first() != Some(&idx) {
2311 return Err(FrameValidationError::DeployNotFirst { frame_index: idx });
2312 }
2313 if frame.execution_mode() != FrameMode::Default {
2314 return Err(FrameValidationError::DeployNotDefaultMode {
2315 frame_index: idx,
2316 });
2317 }
2318 }
2319 _ => {
2320 if frame.execution_mode() != FrameMode::Verify {
2322 return Err(FrameValidationError::VerifyFrameNotVerifyMode {
2323 frame_index: idx,
2324 });
2325 }
2326
2327 let target_ok = match frame.target {
2329 None => true,
2330 Some(addr) => addr == self.sender,
2331 };
2332 if !target_ok {
2333 return Err(FrameValidationError::VerifyTargetNotSender {
2334 frame_index: idx,
2335 });
2336 }
2337
2338 let expected_scope = match prefix.shape {
2340 PrefixShape::SelfVerify | PrefixShape::DeploySelfVerify => {
2341 APPROVE_EXECUTION_AND_PAYMENT
2342 }
2343 PrefixShape::OnlyVerifyPay | PrefixShape::DeployOnlyVerifyPay => {
2344 if prefix.pay_index == Some(idx) {
2346 APPROVE_PAYMENT
2347 } else {
2348 APPROVE_EXECUTION
2349 }
2350 }
2351 };
2352 if frame.scope_restriction() != expected_scope {
2353 return Err(FrameValidationError::WrongScopeRestriction {
2354 frame_index: idx,
2355 expected: expected_scope,
2356 actual: frame.scope_restriction(),
2357 });
2358 }
2359 }
2360 }
2361 }
2362
2363 let prefix_gas: u64 = prefix
2365 .frame_indices
2366 .iter()
2367 .map(|&i| self.frames[i].gas_limit)
2368 .fold(0u64, |acc, g| acc.saturating_add(g));
2369 let total_verify_gas = prefix_gas.saturating_add(self.signature_verification_cost());
2370 if total_verify_gas > FRAME_TX_MAX_VERIFY_GAS {
2371 return Err(FrameValidationError::VerifyGasBudgetExceeded {
2372 actual: total_verify_gas,
2373 limit: FRAME_TX_MAX_VERIFY_GAS,
2374 });
2375 }
2376
2377 Ok(())
2378 }
2379}
2380
2381#[derive(Clone, Debug, PartialEq, Eq)]
2383pub enum PrefixShape {
2384 SelfVerify,
2386 DeploySelfVerify,
2388 OnlyVerifyPay,
2390 DeployOnlyVerifyPay,
2392}
2393
2394#[derive(Clone, Debug, PartialEq, Eq)]
2396pub struct ValidationPrefix {
2397 pub shape: PrefixShape,
2399 pub frame_indices: Vec<usize>,
2402 pub deploy_index: Option<usize>,
2404 pub pay_index: Option<usize>,
2406}
2407
2408#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
2411pub enum FrameValidationError {
2412 #[error("frame transaction validation prefix does not match any recognized shape")]
2413 UnrecognizedPrefix,
2414 #[error("frame {frame_index}: prefix has more than one DEFAULT deploy frame")]
2415 MultipleDeploys { frame_index: usize },
2416 #[error("frame {frame_index}: deploy frame must be at index 0")]
2417 DeployNotFirst { frame_index: usize },
2418 #[error("frame {frame_index}: deploy frame must use DEFAULT execution mode")]
2419 DeployNotDefaultMode { frame_index: usize },
2420 #[error("frame {frame_index}: prefix VERIFY frame does not use VERIFY execution mode")]
2421 VerifyFrameNotVerifyMode { frame_index: usize },
2422 #[error("frame {frame_index}: VERIFY frame target is not tx.sender")]
2423 VerifyTargetNotSender { frame_index: usize },
2424 #[error("frame {frame_index}: scope restriction is {actual:#04x}, expected {expected:#04x}")]
2425 WrongScopeRestriction {
2426 frame_index: usize,
2427 expected: u8,
2428 actual: u8,
2429 },
2430 #[error("frame {frame_index}: prefix frame has atomic-batch flag set")]
2431 AtomicBatchInPrefix { frame_index: usize },
2432 #[error("prefix gas budget exceeded: {actual} > {limit} (MAX_VERIFY_GAS)")]
2433 VerifyGasBudgetExceeded { actual: u64, limit: u64 },
2434}
2435
2436impl RLPEncode for FrameTransaction {
2437 fn encode(&self, buf: &mut dyn bytes::BufMut) {
2438 Encoder::new(buf)
2439 .encode_field(&self.chain_id)
2440 .encode_field(&self.nonce)
2441 .encode_field(&self.sender)
2442 .encode_field(&self.frames)
2443 .encode_field(&self.signatures)
2444 .encode_field(&self.max_priority_fee_per_gas)
2445 .encode_field(&self.max_fee_per_gas)
2446 .encode_field(&self.max_fee_per_blob_gas)
2447 .encode_field(&self.blob_versioned_hashes)
2448 .finish();
2449 }
2450}
2451
2452impl RLPDecode for FrameTransaction {
2453 fn decode_unfinished(rlp: &[u8]) -> Result<(FrameTransaction, &[u8]), RLPDecodeError> {
2454 let decoder = Decoder::new(rlp)?;
2455 let (chain_id, decoder) = decoder.decode_field("chain_id")?;
2456 let (nonce, decoder) = decode_nonce_field(decoder)?;
2457 let (sender, decoder) = decoder.decode_field("sender")?;
2458 let (frames, decoder) = decoder.decode_field("frames")?;
2459 let (signatures, decoder) = decoder.decode_field("signatures")?;
2460 let (max_priority_fee_per_gas, decoder) =
2461 decoder.decode_field("max_priority_fee_per_gas")?;
2462 let (max_fee_per_gas, decoder) = decoder.decode_field("max_fee_per_gas")?;
2463 let (max_fee_per_blob_gas, decoder) = decoder.decode_field("max_fee_per_blob_gas")?;
2464 let (blob_versioned_hashes, decoder) = decoder.decode_field("blob_versioned_hashes")?;
2465 let tx = FrameTransaction {
2466 chain_id,
2467 nonce,
2468 sender,
2469 frames,
2470 signatures,
2471 max_priority_fee_per_gas,
2472 max_fee_per_gas,
2473 max_fee_per_blob_gas,
2474 blob_versioned_hashes,
2475 inner_hash: OnceCell::new(),
2476 cached_canonical: OnceCell::new(),
2477 };
2478 Ok((tx, decoder.finish()?))
2479 }
2480}
2481
2482impl PayloadRLPEncode for FrameTransaction {
2483 fn encode_payload(&self, buf: &mut dyn bytes::BufMut) {
2484 buf.put_u8(TxType::Frame as u8);
2485 self.encode(buf);
2486 }
2487}
2488
2489mod canonic_encoding {
2495 use super::*;
2496
2497 impl Transaction {
2498 pub fn decode_canonical(bytes: &[u8]) -> Result<Self, RLPDecodeError> {
2504 match bytes.first() {
2506 Some(tx_type) if *tx_type < 0x7f => {
2508 let tx_bytes = &bytes[1..];
2510 match *tx_type {
2511 0x0 => {
2513 LegacyTransaction::decode(tx_bytes).map(Transaction::LegacyTransaction)
2514 } 0x1 => EIP2930Transaction::decode(tx_bytes)
2517 .map(Transaction::EIP2930Transaction),
2518 0x2 => EIP1559Transaction::decode(tx_bytes)
2520 .map(Transaction::EIP1559Transaction),
2521 0x3 => EIP4844Transaction::decode(tx_bytes)
2523 .map(Transaction::EIP4844Transaction),
2524 0x4 => EIP7702Transaction::decode(tx_bytes)
2526 .map(Transaction::EIP7702Transaction),
2527 0x6 => {
2529 FrameTransaction::decode(tx_bytes).map(Transaction::FrameTransaction)
2530 }
2531 0x7d => FeeTokenTransaction::decode(tx_bytes)
2533 .map(Transaction::FeeTokenTransaction),
2534 0x7e => PrivilegedL2Transaction::decode(tx_bytes)
2536 .map(Transaction::PrivilegedL2Transaction),
2537 ty => Err(RLPDecodeError::Custom(format!(
2538 "Invalid transaction type: {ty}"
2539 ))),
2540 }
2541 }
2542 _ => LegacyTransaction::decode(bytes).map(Transaction::LegacyTransaction),
2544 }
2545 }
2546
2547 pub fn encode_canonical(&self, buf: &mut dyn bytes::BufMut) {
2553 match self {
2554 Transaction::LegacyTransaction(_) => {}
2556 _ => buf.put_u8(self.tx_type() as u8),
2557 }
2558 match self {
2559 Transaction::LegacyTransaction(t) => t.encode(buf),
2560 Transaction::EIP2930Transaction(t) => t.encode(buf),
2561 Transaction::EIP1559Transaction(t) => t.encode(buf),
2562 Transaction::EIP4844Transaction(t) => t.encode(buf),
2563 Transaction::EIP7702Transaction(t) => t.encode(buf),
2564 Transaction::FeeTokenTransaction(t) => t.encode(buf),
2565 Transaction::PrivilegedL2Transaction(t) => t.encode(buf),
2566 Transaction::FrameTransaction(t) => t.encode(buf),
2567 };
2568 }
2569
2570 pub fn encode_canonical_to_vec(&self) -> Vec<u8> {
2576 if let Some(cell) = self.cached_canonical_cell() {
2577 return cell
2578 .get_or_init(|| {
2579 let mut buf = Vec::new();
2580 self.encode_canonical(&mut buf);
2581 buf
2582 })
2583 .clone();
2584 }
2585 let mut buf = Vec::new();
2586 self.encode_canonical(&mut buf);
2587 buf
2588 }
2589
2590 pub fn encode_canonical_len(&self) -> usize {
2595 let prefix_len = match self {
2596 Transaction::LegacyTransaction(_) => 0,
2597 _ => 1,
2598 };
2599 let inner_len = match self {
2600 Transaction::LegacyTransaction(t) => t.length(),
2601 Transaction::EIP2930Transaction(t) => t.length(),
2602 Transaction::EIP1559Transaction(t) => t.length(),
2603 Transaction::EIP4844Transaction(t) => t.length(),
2604 Transaction::EIP7702Transaction(t) => t.length(),
2605 Transaction::FeeTokenTransaction(t) => t.length(),
2606 Transaction::PrivilegedL2Transaction(t) => t.length(),
2607 Transaction::FrameTransaction(t) => t.length(),
2608 };
2609 prefix_len + inner_len
2610 }
2611 }
2612
2613 impl P2PTransaction {
2614 pub fn tx_type(&self) -> TxType {
2615 match self {
2616 P2PTransaction::LegacyTransaction(_) => TxType::Legacy,
2617 P2PTransaction::EIP2930Transaction(_) => TxType::EIP2930,
2618 P2PTransaction::EIP1559Transaction(_) => TxType::EIP1559,
2619 P2PTransaction::EIP4844TransactionWithBlobs(_) => TxType::EIP4844,
2620 P2PTransaction::EIP7702Transaction(_) => TxType::EIP7702,
2621 P2PTransaction::FeeTokenTransaction(_) => TxType::FeeToken,
2622 P2PTransaction::FrameTransaction(_) => TxType::Frame,
2623 }
2624 }
2625
2626 pub fn encode_canonical(&self, buf: &mut dyn bytes::BufMut) {
2627 match self {
2628 P2PTransaction::LegacyTransaction(_) => {}
2630 _ => buf.put_u8(self.tx_type() as u8),
2631 }
2632 match self {
2633 P2PTransaction::LegacyTransaction(t) => t.encode(buf),
2634 P2PTransaction::EIP2930Transaction(t) => t.encode(buf),
2635 P2PTransaction::EIP1559Transaction(t) => t.encode(buf),
2636 P2PTransaction::EIP4844TransactionWithBlobs(t) => t.encode(buf),
2637 P2PTransaction::EIP7702Transaction(t) => t.encode(buf),
2638 P2PTransaction::FeeTokenTransaction(t) => t.encode(buf),
2639 P2PTransaction::FrameTransaction(t) => t.encode(buf),
2640 };
2641 }
2642
2643 pub fn encode_canonical_to_vec(&self) -> Vec<u8> {
2644 let mut buf = Vec::new();
2645 self.encode_canonical(&mut buf);
2646 buf
2647 }
2648
2649 pub fn compute_hash(&self) -> H256 {
2650 match self {
2651 P2PTransaction::LegacyTransaction(t) => {
2652 Transaction::LegacyTransaction(t.clone()).compute_hash(&NativeCrypto)
2653 }
2654 P2PTransaction::EIP2930Transaction(t) => {
2655 Transaction::EIP2930Transaction(t.clone()).compute_hash(&NativeCrypto)
2656 }
2657 P2PTransaction::EIP1559Transaction(t) => {
2658 Transaction::EIP1559Transaction(t.clone()).compute_hash(&NativeCrypto)
2659 }
2660 P2PTransaction::EIP4844TransactionWithBlobs(t) => {
2661 Transaction::EIP4844Transaction(t.tx.clone()).compute_hash(&NativeCrypto)
2662 }
2663 P2PTransaction::EIP7702Transaction(t) => {
2664 Transaction::EIP7702Transaction(t.clone()).compute_hash(&NativeCrypto)
2665 }
2666 P2PTransaction::FeeTokenTransaction(t) => {
2667 Transaction::FeeTokenTransaction(t.clone()).compute_hash(&NativeCrypto)
2668 }
2669 P2PTransaction::FrameTransaction(t) => {
2670 Transaction::FrameTransaction(t.clone()).compute_hash(&NativeCrypto)
2671 }
2672 }
2673 }
2674 }
2675}
2676
2677mod serde_impl {
2681 use ethereum_types::H160;
2682 use serde::Deserialize;
2683 use serde::{Deserializer, de::Error};
2684 use serde_json::Value;
2685 use std::{collections::HashMap, str::FromStr};
2686
2687 #[cfg(feature = "c-kzg")]
2688 use crate::types::BYTES_PER_BLOB;
2689 use crate::types::{AccessListItem, AuthorizationTuple, BlobsBundleError};
2690
2691 use super::*;
2692
2693 impl Serialize for TxKind {
2694 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
2695 where
2696 S: serde::Serializer,
2697 {
2698 match self {
2699 TxKind::Call(address) => serializer.serialize_str(&format!("{address:#x}")),
2700 TxKind::Create => serializer.serialize_none(),
2701 }
2702 }
2703 }
2704
2705 impl<'de> Deserialize<'de> for TxKind {
2706 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2707 where
2708 D: serde::Deserializer<'de>,
2709 {
2710 let str_option = Option::<String>::deserialize(deserializer)?;
2711 match str_option {
2712 Some(str) if !str.is_empty() => Ok(TxKind::Call(
2713 Address::from_str(str.trim_start_matches("0x")).map_err(|_| {
2714 serde::de::Error::custom(format!("Failed to deserialize hex value {str}"))
2715 })?,
2716 )),
2717 _ => Ok(TxKind::Create),
2718 }
2719 }
2720 }
2721
2722 impl Serialize for TxType {
2723 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
2724 where
2725 S: serde::Serializer,
2726 {
2727 serializer.serialize_str(&format!("{:#x}", *self as u8))
2728 }
2729 }
2730
2731 impl<'de> Deserialize<'de> for TxType {
2732 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2733 where
2734 D: serde::Deserializer<'de>,
2735 {
2736 let str = String::deserialize(deserializer)?;
2737 let tx_num = u8::from_str_radix(str.trim_start_matches("0x"), 16).map_err(|_| {
2738 serde::de::Error::custom(format!("Failed to deserialize hex value {str}"))
2739 })?;
2740 TxType::from_u8(tx_num).ok_or_else(|| {
2741 serde::de::Error::custom(format!("Invalid transaction type {tx_num}"))
2742 })
2743 }
2744 }
2745
2746 #[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
2747 #[serde(rename_all = "camelCase")]
2748 pub struct AccessListEntry {
2749 pub address: Address,
2750 pub storage_keys: Vec<H256>,
2751 }
2752
2753 impl From<&AccessListItem> for AccessListEntry {
2754 fn from(value: &AccessListItem) -> AccessListEntry {
2755 AccessListEntry {
2756 address: value.0,
2757 storage_keys: value.1.clone(),
2758 }
2759 }
2760 }
2761
2762 #[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
2763 #[serde(rename_all = "camelCase")]
2764 pub struct AuthorizationTupleEntry {
2765 pub chain_id: U256,
2766 pub address: Address,
2767 #[serde(default, with = "crate::serde_utils::u64::hex_str")]
2768 pub nonce: u64,
2769 pub y_parity: U256,
2770 pub r: U256,
2771 pub s: U256,
2772 }
2773
2774 impl From<&AuthorizationTuple> for AuthorizationTupleEntry {
2775 fn from(value: &AuthorizationTuple) -> AuthorizationTupleEntry {
2776 AuthorizationTupleEntry {
2777 chain_id: value.chain_id,
2778 address: value.address,
2779 nonce: value.nonce,
2780 y_parity: value.y_parity,
2781 r: value.r_signature,
2782 s: value.s_signature,
2783 }
2784 }
2785 }
2786
2787 impl From<AuthorizationTupleEntry> for AuthorizationTuple {
2788 fn from(entry: AuthorizationTupleEntry) -> AuthorizationTuple {
2789 AuthorizationTuple {
2790 chain_id: entry.chain_id,
2791 address: entry.address,
2792 nonce: entry.nonce,
2793 y_parity: entry.y_parity,
2794 r_signature: entry.r,
2795 s_signature: entry.s,
2796 }
2797 }
2798 }
2799
2800 #[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
2801 #[serde(rename_all = "camelCase")]
2802 pub struct FrameEntry {
2803 #[serde(with = "crate::serde_utils::u64::hex_str")]
2804 pub mode: u64,
2805 #[serde(with = "crate::serde_utils::u64::hex_str")]
2806 pub flags: u64,
2807 pub to: Option<Address>,
2808 #[serde(with = "crate::serde_utils::u64::hex_str")]
2809 pub gas_limit: u64,
2810 #[serde(
2811 default,
2812 serialize_with = "serialize_u256_hex",
2813 deserialize_with = "crate::serde_utils::u256::deser_hex_str"
2814 )]
2815 pub value: U256,
2816 #[serde(with = "crate::serde_utils::bytes")]
2817 pub data: Bytes,
2818 }
2819
2820 #[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
2822 #[serde(rename_all = "camelCase")]
2823 pub struct SignatureEntry {
2824 #[serde(with = "crate::serde_utils::u64::hex_str")]
2825 pub scheme: u64,
2826 pub signer: Address,
2827 #[serde(with = "crate::serde_utils::bytes")]
2828 pub msg: Bytes,
2829 #[serde(with = "crate::serde_utils::bytes")]
2830 pub signature: Bytes,
2831 }
2832
2833 impl From<&FrameSignature> for SignatureEntry {
2834 fn from(value: &FrameSignature) -> SignatureEntry {
2835 SignatureEntry {
2836 scheme: value.scheme as u64,
2837 signer: value.signer,
2838 msg: value.msg.clone(),
2839 signature: value.signature.clone(),
2840 }
2841 }
2842 }
2843
2844 fn serialize_u256_hex<S: serde::Serializer>(value: &U256, s: S) -> Result<S::Ok, S::Error> {
2845 s.serialize_str(&format!("{value:#x}"))
2846 }
2847
2848 impl From<&Frame> for FrameEntry {
2849 fn from(value: &Frame) -> FrameEntry {
2850 FrameEntry {
2851 mode: value.mode as u64,
2852 flags: value.flags as u64,
2853 to: value.target,
2854 gas_limit: value.gas_limit,
2855 value: value.value,
2856 data: value.data.clone(),
2857 }
2858 }
2859 }
2860
2861 impl From<FrameEntry> for Frame {
2862 fn from(entry: FrameEntry) -> Frame {
2863 Frame {
2864 mode: entry.mode as u8,
2865 flags: entry.flags as u8,
2866 target: entry.to,
2867 gas_limit: entry.gas_limit,
2868 value: entry.value,
2869 data: entry.data,
2870 }
2871 }
2872 }
2873
2874 impl Serialize for LegacyTransaction {
2875 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
2876 where
2877 S: serde::Serializer,
2878 {
2879 let mut struct_serializer = serializer.serialize_struct("LegacyTransaction", 11)?;
2880 struct_serializer.serialize_field("type", &TxType::Legacy)?;
2881 struct_serializer.serialize_field("nonce", &format!("{:#x}", self.nonce))?;
2882 struct_serializer.serialize_field("to", &self.to)?;
2883 struct_serializer.serialize_field("gas", &format!("{:#x}", self.gas))?;
2884 struct_serializer.serialize_field("value", &self.value)?;
2885 struct_serializer.serialize_field("input", &format!("0x{:x}", self.data))?;
2886 struct_serializer.serialize_field("gasPrice", &format!("{:#x}", self.gas_price))?;
2887 struct_serializer.serialize_field(
2888 "chainId",
2889 &format!("{:#x}", derive_legacy_chain_id(self.v).unwrap_or_default()),
2890 )?;
2891 struct_serializer.serialize_field("v", &self.v)?;
2892 struct_serializer.serialize_field("r", &self.r)?;
2893 struct_serializer.serialize_field("s", &self.s)?;
2894 struct_serializer.end()
2895 }
2896 }
2897
2898 impl Serialize for EIP2930Transaction {
2899 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
2900 where
2901 S: serde::Serializer,
2902 {
2903 let mut struct_serializer = serializer.serialize_struct("Eip2930Transaction", 12)?;
2904 struct_serializer.serialize_field("type", &TxType::EIP2930)?;
2905 struct_serializer.serialize_field("nonce", &format!("{:#x}", self.nonce))?;
2906 struct_serializer.serialize_field("to", &self.to)?;
2907 struct_serializer.serialize_field("gas", &format!("{:#x}", self.gas_limit))?;
2908 struct_serializer.serialize_field("value", &self.value)?;
2909 struct_serializer.serialize_field("input", &format!("0x{:x}", self.data))?;
2910 struct_serializer.serialize_field("gasPrice", &format!("{:#x}", self.gas_price))?;
2911 struct_serializer.serialize_field(
2912 "accessList",
2913 &self
2914 .access_list
2915 .iter()
2916 .map(AccessListEntry::from)
2917 .collect::<Vec<_>>(),
2918 )?;
2919 struct_serializer.serialize_field("chainId", &format!("{:#x}", self.chain_id))?;
2920 struct_serializer
2921 .serialize_field("yParity", &format!("{:#x}", self.signature_y_parity as u8))?;
2922 struct_serializer
2923 .serialize_field("v", &format!("{:#x}", self.signature_y_parity as u8))?; struct_serializer.serialize_field("r", &self.signature_r)?;
2925 struct_serializer.serialize_field("s", &self.signature_s)?;
2926 struct_serializer.end()
2927 }
2928 }
2929
2930 impl Serialize for EIP1559Transaction {
2931 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
2932 where
2933 S: serde::Serializer,
2934 {
2935 let mut struct_serializer = serializer.serialize_struct("Eip1559Transaction", 14)?;
2936 struct_serializer.serialize_field("type", &TxType::EIP1559)?;
2937 struct_serializer.serialize_field("nonce", &format!("{:#x}", self.nonce))?;
2938 struct_serializer.serialize_field("to", &self.to)?;
2939 struct_serializer.serialize_field("gas", &format!("{:#x}", self.gas_limit))?;
2940 struct_serializer.serialize_field("value", &self.value)?;
2941 struct_serializer.serialize_field("input", &format!("0x{:x}", self.data))?;
2942 struct_serializer.serialize_field(
2943 "maxPriorityFeePerGas",
2944 &format!("{:#x}", self.max_priority_fee_per_gas),
2945 )?;
2946 struct_serializer
2947 .serialize_field("maxFeePerGas", &format!("{:#x}", self.max_fee_per_gas))?;
2948 struct_serializer
2949 .serialize_field("gasPrice", &format!("{:#x}", self.max_fee_per_gas))?;
2950 struct_serializer.serialize_field(
2951 "accessList",
2952 &self
2953 .access_list
2954 .iter()
2955 .map(AccessListEntry::from)
2956 .collect::<Vec<_>>(),
2957 )?;
2958 struct_serializer.serialize_field("chainId", &format!("{:#x}", self.chain_id))?;
2959 struct_serializer
2960 .serialize_field("yParity", &format!("{:#x}", self.signature_y_parity as u8))?;
2961 struct_serializer
2962 .serialize_field("v", &format!("{:#x}", self.signature_y_parity as u8))?; struct_serializer.serialize_field("r", &self.signature_r)?;
2964 struct_serializer.serialize_field("s", &self.signature_s)?;
2965 struct_serializer.end()
2966 }
2967 }
2968
2969 impl Serialize for EIP4844Transaction {
2970 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
2971 where
2972 S: serde::Serializer,
2973 {
2974 let mut struct_serializer = serializer.serialize_struct("Eip4844Transaction", 15)?;
2975 struct_serializer.serialize_field("type", &TxType::EIP4844)?;
2976 struct_serializer.serialize_field("nonce", &format!("{:#x}", self.nonce))?;
2977 struct_serializer.serialize_field("to", &self.to)?;
2978 struct_serializer.serialize_field("gas", &format!("{:#x}", self.gas))?;
2979 struct_serializer.serialize_field("value", &self.value)?;
2980 struct_serializer.serialize_field("input", &format!("0x{:x}", self.data))?;
2981 struct_serializer.serialize_field(
2982 "maxPriorityFeePerGas",
2983 &format!("{:#x}", self.max_priority_fee_per_gas),
2984 )?;
2985 struct_serializer
2986 .serialize_field("maxFeePerGas", &format!("{:#x}", self.max_fee_per_gas))?;
2987 struct_serializer
2988 .serialize_field("gasPrice", &format!("{:#x}", self.max_fee_per_gas))?;
2989 struct_serializer.serialize_field(
2990 "maxFeePerBlobGas",
2991 &format!("{:#x}", self.max_fee_per_blob_gas),
2992 )?;
2993 struct_serializer.serialize_field(
2994 "accessList",
2995 &self
2996 .access_list
2997 .iter()
2998 .map(AccessListEntry::from)
2999 .collect::<Vec<_>>(),
3000 )?;
3001 struct_serializer
3002 .serialize_field("blobVersionedHashes", &self.blob_versioned_hashes)?;
3003 struct_serializer.serialize_field("chainId", &format!("{:#x}", self.chain_id))?;
3004 struct_serializer
3005 .serialize_field("yParity", &format!("{:#x}", self.signature_y_parity as u8))?;
3006 struct_serializer
3007 .serialize_field("v", &format!("{:#x}", self.signature_y_parity as u8))?; struct_serializer.serialize_field("r", &self.signature_r)?;
3009 struct_serializer.serialize_field("s", &self.signature_s)?;
3010 struct_serializer.end()
3011 }
3012 }
3013
3014 impl Serialize for EIP7702Transaction {
3015 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
3016 where
3017 S: serde::Serializer,
3018 {
3019 let mut struct_serializer = serializer.serialize_struct("Eip7702Transaction", 15)?;
3020 struct_serializer.serialize_field("type", &TxType::EIP7702)?;
3021 struct_serializer.serialize_field("nonce", &format!("{:#x}", self.nonce))?;
3022 struct_serializer.serialize_field("to", &self.to)?;
3023 struct_serializer.serialize_field("gas", &format!("{:#x}", self.gas_limit))?;
3024 struct_serializer.serialize_field("value", &self.value)?;
3025 struct_serializer.serialize_field("input", &format!("0x{:x}", self.data))?;
3026 struct_serializer.serialize_field(
3027 "maxPriorityFeePerGas",
3028 &format!("{:#x}", self.max_priority_fee_per_gas),
3029 )?;
3030 struct_serializer
3031 .serialize_field("maxFeePerGas", &format!("{:#x}", self.max_fee_per_gas))?;
3032 struct_serializer
3033 .serialize_field("gasPrice", &format!("{:#x}", self.max_fee_per_gas))?;
3034 struct_serializer.serialize_field(
3035 "accessList",
3036 &self
3037 .access_list
3038 .iter()
3039 .map(AccessListEntry::from)
3040 .collect::<Vec<_>>(),
3041 )?;
3042 struct_serializer.serialize_field(
3043 "authorizationList",
3044 &self
3045 .authorization_list
3046 .iter()
3047 .map(AuthorizationTupleEntry::from)
3048 .collect::<Vec<_>>(),
3049 )?;
3050 struct_serializer.serialize_field("chainId", &format!("{:#x}", self.chain_id))?;
3051 struct_serializer
3052 .serialize_field("yParity", &format!("{:#x}", self.signature_y_parity as u8))?;
3053 struct_serializer
3054 .serialize_field("v", &format!("{:#x}", self.signature_y_parity as u8))?; struct_serializer.serialize_field("r", &self.signature_r)?;
3056 struct_serializer.serialize_field("s", &self.signature_s)?;
3057 struct_serializer.end()
3058 }
3059 }
3060
3061 impl Serialize for PrivilegedL2Transaction {
3062 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
3063 where
3064 S: serde::Serializer,
3065 {
3066 let mut struct_serializer = serializer.serialize_struct("Eip1559Transaction", 14)?;
3067 struct_serializer.serialize_field("type", &TxType::Privileged)?;
3068 struct_serializer.serialize_field("nonce", &format!("{:#x}", self.nonce))?;
3069 struct_serializer.serialize_field("to", &self.to)?;
3070 struct_serializer.serialize_field("gas", &format!("{:#x}", self.gas_limit))?;
3071 struct_serializer.serialize_field("value", &self.value)?;
3072 struct_serializer.serialize_field("input", &format!("0x{:x}", self.data))?;
3073 struct_serializer.serialize_field(
3074 "maxPriorityFeePerGas",
3075 &format!("{:#x}", self.max_priority_fee_per_gas),
3076 )?;
3077 struct_serializer
3078 .serialize_field("maxFeePerGas", &format!("{:#x}", self.max_fee_per_gas))?;
3079 struct_serializer
3080 .serialize_field("gasPrice", &format!("{:#x}", self.max_fee_per_gas))?;
3081 struct_serializer.serialize_field(
3082 "accessList",
3083 &self
3084 .access_list
3085 .iter()
3086 .map(AccessListEntry::from)
3087 .collect::<Vec<_>>(),
3088 )?;
3089 struct_serializer.serialize_field("chainId", &format!("{:#x}", self.chain_id))?;
3090 struct_serializer.serialize_field("sender", &self.from)?;
3091 struct_serializer.end()
3092 }
3093 }
3094
3095 impl Serialize for FeeTokenTransaction {
3096 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
3097 where
3098 S: serde::Serializer,
3099 {
3100 let mut struct_serializer = serializer.serialize_struct("FeeTokenTransaction", 15)?;
3101 struct_serializer.serialize_field("type", &TxType::FeeToken)?;
3102 struct_serializer.serialize_field("nonce", &format!("{:#x}", self.nonce))?;
3103 struct_serializer.serialize_field("to", &self.to)?;
3104 struct_serializer.serialize_field("gas", &format!("{:#x}", self.gas_limit))?;
3105 struct_serializer.serialize_field("value", &self.value)?;
3106 struct_serializer.serialize_field("input", &format!("0x{:x}", self.data))?;
3107 struct_serializer.serialize_field(
3108 "maxPriorityFeePerGas",
3109 &format!("{:#x}", self.max_priority_fee_per_gas),
3110 )?;
3111 struct_serializer
3112 .serialize_field("maxFeePerGas", &format!("{:#x}", self.max_fee_per_gas))?;
3113 struct_serializer
3114 .serialize_field("gasPrice", &format!("{:#x}", self.max_fee_per_gas))?;
3115 struct_serializer.serialize_field(
3116 "accessList",
3117 &self
3118 .access_list
3119 .iter()
3120 .map(AccessListEntry::from)
3121 .collect::<Vec<_>>(),
3122 )?;
3123 struct_serializer.serialize_field("feeToken", &format!("{:#x}", self.fee_token))?;
3124 struct_serializer.serialize_field("chainId", &format!("{:#x}", self.chain_id))?;
3125 struct_serializer
3126 .serialize_field("yParity", &format!("{:#x}", self.signature_y_parity as u8))?;
3127 struct_serializer
3128 .serialize_field("v", &format!("{:#x}", self.signature_y_parity as u8))?;
3129 struct_serializer.serialize_field("r", &self.signature_r)?;
3130 struct_serializer.serialize_field("s", &self.signature_s)?;
3131 struct_serializer.end()
3132 }
3133 }
3134
3135 impl Serialize for FrameTransaction {
3136 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
3137 where
3138 S: serde::Serializer,
3139 {
3140 let mut s = serializer.serialize_struct("FrameTransaction", 10)?;
3141 s.serialize_field("type", &TxType::Frame)?;
3142 s.serialize_field("chainId", &format!("{:#x}", self.chain_id))?;
3143 s.serialize_field("nonce", &format!("{:#x}", self.nonce))?;
3144 s.serialize_field("sender", &format!("{:#x}", self.sender))?;
3145 s.serialize_field(
3146 "frames",
3147 &self.frames.iter().map(FrameEntry::from).collect::<Vec<_>>(),
3148 )?;
3149 s.serialize_field(
3150 "signatures",
3151 &self
3152 .signatures
3153 .iter()
3154 .map(SignatureEntry::from)
3155 .collect::<Vec<_>>(),
3156 )?;
3157 s.serialize_field(
3158 "maxPriorityFeePerGas",
3159 &format!("{:#x}", self.max_priority_fee_per_gas),
3160 )?;
3161 s.serialize_field("maxFeePerGas", &format!("{:#x}", self.max_fee_per_gas))?;
3162 s.serialize_field("maxFeePerBlobGas", &self.max_fee_per_blob_gas)?;
3163 s.serialize_field("blobVersionedHashes", &self.blob_versioned_hashes)?;
3164 s.end()
3165 }
3166 }
3167
3168 impl<'de> Deserialize<'de> for Transaction {
3169 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3170 where
3171 D: serde::Deserializer<'de>,
3172 {
3173 let mut map = <HashMap<String, serde_json::Value>>::deserialize(deserializer)?;
3174 let tx_type =
3175 serde_json::from_value::<TxType>(map.remove("type").unwrap_or(Value::default()))
3176 .unwrap_or_else(|_| {
3177 if map.contains_key("tx_type") {
3178 return TxType::Privileged;
3179 }
3180 TxType::EIP1559
3181 });
3182
3183 let iter = map.into_iter();
3184 match tx_type {
3185 TxType::Legacy => {
3186 LegacyTransaction::deserialize(serde::de::value::MapDeserializer::new(iter))
3187 .map(Transaction::LegacyTransaction)
3188 .map_err(|e| {
3189 serde::de::Error::custom(format!("Couldn't Deserialize Legacy {e}"))
3190 })
3191 }
3192 TxType::EIP2930 => {
3193 EIP2930Transaction::deserialize(serde::de::value::MapDeserializer::new(iter))
3194 .map(Transaction::EIP2930Transaction)
3195 .map_err(|e| {
3196 serde::de::Error::custom(format!("Couldn't Deserialize EIP2930 {e}"))
3197 })
3198 }
3199 TxType::EIP1559 => {
3200 EIP1559Transaction::deserialize(serde::de::value::MapDeserializer::new(iter))
3201 .map(Transaction::EIP1559Transaction)
3202 .map_err(|e| {
3203 serde::de::Error::custom(format!("Couldn't Deserialize EIP1559 {e}"))
3204 })
3205 }
3206 TxType::EIP4844 => {
3207 EIP4844Transaction::deserialize(serde::de::value::MapDeserializer::new(iter))
3208 .map(Transaction::EIP4844Transaction)
3209 .map_err(|e| {
3210 serde::de::Error::custom(format!("Couldn't Deserialize EIP4844 {e}"))
3211 })
3212 }
3213 TxType::EIP7702 => {
3214 EIP7702Transaction::deserialize(serde::de::value::MapDeserializer::new(iter))
3215 .map(Transaction::EIP7702Transaction)
3216 .map_err(|e| {
3217 serde::de::Error::custom(format!("Couldn't Deserialize EIP7702 {e}"))
3218 })
3219 }
3220 TxType::Privileged => PrivilegedL2Transaction::deserialize(
3221 serde::de::value::MapDeserializer::new(iter),
3222 )
3223 .map(Transaction::PrivilegedL2Transaction)
3224 .map_err(|e| {
3225 serde::de::Error::custom(format!("Couldn't Deserialize Privileged: {e}"))
3226 }),
3227 TxType::FeeToken => {
3228 FeeTokenTransaction::deserialize(serde::de::value::MapDeserializer::new(iter))
3229 .map(Transaction::FeeTokenTransaction)
3230 .map_err(|e| {
3231 serde::de::Error::custom(format!("Couldn't Deserialize FeeToken {e}"))
3232 })
3233 }
3234 TxType::Frame => Err(serde::de::Error::custom(
3235 "Frame transaction JSON deserialization not supported",
3236 )),
3237 }
3238 }
3239 }
3240
3241 fn deserialize_input_field(
3242 map: &mut std::collections::HashMap<String, Value>,
3243 ) -> Result<Bytes, serde_json::Error> {
3244 let data_str: String = serde_json::from_value(
3245 map.remove("input")
3246 .ok_or_else(|| serde::de::Error::missing_field("input"))?,
3247 )
3248 .map_err(serde::de::Error::custom)?;
3249 if let Some(stripped) = data_str.strip_prefix("0x") {
3250 match hex::decode(stripped) {
3251 Ok(decoded_bytes) => Ok(Bytes::from(decoded_bytes)),
3252 Err(_) => Err(serde::de::Error::custom(
3253 "Invalid hex format in 'input' field",
3254 ))?,
3255 }
3256 } else {
3257 Err(serde::de::Error::custom(
3258 "'input' field must start with '0x'",
3259 ))?
3260 }
3261 }
3262
3263 fn deserialize_field<'de, T, D>(
3264 map: &mut HashMap<String, serde_json::Value>,
3265 key: &str,
3266 ) -> Result<T, D::Error>
3267 where
3268 D: serde::Deserializer<'de>,
3269 T: serde::de::DeserializeOwned,
3270 {
3271 map.remove(key)
3272 .ok_or_else(|| D::Error::custom(format!("Missing field: {key}")))
3273 .and_then(|value| {
3274 serde_json::from_value(value).map_err(|err| D::Error::custom(err.to_string()))
3275 })
3276 }
3277
3278 fn deserialize_u64_field<'de, D>(
3279 map: &mut HashMap<String, serde_json::Value>,
3280 key: &str,
3281 ) -> Result<u64, D::Error>
3282 where
3283 D: serde::Deserializer<'de>,
3284 {
3285 let value = deserialize_field::<U256, D>(map, key)?;
3286 u64::try_from(value).map_err(|_| D::Error::custom(format!("{key} value overflows u64")))
3287 }
3288
3289 impl<'de> Deserialize<'de> for LegacyTransaction {
3290 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3291 where
3292 D: serde::Deserializer<'de>,
3293 {
3294 let mut map = <HashMap<String, serde_json::Value>>::deserialize(deserializer)?;
3295
3296 Ok(LegacyTransaction {
3297 nonce: deserialize_u64_field::<D>(&mut map, "nonce")?,
3298 gas_price: deserialize_field::<U256, D>(&mut map, "gasPrice")?,
3299 gas: deserialize_u64_field::<D>(&mut map, "gas")?,
3300 to: deserialize_field::<TxKind, D>(&mut map, "to")?,
3301 value: deserialize_field::<U256, D>(&mut map, "value")?,
3302 data: deserialize_input_field(&mut map).map_err(serde::de::Error::custom)?,
3303 v: deserialize_field::<U256, D>(&mut map, "v")?,
3304 r: deserialize_field::<U256, D>(&mut map, "r")?,
3305 s: deserialize_field::<U256, D>(&mut map, "s")?,
3306 ..Default::default()
3307 })
3308 }
3309 }
3310
3311 impl<'de> Deserialize<'de> for EIP2930Transaction {
3312 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3313 where
3314 D: serde::Deserializer<'de>,
3315 {
3316 let mut map = <HashMap<String, serde_json::Value>>::deserialize(deserializer)?;
3317
3318 Ok(EIP2930Transaction {
3319 chain_id: deserialize_u64_field::<D>(&mut map, "chainId")?,
3320 nonce: deserialize_u64_field::<D>(&mut map, "nonce")?,
3321 gas_price: deserialize_field::<U256, D>(&mut map, "gasPrice")?,
3322 gas_limit: deserialize_u64_field::<D>(&mut map, "gas")?,
3323 to: deserialize_field::<TxKind, D>(&mut map, "to")?,
3324 value: deserialize_field::<U256, D>(&mut map, "value")?,
3325 data: deserialize_input_field(&mut map).map_err(serde::de::Error::custom)?,
3326 access_list: deserialize_field::<Vec<AccessListEntry>, D>(&mut map, "accessList")?
3327 .into_iter()
3328 .map(|v| (v.address, v.storage_keys))
3329 .collect::<Vec<_>>(),
3330 signature_y_parity: u8::from_str_radix(
3331 deserialize_field::<String, D>(&mut map, "yParity")?.trim_start_matches("0x"),
3332 16,
3333 )
3334 .map_err(serde::de::Error::custom)?
3335 != 0,
3336 signature_r: deserialize_field::<U256, D>(&mut map, "r")?,
3337 signature_s: deserialize_field::<U256, D>(&mut map, "s")?,
3338 ..Default::default()
3339 })
3340 }
3341 }
3342
3343 impl<'de> Deserialize<'de> for EIP1559Transaction {
3344 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3345 where
3346 D: serde::Deserializer<'de>,
3347 {
3348 let mut map = <HashMap<String, serde_json::Value>>::deserialize(deserializer)?;
3349 Ok(EIP1559Transaction {
3350 chain_id: deserialize_u64_field::<D>(&mut map, "chainId")?,
3351 nonce: deserialize_u64_field::<D>(&mut map, "nonce")?,
3352 max_priority_fee_per_gas: deserialize_u64_field::<D>(
3353 &mut map,
3354 "maxPriorityFeePerGas",
3355 )?,
3356 max_fee_per_gas: deserialize_u64_field::<D>(&mut map, "maxFeePerGas")?,
3357 gas_limit: deserialize_u64_field::<D>(&mut map, "gas")?,
3358 to: deserialize_field::<TxKind, D>(&mut map, "to")?,
3359 value: deserialize_field::<U256, D>(&mut map, "value")?,
3360 data: deserialize_input_field(&mut map).map_err(serde::de::Error::custom)?,
3361 access_list: deserialize_field::<Vec<AccessListEntry>, D>(&mut map, "accessList")?
3362 .into_iter()
3363 .map(|v| (v.address, v.storage_keys))
3364 .collect::<Vec<_>>(),
3365 signature_y_parity: u8::from_str_radix(
3366 deserialize_field::<String, D>(&mut map, "yParity")?.trim_start_matches("0x"),
3367 16,
3368 )
3369 .map_err(serde::de::Error::custom)?
3370 != 0,
3371 signature_r: deserialize_field::<U256, D>(&mut map, "r")?,
3372 signature_s: deserialize_field::<U256, D>(&mut map, "s")?,
3373 ..Default::default()
3374 })
3375 }
3376 }
3377
3378 impl<'de> Deserialize<'de> for EIP4844Transaction {
3379 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3380 where
3381 D: serde::Deserializer<'de>,
3382 {
3383 let mut map = <HashMap<String, serde_json::Value>>::deserialize(deserializer)?;
3384
3385 Ok(EIP4844Transaction {
3386 chain_id: deserialize_u64_field::<D>(&mut map, "chainId")?,
3387 nonce: deserialize_u64_field::<D>(&mut map, "nonce")?,
3388 max_priority_fee_per_gas: deserialize_u64_field::<D>(
3389 &mut map,
3390 "maxPriorityFeePerGas",
3391 )?,
3392 max_fee_per_gas: deserialize_u64_field::<D>(&mut map, "maxFeePerGas")?,
3393 gas: deserialize_u64_field::<D>(&mut map, "gas")?,
3394 to: deserialize_field::<Address, D>(&mut map, "to")?,
3395 value: deserialize_field::<U256, D>(&mut map, "value")?,
3396 data: deserialize_input_field(&mut map).map_err(serde::de::Error::custom)?,
3397 access_list: deserialize_field::<Vec<AccessListEntry>, D>(&mut map, "accessList")?
3398 .into_iter()
3399 .map(|v| (v.address, v.storage_keys))
3400 .collect::<Vec<_>>(),
3401 max_fee_per_blob_gas: deserialize_field::<U256, D>(&mut map, "maxFeePerBlobGas")?,
3402 blob_versioned_hashes: deserialize_field::<Vec<H256>, D>(
3403 &mut map,
3404 "blobVersionedHashes",
3405 )?,
3406 signature_y_parity: u8::from_str_radix(
3407 deserialize_field::<String, D>(&mut map, "yParity")?.trim_start_matches("0x"),
3408 16,
3409 )
3410 .map_err(serde::de::Error::custom)?
3411 != 0,
3412 signature_r: deserialize_field::<U256, D>(&mut map, "r")?,
3413 signature_s: deserialize_field::<U256, D>(&mut map, "s")?,
3414 ..Default::default()
3415 })
3416 }
3417 }
3418
3419 impl<'de> Deserialize<'de> for EIP7702Transaction {
3420 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3421 where
3422 D: serde::Deserializer<'de>,
3423 {
3424 let mut map = <HashMap<String, serde_json::Value>>::deserialize(deserializer)?;
3425
3426 Ok(EIP7702Transaction {
3427 chain_id: deserialize_u64_field::<D>(&mut map, "chainId")?,
3428 nonce: deserialize_u64_field::<D>(&mut map, "nonce")?,
3429 max_priority_fee_per_gas: deserialize_u64_field::<D>(
3430 &mut map,
3431 "maxPriorityFeePerGas",
3432 )?,
3433 max_fee_per_gas: deserialize_u64_field::<D>(&mut map, "maxFeePerGas")?,
3434 gas_limit: deserialize_u64_field::<D>(&mut map, "gas")?,
3435 to: deserialize_field::<Address, D>(&mut map, "to")?,
3436 value: deserialize_field::<U256, D>(&mut map, "value")?,
3437 data: deserialize_input_field(&mut map).map_err(serde::de::Error::custom)?,
3438 access_list: deserialize_field::<Vec<AccessListEntry>, D>(&mut map, "accessList")?
3439 .into_iter()
3440 .map(|v| (v.address, v.storage_keys))
3441 .collect::<Vec<_>>(),
3442 authorization_list: deserialize_field::<Vec<AuthorizationTupleEntry>, D>(
3443 &mut map,
3444 "authorizationList",
3445 )?
3446 .into_iter()
3447 .map(AuthorizationTuple::from)
3448 .collect::<Vec<_>>(),
3449 signature_y_parity: u8::from_str_radix(
3450 deserialize_field::<String, D>(&mut map, "yParity")?.trim_start_matches("0x"),
3451 16,
3452 )
3453 .map_err(serde::de::Error::custom)?
3454 != 0,
3455 signature_r: deserialize_field::<U256, D>(&mut map, "r")?,
3456 signature_s: deserialize_field::<U256, D>(&mut map, "s")?,
3457 ..Default::default()
3458 })
3459 }
3460 }
3461
3462 impl<'de> Deserialize<'de> for PrivilegedL2Transaction {
3463 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3464 where
3465 D: serde::Deserializer<'de>,
3466 {
3467 let mut map = <HashMap<String, serde_json::Value>>::deserialize(deserializer)?;
3468
3469 Ok(PrivilegedL2Transaction {
3470 chain_id: deserialize_u64_field::<D>(&mut map, "chainId")?,
3471 nonce: deserialize_u64_field::<D>(&mut map, "nonce")?,
3472 max_priority_fee_per_gas: deserialize_u64_field::<D>(
3473 &mut map,
3474 "maxPriorityFeePerGas",
3475 )?,
3476 max_fee_per_gas: deserialize_u64_field::<D>(&mut map, "maxFeePerGas")?,
3477 gas_limit: deserialize_u64_field::<D>(&mut map, "gas")?,
3478 to: deserialize_field::<TxKind, D>(&mut map, "to")?,
3479 value: deserialize_field::<U256, D>(&mut map, "value")?,
3480 data: deserialize_input_field(&mut map).map_err(serde::de::Error::custom)?,
3481 access_list: deserialize_field::<Vec<AccessListEntry>, D>(&mut map, "accessList")?
3482 .into_iter()
3483 .map(|v| (v.address, v.storage_keys))
3484 .collect::<Vec<_>>(),
3485 from: deserialize_field::<Address, D>(&mut map, "sender")?,
3486 ..Default::default()
3487 })
3488 }
3489 }
3490
3491 impl<'de> Deserialize<'de> for FeeTokenTransaction {
3492 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3493 where
3494 D: serde::Deserializer<'de>,
3495 {
3496 let mut map = <HashMap<String, serde_json::Value>>::deserialize(deserializer)?;
3497
3498 Ok(FeeTokenTransaction {
3499 chain_id: deserialize_u64_field::<D>(&mut map, "chainId")?,
3500 nonce: deserialize_u64_field::<D>(&mut map, "nonce")?,
3501 max_priority_fee_per_gas: deserialize_u64_field::<D>(
3502 &mut map,
3503 "maxPriorityFeePerGas",
3504 )?,
3505 max_fee_per_gas: deserialize_u64_field::<D>(&mut map, "maxFeePerGas")?,
3506 gas_limit: deserialize_u64_field::<D>(&mut map, "gas")?,
3507 to: deserialize_field::<TxKind, D>(&mut map, "to")?,
3508 value: deserialize_field::<U256, D>(&mut map, "value")?,
3509 data: deserialize_input_field(&mut map).map_err(serde::de::Error::custom)?,
3510 access_list: deserialize_field::<Vec<AccessListEntry>, D>(&mut map, "accessList")?
3511 .into_iter()
3512 .map(|v| (v.address, v.storage_keys))
3513 .collect::<Vec<_>>(),
3514 fee_token: deserialize_field::<Address, D>(&mut map, "feeToken")?,
3515 signature_y_parity: u8::from_str_radix(
3516 deserialize_field::<String, D>(&mut map, "yParity")?.trim_start_matches("0x"),
3517 16,
3518 )
3519 .map_err(serde::de::Error::custom)?
3520 != 0,
3521 signature_r: deserialize_field::<U256, D>(&mut map, "r")?,
3522 signature_s: deserialize_field::<U256, D>(&mut map, "s")?,
3523 ..Default::default()
3524 })
3525 }
3526 }
3527
3528 #[derive(Debug, thiserror::Error)]
3529 pub enum GenericTransactionError {
3530 #[error("Invalid transaction type: {0}")]
3531 InvalidTxType(TxType),
3532 #[error("Blob bundle error: {0}")]
3533 BlobBundleError(#[from] BlobsBundleError),
3534 #[error("Missing field: {0}")]
3535 MissingField(String),
3536 #[error("Invalid field: {0}")]
3537 InvalidField(String),
3538 }
3539
3540 #[derive(Deserialize, Debug, PartialEq, Clone, Default)]
3543 #[serde(rename_all = "camelCase")]
3544 pub struct GenericTransaction {
3545 #[serde(default)]
3546 pub r#type: TxType,
3547 #[serde(default, with = "crate::serde_utils::u64::hex_str_opt")]
3548 pub nonce: Option<u64>,
3549 pub to: TxKind,
3550 #[serde(default)]
3551 pub from: Address,
3552 #[serde(default, with = "crate::serde_utils::u64::hex_str_opt")]
3553 pub gas: Option<u64>,
3554 #[serde(default)]
3555 pub value: U256,
3556 #[serde(default)]
3557 pub gas_price: U256,
3558 #[serde(default, with = "crate::serde_utils::u64::hex_str_opt")]
3559 pub max_priority_fee_per_gas: Option<u64>,
3560 #[serde(default, with = "crate::serde_utils::u64::hex_str_opt")]
3561 pub max_fee_per_gas: Option<u64>,
3562 pub max_fee_per_blob_gas: Option<U256>,
3563 #[serde(default)]
3564 pub access_list: Vec<AccessListEntry>,
3565 #[serde(default)]
3566 pub fee_token: Option<Address>,
3567 #[serde(default)]
3568 pub authorization_list: Option<Vec<AuthorizationTupleEntry>>,
3569 #[serde(default)]
3570 pub blob_versioned_hashes: Vec<H256>,
3571 pub wrapper_version: Option<u8>,
3572 #[serde(default, with = "crate::serde_utils::bytes::vec")]
3573 pub blobs: Vec<Bytes>,
3574 #[serde(default, with = "crate::serde_utils::u64::hex_str_opt")]
3575 pub chain_id: Option<u64>,
3576 #[serde(
3578 flatten,
3579 rename = "input_or_data",
3580 deserialize_with = "deserialize_input",
3581 serialize_with = "crate::serde_utils::bytes::serialize"
3582 )]
3583 pub input: Bytes,
3584 }
3585 pub fn deserialize_input<'de, D>(deserializer: D) -> Result<Bytes, D::Error>
3587 where
3588 D: Deserializer<'de>,
3589 {
3590 let variables = HashMap::<String, Value>::deserialize(deserializer)?;
3593 let data = variables.get("data");
3594 let input = variables.get("input");
3595 let value = match (data, input) {
3596 (None, None) => return Ok(Bytes::new()),
3598 (None, Some(val)) => val,
3599 (Some(val), None) => val,
3600 (Some(val_a), Some(val_b)) => {
3601 if val_a == val_b {
3602 val_a
3603 } else {
3604 return Err(D::Error::custom(
3605 "Transaction has both `data` and `input` fields with different values",
3606 ));
3607 }
3608 }
3609 };
3610 let value = String::deserialize(value).map_err(D::Error::custom)?;
3611 let bytes = hex::decode(value.trim_start_matches("0x"))
3612 .map_err(|e| D::Error::custom(e.to_string()))?;
3613 Ok(Bytes::from(bytes))
3614 }
3615
3616 impl From<EIP1559Transaction> for GenericTransaction {
3617 fn from(value: EIP1559Transaction) -> Self {
3618 Self {
3619 r#type: TxType::EIP1559,
3620 nonce: Some(value.nonce),
3621 to: value.to,
3622 gas: Some(value.gas_limit),
3623 value: value.value,
3624 input: value.data.clone(),
3625 gas_price: U256::from(value.max_fee_per_gas),
3626 max_priority_fee_per_gas: Some(value.max_priority_fee_per_gas),
3627 max_fee_per_gas: Some(value.max_fee_per_gas),
3628 max_fee_per_blob_gas: None,
3629 access_list: value
3630 .access_list
3631 .iter()
3632 .map(AccessListEntry::from)
3633 .collect(),
3634 fee_token: None,
3635 authorization_list: None,
3636 blob_versioned_hashes: vec![],
3637 blobs: vec![],
3638 wrapper_version: None,
3639 chain_id: Some(value.chain_id),
3640 from: Address::default(),
3641 }
3642 }
3643 }
3644
3645 impl TryFrom<GenericTransaction> for EIP1559Transaction {
3646 type Error = GenericTransactionError;
3647
3648 fn try_from(value: GenericTransaction) -> Result<Self, Self::Error> {
3649 if value.r#type != TxType::EIP1559 {
3650 return Err(GenericTransactionError::InvalidTxType(value.r#type));
3651 }
3652
3653 Ok(Self {
3654 nonce: value.nonce.unwrap_or_default(),
3655 to: value.to,
3656 gas_limit: value.gas.unwrap_or_default(),
3657 value: value.value,
3658 data: value.input.clone(),
3659 max_priority_fee_per_gas: value.max_priority_fee_per_gas.unwrap_or_default(),
3660 max_fee_per_gas: value.max_fee_per_gas.unwrap_or(
3661 u64::try_from(value.gas_price).map_err(|_| {
3662 GenericTransactionError::InvalidField("gas_price overflows u64".to_owned())
3663 })?,
3664 ),
3665 access_list: value
3666 .access_list
3667 .into_iter()
3668 .map(|v| (v.address, v.storage_keys))
3669 .collect::<Vec<_>>(),
3670 chain_id: value.chain_id.unwrap_or_default(),
3671 ..Default::default()
3672 })
3673 }
3674 }
3675
3676 impl From<EIP4844Transaction> for GenericTransaction {
3677 fn from(value: EIP4844Transaction) -> Self {
3678 Self {
3679 r#type: TxType::EIP4844,
3680 nonce: Some(value.nonce),
3681 to: TxKind::Call(value.to),
3682 gas: Some(value.gas),
3683 value: value.value,
3684 input: value.data.clone(),
3685 gas_price: U256::from(value.max_fee_per_gas),
3686 max_priority_fee_per_gas: Some(value.max_priority_fee_per_gas),
3687 max_fee_per_gas: Some(value.max_fee_per_gas),
3688 max_fee_per_blob_gas: Some(value.max_fee_per_blob_gas),
3689 access_list: value
3690 .access_list
3691 .iter()
3692 .map(AccessListEntry::from)
3693 .collect(),
3694 fee_token: None,
3695 authorization_list: None,
3696 blob_versioned_hashes: value.blob_versioned_hashes,
3697 blobs: vec![],
3698 wrapper_version: None,
3699 chain_id: Some(value.chain_id),
3700 from: Address::default(),
3701 }
3702 }
3703 }
3704
3705 #[cfg(feature = "c-kzg")]
3706 impl TryFrom<GenericTransaction> for WrappedEIP4844Transaction {
3707 type Error = GenericTransactionError;
3708
3709 fn try_from(value: GenericTransaction) -> Result<Self, Self::Error> {
3710 let blobs = value
3711 .blobs
3712 .iter()
3713 .map(|bytes| {
3714 let slice = bytes.as_ref();
3715 let mut blob = [0u8; BYTES_PER_BLOB];
3716 blob.copy_from_slice(slice);
3717 blob
3718 })
3719 .collect();
3720
3721 let wrapper_version = value.wrapper_version;
3722 Ok(Self {
3723 tx: value.try_into()?,
3724 wrapper_version,
3725 blobs_bundle: BlobsBundle::create_from_blobs(&blobs, wrapper_version)?,
3726 })
3727 }
3728 }
3729
3730 impl TryFrom<GenericTransaction> for EIP4844Transaction {
3731 type Error = GenericTransactionError;
3732
3733 fn try_from(value: GenericTransaction) -> Result<Self, Self::Error> {
3734 if value.r#type != TxType::EIP4844 {
3735 return Err(GenericTransactionError::InvalidTxType(value.r#type));
3736 }
3737 Ok(Self {
3738 nonce: value.nonce.unwrap_or_default(),
3739 to: match value.to {
3740 TxKind::Call(to) => to,
3741 _ => H160::default(),
3742 },
3743 gas: value.gas.unwrap_or_default(),
3744 value: value.value,
3745 data: value.input.clone(),
3746 max_priority_fee_per_gas: value.max_priority_fee_per_gas.unwrap_or_default(),
3747 max_fee_per_gas: value.max_fee_per_gas.unwrap_or(
3748 u64::try_from(value.gas_price).map_err(|_| {
3749 GenericTransactionError::InvalidField("gas_price overflows u64".to_owned())
3750 })?,
3751 ),
3752 max_fee_per_blob_gas: value.max_fee_per_blob_gas.unwrap_or_default(),
3753 access_list: value
3754 .access_list
3755 .into_iter()
3756 .map(|v| (v.address, v.storage_keys))
3757 .collect::<Vec<_>>(),
3758 blob_versioned_hashes: value.blob_versioned_hashes,
3759 chain_id: value.chain_id.unwrap_or_default(),
3760 ..Default::default()
3761 })
3762 }
3763 }
3764
3765 impl From<EIP7702Transaction> for GenericTransaction {
3766 fn from(value: EIP7702Transaction) -> Self {
3767 Self {
3768 r#type: TxType::EIP7702,
3769 nonce: Some(value.nonce),
3770 to: TxKind::Call(value.to),
3771 gas: Some(value.gas_limit),
3772 value: value.value,
3773 input: value.data.clone(),
3774 gas_price: U256::from(value.max_fee_per_gas),
3775 max_priority_fee_per_gas: Some(value.max_priority_fee_per_gas),
3776 max_fee_per_gas: Some(value.max_fee_per_gas),
3777 max_fee_per_blob_gas: None,
3778 access_list: value
3779 .access_list
3780 .iter()
3781 .map(AccessListEntry::from)
3782 .collect(),
3783 fee_token: None,
3784 authorization_list: Some(
3785 value
3786 .authorization_list
3787 .iter()
3788 .map(AuthorizationTupleEntry::from)
3789 .collect(),
3790 ),
3791 blob_versioned_hashes: vec![],
3792 blobs: vec![],
3793 wrapper_version: None,
3794 chain_id: Some(value.chain_id),
3795 from: Address::default(),
3796 }
3797 }
3798 }
3799
3800 impl TryFrom<GenericTransaction> for EIP7702Transaction {
3801 type Error = GenericTransactionError;
3802
3803 fn try_from(value: GenericTransaction) -> Result<Self, Self::Error> {
3804 if value.r#type != TxType::EIP7702 {
3805 return Err(GenericTransactionError::InvalidTxType(value.r#type));
3806 }
3807 let TxKind::Call(to) = value.to else {
3808 return Err(GenericTransactionError::MissingField("to".to_owned()));
3809 };
3810 Ok(Self {
3811 chain_id: value.chain_id.unwrap_or_default(),
3812 nonce: value.nonce.unwrap_or_default(),
3813 max_priority_fee_per_gas: value.max_priority_fee_per_gas.unwrap_or_default(),
3814 max_fee_per_gas: value.max_fee_per_gas.unwrap_or(
3815 u64::try_from(value.gas_price).map_err(|_| {
3816 GenericTransactionError::InvalidField("gas_price overflows u64".to_owned())
3817 })?,
3818 ),
3819 gas_limit: value.gas.unwrap_or_default(),
3820 to,
3821 value: value.value,
3822 data: value.input,
3823 access_list: value
3824 .access_list
3825 .into_iter()
3826 .map(|v| (v.address, v.storage_keys))
3827 .collect::<Vec<_>>(),
3828 authorization_list: value
3829 .authorization_list
3830 .unwrap_or_default()
3831 .into_iter()
3832 .map(AuthorizationTuple::from)
3833 .collect(),
3834 ..Default::default()
3835 })
3836 }
3837 }
3838
3839 impl From<PrivilegedL2Transaction> for GenericTransaction {
3840 fn from(value: PrivilegedL2Transaction) -> Self {
3841 Self {
3842 r#type: TxType::Privileged,
3843 nonce: Some(value.nonce),
3844 to: value.to,
3845 gas: Some(value.gas_limit),
3846 value: value.value,
3847 input: value.data.clone(),
3848 gas_price: U256::from(value.max_fee_per_gas),
3849 max_priority_fee_per_gas: Some(value.max_priority_fee_per_gas),
3850 max_fee_per_gas: Some(value.max_fee_per_gas),
3851 max_fee_per_blob_gas: None,
3852 access_list: value
3853 .access_list
3854 .iter()
3855 .map(AccessListEntry::from)
3856 .collect(),
3857 fee_token: None,
3858 authorization_list: None,
3859 blob_versioned_hashes: vec![],
3860 blobs: vec![],
3861 wrapper_version: None,
3862 chain_id: Some(value.chain_id),
3863 from: value.from,
3864 }
3865 }
3866 }
3867
3868 impl TryFrom<GenericTransaction> for PrivilegedL2Transaction {
3869 type Error = GenericTransactionError;
3870
3871 fn try_from(value: GenericTransaction) -> Result<Self, Self::Error> {
3872 if value.r#type != TxType::Privileged {
3873 return Err(GenericTransactionError::InvalidTxType(value.r#type));
3874 }
3875 Ok(Self {
3876 nonce: value.nonce.unwrap_or_default(),
3877 to: value.to,
3878 gas_limit: value.gas.unwrap_or_default(),
3879 value: value.value,
3880 data: value.input.clone(),
3881 max_priority_fee_per_gas: value.max_priority_fee_per_gas.unwrap_or_default(),
3882 max_fee_per_gas: value.max_fee_per_gas.unwrap_or(
3883 u64::try_from(value.gas_price).map_err(|_| {
3884 GenericTransactionError::InvalidField("gas_price overflows u64".to_owned())
3885 })?,
3886 ),
3887 access_list: value
3888 .access_list
3889 .into_iter()
3890 .map(|v| (v.address, v.storage_keys))
3891 .collect::<Vec<_>>(),
3892 chain_id: value.chain_id.unwrap_or_default(),
3893 from: value.from,
3894 ..Default::default()
3895 })
3896 }
3897 }
3898
3899 impl From<FeeTokenTransaction> for GenericTransaction {
3900 fn from(value: FeeTokenTransaction) -> Self {
3901 Self {
3902 r#type: TxType::FeeToken,
3903 nonce: Some(value.nonce),
3904 to: value.to,
3905 gas: Some(value.gas_limit),
3906 value: value.value,
3907 input: value.data.clone(),
3908 gas_price: U256::from(value.max_fee_per_gas),
3909 max_priority_fee_per_gas: Some(value.max_priority_fee_per_gas),
3910 max_fee_per_gas: Some(value.max_fee_per_gas),
3911 max_fee_per_blob_gas: None,
3912 access_list: value
3913 .access_list
3914 .iter()
3915 .map(AccessListEntry::from)
3916 .collect(),
3917 fee_token: Some(value.fee_token),
3918 authorization_list: None,
3919 blob_versioned_hashes: vec![],
3920 blobs: vec![],
3921 chain_id: Some(value.chain_id),
3922 from: Address::default(),
3923 wrapper_version: None,
3924 }
3925 }
3926 }
3927
3928 impl TryFrom<GenericTransaction> for FeeTokenTransaction {
3929 type Error = GenericTransactionError;
3930
3931 fn try_from(value: GenericTransaction) -> Result<Self, Self::Error> {
3932 if value.r#type != TxType::FeeToken {
3933 return Err(GenericTransactionError::InvalidTxType(value.r#type));
3934 }
3935
3936 Ok(Self {
3937 nonce: value.nonce.unwrap_or_default(),
3938 to: value.to,
3939 gas_limit: value.gas.unwrap_or_default(),
3940 value: value.value,
3941 data: value.input.clone(),
3942 max_priority_fee_per_gas: value.max_priority_fee_per_gas.unwrap_or_default(),
3943 max_fee_per_gas: value.max_fee_per_gas.unwrap_or(
3944 u64::try_from(value.gas_price).map_err(|_| {
3945 GenericTransactionError::InvalidField("gas_price overflows u64".to_owned())
3946 })?,
3947 ),
3948 access_list: value
3949 .access_list
3950 .into_iter()
3951 .map(|v| (v.address, v.storage_keys))
3952 .collect::<Vec<_>>(),
3953 fee_token: value
3954 .fee_token
3955 .ok_or(GenericTransactionError::MissingField(
3956 "fee token".to_owned(),
3957 ))?,
3958 chain_id: value.chain_id.unwrap_or_default(),
3959 ..Default::default()
3960 })
3961 }
3962 }
3963
3964 impl From<LegacyTransaction> for GenericTransaction {
3965 fn from(value: LegacyTransaction) -> Self {
3966 Self {
3967 r#type: TxType::Legacy,
3968 nonce: Some(value.nonce),
3969 to: value.to,
3970 from: Address::default(),
3971 gas: Some(value.gas),
3972 value: value.value,
3973 gas_price: value.gas_price,
3974 max_priority_fee_per_gas: None,
3975 max_fee_per_gas: None,
3976 max_fee_per_blob_gas: None,
3977 access_list: vec![],
3978 fee_token: None,
3979 authorization_list: None,
3980 blob_versioned_hashes: vec![],
3981 blobs: vec![],
3982 wrapper_version: None,
3983 chain_id: None,
3984 input: value.data,
3985 }
3986 }
3987 }
3988
3989 impl From<EIP2930Transaction> for GenericTransaction {
3990 fn from(value: EIP2930Transaction) -> Self {
3991 Self {
3992 r#type: TxType::EIP2930,
3993 nonce: Some(value.nonce),
3994 to: value.to,
3995 from: Address::default(),
3996 gas: Some(value.gas_limit),
3997 value: value.value,
3998 gas_price: value.gas_price,
3999 max_priority_fee_per_gas: None,
4000 max_fee_per_gas: None,
4001 max_fee_per_blob_gas: None,
4002 access_list: value
4003 .access_list
4004 .into_iter()
4005 .map(|(address, storage_keys)| AccessListEntry {
4006 address,
4007 storage_keys,
4008 })
4009 .collect(),
4010 fee_token: None,
4011 authorization_list: None,
4012 blob_versioned_hashes: vec![],
4013 blobs: vec![],
4014 wrapper_version: None,
4015 chain_id: Some(value.chain_id),
4016 input: value.data,
4017 }
4018 }
4019 }
4020
4021 impl From<Transaction> for GenericTransaction {
4022 fn from(value: Transaction) -> Self {
4023 match value {
4024 Transaction::LegacyTransaction(tx) => tx.into(),
4025 Transaction::EIP2930Transaction(tx) => tx.into(),
4026 Transaction::EIP1559Transaction(tx) => tx.into(),
4027 Transaction::EIP4844Transaction(tx) => tx.into(),
4028 Transaction::EIP7702Transaction(tx) => tx.into(),
4029 Transaction::PrivilegedL2Transaction(tx) => tx.into(),
4030 Transaction::FeeTokenTransaction(tx) => tx.into(),
4031 Transaction::FrameTransaction(tx) => tx.into(),
4032 }
4033 }
4034 }
4035
4036 impl From<FrameTransaction> for GenericTransaction {
4037 fn from(value: FrameTransaction) -> Self {
4038 Self {
4039 r#type: TxType::Frame,
4040 nonce: Some(value.nonce),
4041 to: TxKind::Call(value.sender),
4042 from: value.sender,
4043 gas: Some(value.total_gas_limit()),
4044 value: U256::zero(),
4045 gas_price: value.max_fee_per_gas.into(),
4046 max_priority_fee_per_gas: Some(value.max_priority_fee_per_gas),
4047 max_fee_per_gas: Some(value.max_fee_per_gas),
4048 max_fee_per_blob_gas: if value.blob_versioned_hashes.is_empty() {
4049 None
4050 } else {
4051 Some(value.max_fee_per_blob_gas)
4052 },
4053 access_list: vec![],
4054 fee_token: None,
4055 authorization_list: None,
4056 blob_versioned_hashes: value.blob_versioned_hashes,
4057 blobs: vec![],
4058 wrapper_version: None,
4059 chain_id: Some(value.chain_id),
4060 input: Bytes::new(),
4061 }
4062 }
4063 }
4064}
4065
4066mod mempool {
4067 use super::*;
4068 use std::{
4069 cmp::Ordering,
4070 sync::Arc,
4071 time::{SystemTime, UNIX_EPOCH},
4072 };
4073
4074 #[derive(Debug, Clone, PartialEq, Eq)]
4075 pub struct MempoolTransaction {
4076 timestamp: u128,
4078 sender: Address,
4079 inner: Arc<Transaction>,
4080 }
4081
4082 impl MempoolTransaction {
4083 pub fn new(tx: Transaction, sender: Address) -> Self {
4084 Self {
4085 timestamp: SystemTime::now()
4086 .duration_since(UNIX_EPOCH)
4087 .expect("Invalid system time")
4088 .as_micros(),
4089 sender,
4090 inner: Arc::new(tx),
4091 }
4092 }
4093 pub fn time(&self) -> u128 {
4094 self.timestamp
4095 }
4096
4097 pub fn sender(&self) -> Address {
4098 self.sender
4099 }
4100
4101 pub fn transaction(&self) -> &Transaction {
4102 &self.inner
4103 }
4104 }
4105
4106 impl RLPEncode for MempoolTransaction {
4107 fn encode(&self, buf: &mut dyn bytes::BufMut) {
4108 Encoder::new(buf)
4109 .encode_field(&self.timestamp)
4110 .encode_field(&*self.inner)
4111 .finish();
4112 }
4113 }
4114
4115 impl RLPDecode for MempoolTransaction {
4116 fn decode_unfinished(rlp: &[u8]) -> Result<(Self, &[u8]), RLPDecodeError> {
4117 let decoder = Decoder::new(rlp)?;
4118 let (timestamp, decoder) = decoder.decode_field("timestamp")?;
4119 let (sender, decoder) = decoder.decode_field("sender")?;
4120 let (inner, decoder) = decoder.decode_field("inner")?;
4121 Ok((
4122 Self {
4123 timestamp,
4124 sender,
4125 inner: Arc::new(inner),
4126 },
4127 decoder.finish()?,
4128 ))
4129 }
4130 }
4131
4132 impl std::ops::Deref for MempoolTransaction {
4133 type Target = Transaction;
4134
4135 fn deref(&self) -> &Self::Target {
4136 &self.inner
4137 }
4138 }
4139
4140 impl Ord for MempoolTransaction {
4142 fn cmp(&self, other: &Self) -> Ordering {
4143 match (self.tx_type(), other.tx_type()) {
4144 (TxType::Privileged, TxType::Privileged) => {
4145 return self.nonce().cmp(&other.nonce());
4146 }
4147 (TxType::Privileged, _) => return Ordering::Less,
4148 (_, TxType::Privileged) => return Ordering::Greater,
4149 _ => (),
4150 };
4151 match self.nonce().cmp(&other.nonce()) {
4152 Ordering::Equal => other.time().cmp(&self.time()),
4153 ordering => ordering,
4154 }
4155 }
4156 }
4157
4158 impl PartialOrd for MempoolTransaction {
4159 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
4160 Some(self.cmp(other))
4161 }
4162 }
4163}
4164
4165#[cfg(test)]
4166mod tests {
4167
4168 use super::*;
4169 use crate::types::{
4170 AuthorizationTuple, BlockBody, Receipt, compute_receipts_root, compute_transactions_root,
4171 };
4172 use ethereum_types::H160;
4173 use hex_literal::hex;
4174 use serde_impl::{AccessListEntry, GenericTransaction};
4175 use std::str::FromStr;
4176
4177 #[test]
4178 fn nonce_over_u64_max_maps_to_nonce_is_max() {
4179 let rlp = hex!("ca89010000000000000000");
4182 let decoder = Decoder::new(&rlp).unwrap();
4183 let err = decode_nonce_field(decoder).unwrap_err();
4184 assert!(
4185 err.to_string().contains("Nonce is max"),
4186 "unexpected error: {err}"
4187 );
4188 }
4189
4190 #[test]
4191 fn nonce_at_u64_max_decodes() {
4192 let rlp = hex!("c988ffffffffffffffff");
4194 let decoder = Decoder::new(&rlp).unwrap();
4195 let (nonce, _) = decode_nonce_field(decoder).unwrap();
4196 assert_eq!(nonce, u64::MAX);
4197 }
4198
4199 #[test]
4200 fn legacy_chain_id_handles_out_of_range_v_without_underflow() {
4201 let legacy_chain_id = |v: u64| {
4207 Transaction::LegacyTransaction(LegacyTransaction {
4208 v: U256::from(v),
4209 ..Default::default()
4210 })
4211 .chain_id()
4212 };
4213 for v in [0u64, 1, 26, 27, 28, 34] {
4214 assert_eq!(legacy_chain_id(v), None, "v={v} must yield no chain id");
4215 }
4216 assert_eq!(legacy_chain_id(35), Some(0));
4218 assert_eq!(legacy_chain_id(36), Some(0));
4219 assert_eq!(legacy_chain_id(37), Some(1));
4220 }
4221
4222 #[test]
4223 fn test_compute_transactions_root() {
4224 let mut body = BlockBody::empty();
4225 let tx = LegacyTransaction {
4226 nonce: 0,
4227 gas_price: U256::from(0x0a),
4228 gas: 0x05f5e100,
4229 to: TxKind::Call(hex!("1000000000000000000000000000000000000000").into()),
4230 value: 0.into(),
4231 data: Default::default(),
4232 v: U256::from(0x1b),
4233 r: U256::from_big_endian(&hex!(
4234 "7e09e26678ed4fac08a249ebe8ed680bf9051a5e14ad223e4b2b9d26e0208f37"
4235 )),
4236 s: U256::from_big_endian(&hex!(
4237 "5f6e3f188e3e6eab7d7d3b6568f5eac7d687b08d307d3154ccd8c87b4630509b"
4238 )),
4239 ..Default::default()
4240 };
4241 body.transactions.push(Transaction::LegacyTransaction(tx));
4242 let expected_root =
4243 hex!("8151d548273f6683169524b66ca9fe338b9ce42bc3540046c828fd939ae23bcb");
4244 let result = compute_transactions_root(&body.transactions, ðrex_crypto::NativeCrypto);
4245
4246 assert_eq!(result, expected_root.into());
4247 }
4248 #[test]
4249 fn test_compute_hash() {
4250 let tx_eip2930 = EIP2930Transaction {
4252 chain_id: 3503995874084926u64,
4253 nonce: 7,
4254 gas_price: U256::from(0x2dbf1f9a_u64),
4255 gas_limit: 0x186A0,
4256 to: TxKind::Call(hex!("7dcd17433742f4c0ca53122ab541d0ba67fc27df").into()),
4257 value: 2.into(),
4258 data: Bytes::from(&b"\xdbS\x06$\x8e\x03\x13\xe7emit"[..]),
4259 access_list: vec![(
4260 hex!("7dcd17433742f4c0ca53122ab541d0ba67fc27df").into(),
4261 vec![
4262 hex!("0000000000000000000000000000000000000000000000000000000000000000").into(),
4263 hex!("a3d07a7d68fbd49ec2f8e6befdd86c885f86c272819f6f345f365dec35ae6707").into(),
4264 ],
4265 )],
4266 signature_y_parity: false,
4267 signature_r: U256::from_dec_str(
4268 "75813812796588349127366022588733264074091236448495248199152066031778895768879",
4269 )
4270 .unwrap(),
4271 signature_s: U256::from_dec_str(
4272 "25476208226281085290728123165613764315157904411823916642262684106502155457829",
4273 )
4274 .unwrap(),
4275 ..Default::default()
4276 };
4277 let tx = Transaction::EIP2930Transaction(tx_eip2930);
4278
4279 let expected_hash =
4280 hex!("a0762610d794acddd2dca15fb7c437ada3611c886f3bea675d53d8da8a6c41b2");
4281 let hash = tx.compute_hash(&NativeCrypto);
4282 assert_eq!(hash, expected_hash.into());
4283 }
4284
4285 #[test]
4286 fn test_compute_receipts_root() {
4287 let tx_type = TxType::Legacy;
4290 let succeeded = true;
4291 let cumulative_gas_used = 0x5208;
4292 let logs = vec![];
4293 let receipt = Receipt::new(tx_type, succeeded, cumulative_gas_used, logs);
4294
4295 let result = compute_receipts_root(&[receipt], ðrex_crypto::NativeCrypto);
4296 let expected_root =
4297 hex!("056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2");
4298 assert_eq!(result, expected_root.into());
4299 }
4300
4301 #[test]
4302 fn legacy_tx_rlp_decode() {
4303 let encoded_tx = "f86d80843baa0c4082f618946177843db3138ae69679a54b95cf345ed759450d870aa87bee538000808360306ba0151ccc02146b9b11adf516e6787b59acae3e76544fdcd75e77e67c6b598ce65da064c5dd5aae2fbb535830ebbdad0234975cd7ece3562013b63ea18cc0df6c97d4";
4304 let encoded_tx_bytes = hex::decode(encoded_tx).unwrap();
4305 let tx = LegacyTransaction::decode(&encoded_tx_bytes).unwrap();
4306 let expected_tx = LegacyTransaction {
4307 nonce: 0,
4308 gas_price: U256::from(1001000000u64),
4309 gas: 63000,
4310 to: TxKind::Call(Address::from_slice(
4311 &hex::decode("6177843db3138ae69679A54b95cf345ED759450d").unwrap(),
4312 )),
4313 value: 3000000000000000_u64.into(),
4314 data: Bytes::new(),
4315 r: U256::from_str_radix(
4316 "151ccc02146b9b11adf516e6787b59acae3e76544fdcd75e77e67c6b598ce65d",
4317 16,
4318 )
4319 .unwrap(),
4320 s: U256::from_str_radix(
4321 "64c5dd5aae2fbb535830ebbdad0234975cd7ece3562013b63ea18cc0df6c97d4",
4322 16,
4323 )
4324 .unwrap(),
4325 v: 6303851.into(),
4326 ..Default::default()
4327 };
4328 assert_eq!(tx, expected_tx);
4329 }
4330
4331 #[test]
4332 fn eip1559_tx_rlp_decode() {
4333 let encoded_tx = "f86c8330182480114e82f618946177843db3138ae69679a54b95cf345ed759450d870aa87bee53800080c080a0151ccc02146b9b11adf516e6787b59acae3e76544fdcd75e77e67c6b598ce65da064c5dd5aae2fbb535830ebbdad0234975cd7ece3562013b63ea18cc0df6c97d4";
4334 let encoded_tx_bytes = hex::decode(encoded_tx).unwrap();
4335 let tx = EIP1559Transaction::decode(&encoded_tx_bytes).unwrap();
4336 let expected_tx = EIP1559Transaction {
4337 nonce: 0,
4338 max_fee_per_gas: 78,
4339 max_priority_fee_per_gas: 17,
4340 to: TxKind::Call(Address::from_slice(
4341 &hex::decode("6177843db3138ae69679A54b95cf345ED759450d").unwrap(),
4342 )),
4343 value: 3000000000000000_u64.into(),
4344 data: Bytes::new(),
4345 signature_r: U256::from_str_radix(
4346 "151ccc02146b9b11adf516e6787b59acae3e76544fdcd75e77e67c6b598ce65d",
4347 16,
4348 )
4349 .unwrap(),
4350 signature_s: U256::from_str_radix(
4351 "64c5dd5aae2fbb535830ebbdad0234975cd7ece3562013b63ea18cc0df6c97d4",
4352 16,
4353 )
4354 .unwrap(),
4355 signature_y_parity: false,
4356 chain_id: 3151908,
4357 gas_limit: 63000,
4358 access_list: vec![],
4359 ..Default::default()
4360 };
4361 assert_eq!(tx, expected_tx);
4362 }
4363
4364 #[test]
4365 fn deserialize_tx_kind() {
4366 let tx_kind_create = r#""""#;
4367 let tx_kind_call = r#""0x6177843db3138ae69679A54b95cf345ED759450d""#;
4368 let deserialized_tx_kind_create = TxKind::Create;
4369 let deserialized_tx_kind_call = TxKind::Call(Address::from_slice(
4370 &hex::decode("6177843db3138ae69679A54b95cf345ED759450d").unwrap(),
4371 ));
4372 assert_eq!(
4373 deserialized_tx_kind_create,
4374 serde_json::from_str(tx_kind_create).unwrap()
4375 );
4376 assert_eq!(
4377 deserialized_tx_kind_call,
4378 serde_json::from_str(tx_kind_call).unwrap()
4379 )
4380 }
4381
4382 #[test]
4383 fn deserialize_tx_type() {
4384 let tx_type_eip2930 = r#""0x01""#;
4385 let tx_type_eip1559 = r#""0x02""#;
4386 let deserialized_tx_type_eip2930 = TxType::EIP2930;
4387 let deserialized_tx_type_eip1559 = TxType::EIP1559;
4388 assert_eq!(
4389 deserialized_tx_type_eip2930,
4390 serde_json::from_str(tx_type_eip2930).unwrap()
4391 );
4392 assert_eq!(
4393 deserialized_tx_type_eip1559,
4394 serde_json::from_str(tx_type_eip1559).unwrap()
4395 )
4396 }
4397
4398 #[test]
4399 fn deserialize_generic_transaction() {
4400 let generic_transaction = r#"{
4401 "type":"0x01",
4402 "nonce":"0x02",
4403 "to":"",
4404 "from":"0x6177843db3138ae69679A54b95cf345ED759450d",
4405 "gas":"0x5208",
4406 "value":"0x01",
4407 "input":"0x010203040506",
4408 "gasPrice":"0x07",
4409 "accessList": [
4410 {
4411 "address": "0x000f3df6d732807ef1319fb7b8bb8522d0beac02",
4412 "storageKeys": [
4413 "0x000000000000000000000000000000000000000000000000000000000000000c",
4414 "0x000000000000000000000000000000000000000000000000000000000000200b"
4415 ]
4416 }
4417 ]
4418 }"#;
4419 let deserialized_generic_transaction = GenericTransaction {
4420 r#type: TxType::EIP2930,
4421 nonce: Some(2),
4422 to: TxKind::Create,
4423 from: Address::from_slice(
4424 &hex::decode("6177843db3138ae69679A54b95cf345ED759450d").unwrap(),
4425 ),
4426 gas: Some(0x5208),
4427 value: U256::from(1),
4428 input: Bytes::from(hex::decode("010203040506").unwrap()),
4429 gas_price: U256::from(7),
4430 max_priority_fee_per_gas: Default::default(),
4431 max_fee_per_gas: Default::default(),
4432 max_fee_per_blob_gas: Default::default(),
4433 access_list: vec![AccessListEntry {
4434 address: Address::from_slice(
4435 &hex::decode("000f3df6d732807ef1319fb7b8bb8522d0beac02").unwrap(),
4436 ),
4437 storage_keys: vec![H256::from_low_u64_be(12), H256::from_low_u64_be(8203)],
4438 }],
4439 fee_token: None,
4440 blob_versioned_hashes: Default::default(),
4441 blobs: Default::default(),
4442 wrapper_version: None,
4443 chain_id: Default::default(),
4444 authorization_list: None,
4445 };
4446 assert_eq!(
4447 deserialized_generic_transaction,
4448 serde_json::from_str(generic_transaction).unwrap()
4449 )
4450 }
4451
4452 #[test]
4453 fn deserialize_generic_transaction_with_data_and_input_fields() {
4454 let generic_transaction = r#"{
4455 "type":"0x01",
4456 "nonce":"0x02",
4457 "to":"",
4458 "from":"0x6177843db3138ae69679A54b95cf345ED759450d",
4459 "gas":"0x5208",
4460 "value":"0x01",
4461 "input":"0x010203040506",
4462 "data":"0x010203040506",
4463 "gasPrice":"0x07",
4464 "accessList": [
4465 {
4466 "address": "0x000f3df6d732807ef1319fb7b8bb8522d0beac02",
4467 "storageKeys": [
4468 "0x000000000000000000000000000000000000000000000000000000000000000c",
4469 "0x000000000000000000000000000000000000000000000000000000000000200b"
4470 ]
4471 }
4472 ]
4473 }"#;
4474 let deserialized_generic_transaction = GenericTransaction {
4475 r#type: TxType::EIP2930,
4476 nonce: Some(2),
4477 to: TxKind::Create,
4478 from: Address::from_slice(
4479 &hex::decode("6177843db3138ae69679A54b95cf345ED759450d").unwrap(),
4480 ),
4481 gas: Some(0x5208),
4482 value: U256::from(1),
4483 input: Bytes::from(hex::decode("010203040506").unwrap()),
4484 gas_price: U256::from(7),
4485 max_priority_fee_per_gas: Default::default(),
4486 max_fee_per_gas: Default::default(),
4487 max_fee_per_blob_gas: Default::default(),
4488 access_list: vec![AccessListEntry {
4489 address: Address::from_slice(
4490 &hex::decode("000f3df6d732807ef1319fb7b8bb8522d0beac02").unwrap(),
4491 ),
4492 storage_keys: vec![H256::from_low_u64_be(12), H256::from_low_u64_be(8203)],
4493 }],
4494 fee_token: None,
4495 blob_versioned_hashes: Default::default(),
4496 blobs: Default::default(),
4497 wrapper_version: None,
4498 chain_id: Default::default(),
4499 authorization_list: None,
4500 };
4501 assert_eq!(
4502 deserialized_generic_transaction,
4503 serde_json::from_str(generic_transaction).unwrap()
4504 )
4505 }
4506
4507 #[test]
4508 fn deserialize_eip4844_transaction() {
4509 let eip4844_transaction = r#"{
4510 "chainId":"0x01",
4511 "nonce":"0x02",
4512 "maxPriorityFeePerGas":"0x01",
4513 "maxFeePerGas":"0x01",
4514 "gas":"0x5208",
4515 "to":"0x6177843db3138ae69679A54b95cf345ED759450d",
4516 "value":"0x01",
4517 "input":"0x3033",
4518 "accessList": [
4519 {
4520 "address": "0x000f3df6d732807ef1319fb7b8bb8522d0beac02",
4521 "storageKeys": [
4522 "0x000000000000000000000000000000000000000000000000000000000000000c",
4523 "0x000000000000000000000000000000000000000000000000000000000000200b"
4524 ]
4525 }
4526 ],
4527 "maxFeePerBlobGas":"0x03",
4528 "blobVersionedHashes": [
4529 "0x0000000000000000000000000000000000000000000000000000000000000001",
4530 "0x0000000000000000000000000000000000000000000000000000000000000002"
4531 ],
4532 "yParity":"0x0",
4533 "r": "0x01",
4534 "s": "0x02"
4535 }"#;
4536 let deserialized_eip4844_transaction = EIP4844Transaction {
4537 chain_id: 0x01,
4538 nonce: 0x02,
4539 to: Address::from_slice(
4540 &hex::decode("6177843db3138ae69679A54b95cf345ED759450d").unwrap(),
4541 ),
4542 max_priority_fee_per_gas: 1,
4543 max_fee_per_gas: 1,
4544 max_fee_per_blob_gas: U256::from(0x03),
4545 gas: 0x5208,
4546 value: U256::from(0x01),
4547 data: Bytes::from_static(b"03"),
4549 access_list: vec![(
4550 Address::from_slice(
4551 &hex::decode("000f3df6d732807ef1319fb7b8bb8522d0beac02").unwrap(),
4552 ),
4553 vec![H256::from_low_u64_be(12), H256::from_low_u64_be(8203)],
4554 )],
4555 blob_versioned_hashes: vec![H256::from_low_u64_be(1), H256::from_low_u64_be(2)],
4556 signature_y_parity: false,
4557 signature_r: U256::from(0x01),
4558 signature_s: U256::from(0x02),
4559 ..Default::default()
4560 };
4561
4562 assert_eq!(
4563 deserialized_eip4844_transaction,
4564 serde_json::from_str(eip4844_transaction).unwrap()
4565 )
4566 }
4567
4568 #[test]
4569 fn serialize_deserialize_transaction() {
4570 let eip1559 = EIP1559Transaction {
4571 chain_id: 65536999,
4572 nonce: 1,
4573 max_priority_fee_per_gas: 1000,
4574 max_fee_per_gas: 2000,
4575 gas_limit: 21000,
4576 to: TxKind::Call(H160::from_str("0x000a52D537c4150ec274dcE3962a0d179B7E71B0").unwrap()),
4577 value: U256::from(100000),
4578 data: Bytes::from_static(b"03"),
4579 access_list: vec![(
4580 H160::from_str("0x000a52D537c4150ec274dcE3962a0d179B7E71B3").unwrap(),
4581 vec![H256::zero()],
4582 )],
4583 signature_y_parity: true,
4584 signature_r: U256::one(),
4585 signature_s: U256::zero(),
4586 ..Default::default()
4587 };
4588 let tx_to_serialize = Transaction::EIP1559Transaction(eip1559.clone());
4589 let serialized = serde_json::to_string(&tx_to_serialize).expect("Failed to serialize");
4590
4591 let deserialized_tx: Transaction =
4592 serde_json::from_str(&serialized).expect("Failed to deserialize");
4593
4594 assert!(deserialized_tx.tx_type() == TxType::EIP1559);
4595
4596 if let Transaction::EIP1559Transaction(tx) = deserialized_tx {
4597 assert_eq!(tx, eip1559);
4598 }
4599 }
4600
4601 #[test]
4602 fn serialize_deserialize_eip7702transaction() {
4603 let eip7702 = EIP7702Transaction {
4604 chain_id: 65536999,
4605 nonce: 1,
4606 max_priority_fee_per_gas: 1000,
4607 max_fee_per_gas: 2000,
4608 gas_limit: 21000,
4609 to: Address::from_str("0x000a52D537c4150ec274dcE3962a0d179B7E71B0").unwrap(),
4610 value: U256::from(100000),
4611 data: Bytes::from_static(b"03"),
4612 access_list: vec![],
4613 signature_y_parity: true,
4614 signature_r: U256::one(),
4615 signature_s: U256::zero(),
4616 authorization_list: vec![AuthorizationTuple {
4617 chain_id: U256::from(65536999),
4618 address: H160::from_str("0x000a52D537c4150ec274dcE3962a0d179B7E71B1").unwrap(),
4619 nonce: 2,
4620 y_parity: U256::one(),
4621 r_signature: U256::from(22),
4622 s_signature: U256::from(37),
4623 }],
4624 ..Default::default()
4625 };
4626 let tx_to_serialize = Transaction::EIP7702Transaction(eip7702.clone());
4627 let serialized = serde_json::to_string(&tx_to_serialize).expect("Failed to serialize");
4628
4629 let deserialized_tx: Transaction =
4630 serde_json::from_str(&serialized).expect("Failed to deserialize");
4631
4632 assert!(deserialized_tx.tx_type() == TxType::EIP7702);
4633
4634 if let Transaction::EIP7702Transaction(tx) = deserialized_tx {
4635 assert_eq!(tx, eip7702);
4636 }
4637 }
4638
4639 #[test]
4640 fn serialize_deserialize_privileged_l2_transaction() -> Result<(), RLPDecodeError> {
4641 let privileged_l2 = PrivilegedL2Transaction {
4642 chain_id: 65536999,
4643 nonce: 0,
4644 max_priority_fee_per_gas: 875000000,
4645 max_fee_per_gas: 875000000,
4646 gas_limit: 42000u64,
4647 to: TxKind::Call(
4648 Address::from_str("0x8943545177806ed17b9f23f0a21ee5948ecaa776").unwrap(),
4649 ),
4650 value: U256::from(500000000000000000000000000u128),
4651 data: Bytes::new(),
4652 access_list: vec![],
4653 from: Address::from_str("0x8943545177806ed17b9f23f0a21ee5948ecaa776").unwrap(),
4654 ..Default::default()
4655 };
4656
4657 let encoded = PrivilegedL2Transaction::encode_to_vec(&privileged_l2);
4658 println!("encoded length: {}", encoded.len());
4659 assert_eq!(encoded.len(), privileged_l2.length());
4660
4661 let deserialized_tx = PrivilegedL2Transaction::decode(&encoded)?;
4662
4663 assert_eq!(deserialized_tx, privileged_l2);
4664
4665 Ok(())
4666 }
4667
4668 #[test]
4669 fn test_legacy_transaction_into_generic() {
4670 let legacy_tx = LegacyTransaction {
4671 nonce: 1,
4672 gas_price: U256::from(20_000_000_000u64),
4673 gas: 21000,
4674 to: TxKind::Call(
4675 Address::from_str("0x742d35Cc6634C0532925a3b844Bc454e4438f44e").unwrap(),
4676 ),
4677 value: U256::from(1_000_000_000_000_000_000u64),
4678 data: Bytes::default(),
4679 v: U256::from(27),
4680 r: U256::from(1),
4681 s: U256::from(1),
4682 ..Default::default()
4683 };
4684
4685 let generic_tx: GenericTransaction = legacy_tx.into();
4686 assert_eq!(generic_tx.r#type, TxType::Legacy);
4687 assert_eq!(generic_tx.nonce, Some(1));
4688 assert_eq!(generic_tx.gas_price, U256::from(20_000_000_000u64));
4689 assert_eq!(generic_tx.gas, Some(21000));
4690 assert_eq!(generic_tx.max_priority_fee_per_gas, None);
4691 assert_eq!(generic_tx.max_fee_per_gas, None);
4692 assert_eq!(generic_tx.access_list.len(), 0);
4693 assert_eq!(generic_tx.chain_id, None);
4694 }
4695
4696 #[test]
4697 fn test_eip2930_transaction_into_generic() {
4698 let access_list = vec![(
4699 Address::from_str("0x742d35Cc6634C0532925a3b844Bc454e4438f44e").unwrap(),
4700 vec![
4701 H256::from_str(
4702 "0x1234567890123456789012345678901234567890123456789012345678901234",
4703 )
4704 .unwrap(),
4705 ],
4706 )];
4707
4708 let eip2930_tx = EIP2930Transaction {
4709 chain_id: 1,
4710 nonce: 1,
4711 gas_price: U256::from(20_000_000_000u64),
4712 gas_limit: 21000,
4713 to: TxKind::Call(
4714 Address::from_str("0x742d35Cc6634C0532925a3b844Bc454e4438f44e").unwrap(),
4715 ),
4716 value: U256::from(1_000_000_000_000_000_000u64),
4717 data: Bytes::default(),
4718 access_list: access_list.clone(),
4719 signature_y_parity: false,
4720 signature_r: U256::from(1),
4721 signature_s: U256::from(1),
4722 ..Default::default()
4723 };
4724
4725 let generic_tx: GenericTransaction = eip2930_tx.into();
4726 assert_eq!(generic_tx.r#type, TxType::EIP2930);
4727 assert_eq!(generic_tx.nonce, Some(1));
4728 assert_eq!(generic_tx.gas_price, U256::from(20_000_000_000u64));
4729 assert_eq!(generic_tx.gas, Some(21000));
4730 assert_eq!(generic_tx.max_priority_fee_per_gas, None);
4731 assert_eq!(generic_tx.max_fee_per_gas, None);
4732 assert_eq!(generic_tx.chain_id, Some(1));
4733 assert_eq!(generic_tx.access_list.len(), 1);
4734 assert_eq!(generic_tx.access_list[0].address, access_list[0].0);
4735 assert_eq!(generic_tx.access_list[0].storage_keys, access_list[0].1);
4736 }
4737
4738 #[test]
4739 fn recover_address_rejects_high_s_signatures() {
4740 use ethrex_crypto::NativeCrypto;
4741 use k256::ecdsa::SigningKey;
4742
4743 let crypto = NativeCrypto;
4744
4745 let private_key = hex!("4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318");
4748 let signing_key = SigningKey::from_bytes(&private_key.into()).expect("Valid private key");
4749
4750 let msg = b"Test message for high-s signature rejection";
4752 let payload = keccak(msg).to_fixed_bytes();
4754
4755 let (signature, recovery_id) = signing_key
4758 .sign_prehash_recoverable(&payload)
4759 .expect("Signing failed");
4760
4761 let mut sig_bytes = [0u8; 65];
4763 sig_bytes[..64].copy_from_slice(&signature.to_bytes());
4764 sig_bytes[64] = recovery_id.to_byte();
4765
4766 let uncompressed_pub = signing_key.verifying_key().to_encoded_point(false);
4769 let pub_hash = ethrex_crypto::keccak::keccak_hash(&uncompressed_pub.as_bytes()[1..]);
4770 let expected_address = Address::from_slice(&pub_hash[12..]);
4771
4772 let recovered = crypto
4773 .recover_signer(&sig_bytes, &payload)
4774 .expect("Valid low-s signature should recover successfully");
4775 assert_eq!(recovered, expected_address, "Recovered address mismatch");
4776
4777 let n = U256::from_big_endian(&hex!(
4780 "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141"
4781 ));
4782 let s = U256::from_big_endian(&sig_bytes[32..64]);
4783
4784 let half_n = n / 2;
4786 assert!(
4787 s <= half_n,
4788 "Generated signature was not low-s, cannot test high-s rejection"
4789 );
4790
4791 let s_high = n - s;
4793
4794 let mut sig_high_bytes = sig_bytes;
4795 sig_high_bytes[32..64].copy_from_slice(&s_high.to_big_endian());
4797 sig_high_bytes[64] ^= 1;
4799
4800 assert!(
4803 crypto.recover_signer(&sig_high_bytes, &payload).is_err(),
4804 "High-s signature should be rejected (EIP-2 compliance)"
4805 );
4806 }
4807
4808 #[test]
4809 fn encode_decode_low_size_tx() {
4810 let tx = Transaction::EIP2930Transaction(EIP2930Transaction::default());
4811 let tx_to_encode = tx.clone();
4814 let encoded = tx_to_encode.encode_to_vec();
4815 let decoded_tx = Transaction::decode(&encoded).unwrap();
4816 assert_eq!(tx, decoded_tx);
4817 }
4818
4819 #[test]
4820 fn test_eip1559_simple_transfer_size() {
4821 let tx = Transaction::EIP1559Transaction(EIP1559Transaction::default());
4822 assert_eq!(tx.encode_to_vec().len(), EIP1559_DEFAULT_SERIALIZED_LENGTH);
4823 }
4824
4825 fn make_test_frame_tx() -> FrameTransaction {
4828 FrameTransaction {
4829 chain_id: 1,
4830 nonce: 42,
4831 sender: Address::from_low_u64_be(0xABCD),
4832 frames: vec![
4833 Frame {
4834 mode: FrameMode::Verify as u8,
4835 flags: 0x03, target: Some(Address::from_low_u64_be(0xABCD)),
4837 gas_limit: 100_000,
4838 value: U256::zero(),
4839 data: Bytes::from_static(b"verify_data"),
4840 },
4841 Frame {
4842 mode: FrameMode::Sender as u8,
4843 flags: 0x00,
4844 target: Some(Address::from_low_u64_be(0x1234)),
4845 gas_limit: 200_000,
4846 value: U256::zero(),
4847 data: Bytes::from_static(b"call_data"),
4848 },
4849 ],
4850 signatures: vec![FrameSignature {
4851 scheme: FRAME_SIG_SCHEME_SECP256K1,
4852 signer: Address::from_low_u64_be(0xABCD),
4853 msg: Bytes::new(),
4854 signature: Bytes::from(vec![0u8; 65]),
4855 }],
4856 max_priority_fee_per_gas: 1_000_000_000,
4857 max_fee_per_gas: 30_000_000_000,
4858 max_fee_per_blob_gas: U256::zero(),
4859 blob_versioned_hashes: vec![],
4860 inner_hash: OnceCell::new(),
4861 cached_canonical: OnceCell::new(),
4862 }
4863 }
4864
4865 #[test]
4866 fn atomic_batch_flag_valid_on_default_and_verify_frames() {
4867 let mut tx = make_test_frame_tx();
4869 tx.frames = vec![
4870 Frame {
4871 mode: FrameMode::Default as u8,
4872 flags: 0x04, target: Some(Address::from_low_u64_be(0xB0B)),
4874 gas_limit: 21_000,
4875 value: U256::zero(),
4876 data: Bytes::new(),
4877 },
4878 Frame {
4879 mode: FrameMode::Verify as u8,
4880 flags: 0x04 | 0x03, target: None,
4882 gas_limit: 21_000,
4883 value: U256::zero(),
4884 data: Bytes::new(),
4885 },
4886 Frame {
4887 mode: FrameMode::Sender as u8,
4888 flags: 0x00, target: Some(Address::from_low_u64_be(0xCAFE)),
4890 gas_limit: 21_000,
4891 value: U256::zero(),
4892 data: Bytes::new(),
4893 },
4894 ];
4895 assert!(tx.validate_static_constraints().is_ok());
4896 }
4897
4898 #[test]
4899 fn atomic_batch_flag_on_last_frame_still_invalid() {
4900 let mut tx = make_test_frame_tx();
4901 tx.frames = vec![Frame {
4902 mode: FrameMode::Sender as u8,
4903 flags: 0x04,
4904 target: Some(Address::from_low_u64_be(0xCAFE)),
4905 gas_limit: 21_000,
4906 value: U256::zero(),
4907 data: Bytes::new(),
4908 }];
4909 let err = tx.validate_static_constraints().unwrap_err();
4910 assert!(err.contains("atomic batch flag on last frame"), "{err}");
4911 }
4912
4913 #[test]
4914 fn test_frame_rlp_roundtrip() {
4915 let frame = Frame {
4916 mode: FrameMode::Verify as u8,
4917 flags: 0x03,
4918 target: Some(Address::from_low_u64_be(0x1234)),
4919 gas_limit: 50_000,
4920 value: U256::zero(),
4921 data: Bytes::from_static(b"hello"),
4922 };
4923 let encoded = frame.encode_to_vec();
4924 let decoded = Frame::decode(&encoded).unwrap();
4925 assert_eq!(frame, decoded);
4926 }
4927
4928 #[test]
4929 fn test_frame_null_target_rlp_roundtrip() {
4930 let frame = Frame {
4931 mode: FrameMode::Default as u8,
4932 flags: 0x00,
4933 target: None,
4934 gas_limit: 10_000,
4935 value: U256::zero(),
4936 data: Bytes::from_static(b"deploy"),
4937 };
4938 let encoded = frame.encode_to_vec();
4939 let decoded = Frame::decode(&encoded).unwrap();
4940 assert_eq!(frame, decoded);
4941 }
4942
4943 #[test]
4944 fn test_frame_mode_with_flags_rlp_roundtrip() {
4945 for mode_val in [0u8, 1, 2] {
4947 let frame = Frame {
4948 mode: mode_val,
4949 flags: if mode_val == 1 { 0x03 } else { 0x00 },
4950 target: Some(Address::from_low_u64_be(0x1234)),
4951 gas_limit: 50_000,
4952 value: U256::zero(),
4953 data: Bytes::new(),
4954 };
4955 let encoded = frame.encode_to_vec();
4956 let decoded = Frame::decode(&encoded).unwrap();
4957 assert_eq!(frame, decoded);
4958 }
4959 let frame = Frame {
4961 mode: 2, flags: 0x01 | 0x04, target: Some(Address::from_low_u64_be(0x1234)),
4964 gas_limit: 50_000,
4965 value: U256::zero(),
4966 data: Bytes::new(),
4967 };
4968 assert_eq!(frame.execution_mode(), FrameMode::Sender);
4969 assert_eq!(frame.scope_restriction(), 1);
4970 assert!(frame.is_atomic_batch());
4971 let encoded = frame.encode_to_vec();
4972 let decoded = Frame::decode(&encoded).unwrap();
4973 assert_eq!(frame, decoded);
4974 }
4975
4976 #[test]
4977 fn frame_rlp_roundtrip_preserves_value() {
4978 let frame = Frame {
4979 mode: FrameMode::Sender as u8,
4980 flags: 0x00,
4981 target: Some(Address::from_low_u64_be(0xCAFE)),
4982 gas_limit: 100_000,
4983 value: U256::from(1_000_000_000_000_000u64), data: Bytes::from_static(b"hello"),
4985 };
4986 let encoded = frame.encode_to_vec();
4987 let decoded = Frame::decode(&encoded).unwrap();
4988 assert_eq!(frame, decoded);
4989 assert_eq!(decoded.value, U256::from(1_000_000_000_000_000u64));
4990 }
4991
4992 #[test]
4993 fn test_frame_transaction_rlp_roundtrip() {
4994 let tx = make_test_frame_tx();
4995 let encoded = tx.encode_to_vec();
4996 let decoded = FrameTransaction::decode(&encoded).unwrap();
4997 assert_eq!(tx, decoded);
4998 }
4999
5000 #[test]
5001 fn test_frame_transaction_canonical_roundtrip() {
5002 let tx = Transaction::FrameTransaction(make_test_frame_tx());
5003 let encoded = tx.encode_canonical_to_vec();
5004 let decoded = Transaction::decode_canonical(&encoded).unwrap();
5005 assert_eq!(
5007 tx.hash(ðrex_crypto::NativeCrypto),
5008 decoded.hash(ðrex_crypto::NativeCrypto)
5009 );
5010 assert_eq!(tx.tx_type(), decoded.tx_type());
5012 assert_eq!(tx.nonce(), decoded.nonce());
5013 assert_eq!(tx.chain_id(), decoded.chain_id());
5014 }
5015
5016 #[test]
5017 fn test_frame_transaction_sig_hash_covers_all_frame_data() {
5018 let tx = make_test_frame_tx();
5020 let hash1 = tx.compute_sig_hash();
5021
5022 let mut tx2 = tx.clone();
5024 tx2.frames[0].data = Bytes::from_static(b"completely_different_verify_data");
5025 let hash2 = tx2.compute_sig_hash();
5026 assert_ne!(hash1, hash2);
5027
5028 let mut tx3 = tx;
5030 tx3.frames[1].data = Bytes::from_static(b"different_call_data");
5031 let hash3 = tx3.compute_sig_hash();
5032 assert_ne!(hash1, hash3);
5033 }
5034
5035 #[test]
5036 fn test_frame_transaction_accessor_methods() {
5037 let frame_tx = make_test_frame_tx();
5038 let tx = Transaction::FrameTransaction(frame_tx);
5039
5040 assert_eq!(tx.tx_type(), TxType::Frame);
5041 assert_eq!(tx.nonce(), 42);
5042 assert_eq!(tx.chain_id(), Some(1));
5043 assert_eq!(tx.to(), TxKind::Call(Address::from_low_u64_be(0xABCD)));
5044 assert_eq!(tx.value(), U256::zero());
5045 assert_eq!(tx.data(), &Bytes::new());
5046 assert!(tx.access_list().is_empty());
5047 assert!(tx.authorization_list().is_none());
5048 assert_eq!(tx.max_priority_fee(), Some(1_000_000_000));
5049 assert_eq!(tx.max_fee_per_gas(), Some(30_000_000_000));
5050 assert_eq!(tx.max_fee_per_blob_gas(), None); assert!(!tx.is_contract_creation());
5052 assert_eq!(
5054 tx.sender(ðrex_crypto::NativeCrypto).unwrap(),
5055 Address::from_low_u64_be(0xABCD)
5056 );
5057 assert!(tx.gas_limit() >= 300_000); }
5060
5061 #[test]
5062 fn test_frame_transaction_hash_uses_oncecell() {
5063 let tx = Transaction::FrameTransaction(make_test_frame_tx());
5064 let h1 = tx.hash(ðrex_crypto::NativeCrypto);
5065 let h2 = tx.hash(ðrex_crypto::NativeCrypto);
5066 assert_eq!(h1, h2);
5067 }
5068
5069 #[test]
5070 fn frame_transaction_rlp_roundtrip_preserves_fields() {
5071 let tx = make_test_frame_tx();
5072 let mut buf = Vec::new();
5073 tx.encode(&mut buf);
5074 let (decoded, rest) = FrameTransaction::decode_unfinished(&buf).unwrap();
5075 assert!(rest.is_empty());
5076 assert_eq!(decoded.chain_id, tx.chain_id);
5077 assert_eq!(decoded.nonce, tx.nonce);
5078 assert_eq!(decoded.sender, tx.sender);
5079 }
5080
5081 #[test]
5082 fn frame_transaction_variant_is_exposed_on_transaction_enum() {
5083 let tx = Transaction::FrameTransaction(make_test_frame_tx());
5084 assert!(matches!(tx, Transaction::FrameTransaction(_)));
5085 }
5086
5087 #[test]
5088 fn frame_transaction_serializes_signatures_as_array() {
5089 let tx = make_test_frame_tx();
5091 let json = serde_json::to_value(&tx).unwrap();
5092 let sigs = json
5093 .get("signatures")
5094 .expect("signatures field present")
5095 .as_array()
5096 .expect("signatures serialized as an array");
5097 assert_eq!(sigs.len(), tx.signatures.len());
5098 assert_eq!(sigs[0].get("scheme").unwrap(), "0x0");
5099 assert!(sigs[0].get("signer").is_some());
5100 assert!(sigs[0].get("signature").is_some());
5101 assert!(sigs[0].get("msg").is_some());
5102 }
5103
5104 fn make_frame_tx_with_gas_limits(limits: Vec<u64>) -> FrameTransaction {
5105 let frames = limits
5106 .into_iter()
5107 .map(|gl| Frame {
5108 mode: FrameMode::Sender as u8,
5109 flags: 0x00,
5110 target: Some(Address::from_low_u64_be(0x1234)),
5111 gas_limit: gl,
5112 value: U256::zero(),
5113 data: Bytes::new(),
5114 })
5115 .collect();
5116 FrameTransaction {
5117 chain_id: 1,
5118 nonce: 0,
5119 sender: Address::from_low_u64_be(0xABCD),
5120 frames,
5121 signatures: vec![],
5122 max_priority_fee_per_gas: 1_000_000_000,
5123 max_fee_per_gas: 30_000_000_000,
5124 max_fee_per_blob_gas: U256::zero(),
5125 blob_versioned_hashes: vec![],
5126 inner_hash: OnceCell::new(),
5127 cached_canonical: OnceCell::new(),
5128 }
5129 }
5130
5131 #[test]
5132 fn per_frame_gas_limit_above_i64_max_is_rejected() {
5133 let tx = make_frame_tx_with_gas_limits(vec![(i64::MAX as u64) + 1]);
5134 let err = tx.validate_static_constraints().unwrap_err();
5135 assert!(err.contains("exceeds 2**63-1"), "unexpected error: {err}");
5136 }
5137
5138 #[test]
5139 fn cumulative_frame_gas_limit_above_i64_max_is_rejected() {
5140 let half = (i64::MAX as u64) / 2 + 1;
5141 let tx = make_frame_tx_with_gas_limits(vec![half, half]);
5142 let err = tx.validate_static_constraints().unwrap_err();
5143 assert!(err.contains("cumulative"), "unexpected error: {err}");
5144 }
5145
5146 #[test]
5147 fn cumulative_frame_gas_limit_equal_to_i64_max_is_accepted() {
5148 let a = (i64::MAX as u64) / 2;
5149 let b = i64::MAX as u64 - a;
5150 let tx = make_frame_tx_with_gas_limits(vec![a, b]);
5151 tx.validate_static_constraints()
5152 .expect("exact i64::MAX total should be accepted");
5153 }
5154
5155 #[test]
5156 fn empty_frames_list_is_rejected_by_count_check_not_gas_check() {
5157 let tx = make_frame_tx_with_gas_limits(vec![]);
5160 let err = tx.validate_static_constraints().unwrap_err();
5161 assert!(err.contains("between 1 and"), "unexpected error: {err}");
5162 }
5163
5164 #[test]
5165 fn sig_hash_covers_frame_value() {
5166 let tx = make_test_frame_tx();
5169 let baseline = tx.compute_sig_hash();
5170
5171 let mut with_sender_value = tx.clone();
5172 with_sender_value.frames[1].value = U256::from(1u64);
5173 assert_ne!(
5174 baseline,
5175 with_sender_value.compute_sig_hash(),
5176 "sig_hash must change when a SENDER frame's value changes"
5177 );
5178
5179 let mut with_verify_value = tx.clone();
5180 with_verify_value.frames[0].value = U256::from(1u64);
5181 assert_ne!(
5182 baseline,
5183 with_verify_value.compute_sig_hash(),
5184 "sig_hash must change when a VERIFY frame's value changes"
5185 );
5186
5187 let mut with_verify_data = tx;
5189 with_verify_data.frames[0].data = Bytes::from_static(b"different_verify_data");
5190 assert_ne!(
5191 baseline,
5192 with_verify_data.compute_sig_hash(),
5193 "VERIFY.data is now covered by sig_hash (no longer elided)"
5194 );
5195 }
5196
5197 #[test]
5198 fn validate_static_constraints_rejects_nonzero_value_on_non_sender_frames() {
5199 let verify_tx = FrameTransaction {
5201 chain_id: 1,
5202 nonce: 0,
5203 sender: Address::from_low_u64_be(0xABCD),
5204 frames: vec![Frame {
5205 mode: FrameMode::Verify as u8,
5206 flags: 0x01,
5207 target: None,
5208 gas_limit: 50_000,
5209 value: U256::from(1u64),
5210 data: Bytes::new(),
5211 }],
5212 signatures: vec![],
5213 max_priority_fee_per_gas: 1_000_000_000,
5214 max_fee_per_gas: 30_000_000_000,
5215 max_fee_per_blob_gas: U256::zero(),
5216 blob_versioned_hashes: vec![],
5217 inner_hash: OnceCell::new(),
5218 cached_canonical: OnceCell::new(),
5219 };
5220 let err = verify_tx.validate_static_constraints().unwrap_err();
5221 assert!(
5222 err.contains("non-zero value only allowed in SENDER mode"),
5223 "unexpected error for VERIFY: {err}"
5224 );
5225
5226 let default_tx = FrameTransaction {
5228 frames: vec![Frame {
5229 mode: FrameMode::Default as u8,
5230 flags: 0x00,
5231 target: Some(Address::from_low_u64_be(0x1234)),
5232 gas_limit: 50_000,
5233 value: U256::from(1u64),
5234 data: Bytes::new(),
5235 }],
5236 ..verify_tx
5237 };
5238 let err = default_tx.validate_static_constraints().unwrap_err();
5239 assert!(
5240 err.contains("non-zero value only allowed in SENDER mode"),
5241 "unexpected error for DEFAULT: {err}"
5242 );
5243
5244 let sender_tx = FrameTransaction {
5246 frames: vec![Frame {
5247 mode: FrameMode::Sender as u8,
5248 flags: 0x00,
5249 target: Some(Address::from_low_u64_be(0x1234)),
5250 gas_limit: 50_000,
5251 value: U256::from(1u64),
5252 data: Bytes::new(),
5253 }],
5254 ..default_tx
5255 };
5256 sender_tx
5257 .validate_static_constraints()
5258 .expect("SENDER frames may carry non-zero value");
5259 }
5260
5261 fn chain_config_with_hegota(hegota_time: Option<u64>) -> crate::types::ChainConfig {
5264 crate::types::ChainConfig {
5265 hegota_time,
5266 ..Default::default()
5267 }
5268 }
5269
5270 #[test]
5271 fn test_frame_tx_pre_fork_chain_config_rejects() {
5272 let cfg = chain_config_with_hegota(None);
5273 assert!(!cfg.is_hegota_activated(0));
5274 assert!(!cfg.is_hegota_activated(u64::MAX));
5275 }
5276
5277 #[test]
5278 fn test_frame_tx_post_fork_admits() {
5279 let cfg = chain_config_with_hegota(Some(1000));
5280 assert!(cfg.is_hegota_activated(2000));
5281 }
5282
5283 #[test]
5284 fn test_frame_tx_fork_boundary_admits() {
5285 let activation_time = 1_700_000_000u64;
5286 let cfg = chain_config_with_hegota(Some(activation_time));
5287 assert!(cfg.is_hegota_activated(activation_time));
5288 assert!(!cfg.is_hegota_activated(activation_time - 1));
5289 }
5290
5291 #[test]
5292 fn test_frame_tx_devnet_fork_epoch_zero_admits() {
5293 let cfg = chain_config_with_hegota(Some(0));
5294 assert!(cfg.is_hegota_activated(0));
5295 assert!(cfg.is_hegota_activated(1));
5296 assert!(cfg.is_hegota_activated(u64::MAX));
5297 }
5298
5299 #[test]
5300 fn test_frame_tx_pre_fork_rlp_roundtrip_still_decodes() {
5301 let tx = make_test_frame_tx();
5304 let mut buf = Vec::new();
5305 tx.encode(&mut buf);
5306 let (decoded, rest) = FrameTransaction::decode_unfinished(&buf).unwrap();
5307 assert!(rest.is_empty());
5308 assert_eq!(decoded.chain_id, tx.chain_id);
5309 assert_eq!(decoded.nonce, tx.nonce);
5310 assert_eq!(decoded.sender, tx.sender);
5311 }
5312
5313 fn expiry_frame(deadline: u64) -> Frame {
5316 Frame {
5317 mode: FrameMode::Verify as u8,
5318 flags: 0x00,
5319 target: Some(frame_tx_expiry_verifier()),
5320 gas_limit: 30_000,
5321 value: U256::zero(),
5322 data: Bytes::copy_from_slice(&deadline.to_be_bytes()),
5323 }
5324 }
5325
5326 #[test]
5327 fn expiry_verifier_frame_passes_static_validation() {
5328 let mut tx = make_test_frame_tx();
5329 tx.frames.insert(0, expiry_frame(1_700_000_000));
5330 assert!(tx.validate_static_constraints().is_ok());
5331 }
5332
5333 #[test]
5334 fn expiry_verifier_frame_rejects_bad_data_length() {
5335 let mut tx = make_test_frame_tx();
5336 let mut f = expiry_frame(0);
5337 f.data = Bytes::from_static(b"short");
5338 tx.frames.insert(0, f);
5339 let err = tx.validate_static_constraints().unwrap_err();
5340 assert!(err.contains("8 bytes"), "{err}");
5341 }
5342
5343 #[test]
5344 fn expiry_verifier_frame_rejects_nonzero_flags() {
5345 let mut tx = make_test_frame_tx();
5346 let mut f = expiry_frame(0);
5347 f.flags = 0x01;
5348 tx.frames.insert(0, f);
5349 let err = tx.validate_static_constraints().unwrap_err();
5350 assert!(err.contains("flags == 0"), "{err}");
5351 }
5352
5353 #[test]
5354 fn at_most_one_expiry_verifier_frame() {
5355 let mut tx = make_test_frame_tx();
5356 tx.frames.insert(0, expiry_frame(1));
5357 tx.frames.insert(0, expiry_frame(2));
5358 let err = tx.validate_static_constraints().unwrap_err();
5359 assert!(err.contains("more than one expiry"), "{err}");
5360 }
5361
5362 #[test]
5363 fn verify_frame_with_zero_scope_is_now_statically_valid() {
5364 let mut tx = make_test_frame_tx();
5369 tx.frames[0].flags = 0x00;
5370 assert!(tx.validate_static_constraints().is_ok());
5371 }
5372
5373 #[test]
5374 fn sig_hash_covers_expiry_deadline_and_all_verify_data() {
5375 let mut tx_a = make_test_frame_tx();
5377 tx_a.frames.insert(0, expiry_frame(100));
5378 let mut tx_b = make_test_frame_tx();
5379 tx_b.frames.insert(0, expiry_frame(200));
5380 assert_ne!(tx_a.compute_sig_hash(), tx_b.compute_sig_hash());
5382
5383 let tx_c = make_test_frame_tx();
5385 let mut tx_d = make_test_frame_tx();
5386 tx_d.frames[0].data = Bytes::from_static(b"different_verify_data");
5387 assert_ne!(tx_c.compute_sig_hash(), tx_d.compute_sig_hash());
5388 }
5389
5390 #[test]
5391 fn expiry_deadline_parses_or_none() {
5392 let mut tx = make_test_frame_tx();
5393 assert_eq!(tx.expiry_deadline(), None);
5394 tx.frames.insert(0, expiry_frame(1_700_000_123));
5395 assert_eq!(tx.expiry_deadline(), Some(1_700_000_123));
5396 }
5397
5398 #[test]
5399 fn signature_rlp_roundtrip() {
5400 let sig = FrameSignature {
5401 scheme: FRAME_SIG_SCHEME_P256,
5402 signer: Address::from_low_u64_be(0x1234),
5403 msg: Bytes::from(vec![7u8; 32]),
5404 signature: Bytes::from(vec![9u8; 128]),
5405 };
5406 let mut buf = Vec::new();
5407 sig.encode(&mut buf);
5408 let (decoded, rest) = FrameSignature::decode_unfinished(&buf).unwrap();
5409 assert!(rest.is_empty());
5410 assert_eq!(decoded, sig);
5411 }
5412
5413 #[test]
5414 fn frame_tx_with_signatures_rlp_roundtrip() {
5415 let tx = make_test_frame_tx();
5416 assert!(!tx.signatures.is_empty());
5417 let mut buf = Vec::new();
5418 tx.encode(&mut buf);
5419 let (decoded, rest) = FrameTransaction::decode_unfinished(&buf).unwrap();
5420 assert!(rest.is_empty());
5421 assert_eq!(decoded.signatures, tx.signatures);
5422 assert_eq!(decoded.frames, tx.frames);
5423 assert_eq!(decoded.nonce, tx.nonce);
5424 }
5425
5426 #[test]
5427 fn p2p_frame_transaction_rlp_roundtrip() {
5428 let ft = make_test_frame_tx();
5431 assert!(!ft.signatures.is_empty());
5432 assert!(!ft.frames.is_empty());
5433 let original = P2PTransaction::FrameTransaction(ft);
5434
5435 let encoded = original.encode_to_vec();
5436 let (decoded, rest) = P2PTransaction::decode_unfinished(&encoded).unwrap();
5437 assert!(rest.is_empty());
5438 assert_eq!(decoded, original);
5439 assert_eq!(decoded.tx_type(), TxType::Frame);
5440
5441 let as_tx: Transaction = decoded.try_into().unwrap();
5444 assert!(matches!(as_tx, Transaction::FrameTransaction(_)));
5445 }
5446
5447 #[test]
5448 fn static_validation_rejects_unknown_scheme() {
5449 let mut tx = make_test_frame_tx();
5450 tx.signatures[0].scheme = 2;
5451 assert!(
5452 tx.validate_static_constraints()
5453 .unwrap_err()
5454 .contains("unsupported scheme"),
5455 );
5456 }
5457
5458 #[test]
5459 fn static_validation_rejects_bad_msg_length() {
5460 let mut tx = make_test_frame_tx();
5461 tx.signatures[0].msg = Bytes::from(vec![1u8; 16]);
5462 assert!(
5463 tx.validate_static_constraints()
5464 .unwrap_err()
5465 .contains("32 bytes"),
5466 );
5467 }
5468
5469 #[test]
5470 fn static_validation_rejects_zero_explicit_msg() {
5471 let mut tx = make_test_frame_tx();
5472 tx.signatures[0].msg = Bytes::from(vec![0u8; 32]);
5473 assert!(
5474 tx.validate_static_constraints()
5475 .unwrap_err()
5476 .contains("zero digest"),
5477 );
5478 }
5479
5480 #[test]
5481 fn sig_hash_elides_empty_msg_signature_bytes() {
5482 let mut a = make_test_frame_tx();
5483 let mut b = make_test_frame_tx();
5484 assert!(a.signatures[0].msg.is_empty());
5486 a.signatures[0].signature = Bytes::from(vec![1u8; 65]);
5487 b.signatures[0].signature = Bytes::from(vec![2u8; 65]);
5488 assert_eq!(a.compute_sig_hash(), b.compute_sig_hash());
5489 }
5490
5491 #[test]
5492 fn sig_hash_covers_frame_data_now() {
5493 let mut a = make_test_frame_tx();
5494 let mut b = make_test_frame_tx();
5495 a.frames[0].data = Bytes::from_static(b"aaaa");
5497 b.frames[0].data = Bytes::from_static(b"bbbb");
5498 assert_ne!(a.compute_sig_hash(), b.compute_sig_hash());
5499 }
5500
5501 #[test]
5502 fn sig_hash_covers_explicit_msg_signature_bytes() {
5503 let mut a = make_test_frame_tx();
5504 let mut b = make_test_frame_tx();
5505 a.signatures[0].msg = Bytes::from(vec![9u8; 32]);
5506 b.signatures[0].msg = Bytes::from(vec![9u8; 32]);
5507 a.signatures[0].signature = Bytes::from(vec![1u8; 65]);
5508 b.signatures[0].signature = Bytes::from(vec![2u8; 65]);
5509 assert_ne!(a.compute_sig_hash(), b.compute_sig_hash());
5511 }
5512
5513 #[test]
5514 fn total_gas_limit_includes_signature_costs() {
5515 let mut tx = make_test_frame_tx();
5516 let base = tx.total_gas_limit();
5517 tx.signatures.push(FrameSignature {
5519 scheme: FRAME_SIG_SCHEME_P256,
5520 signer: Address::from_low_u64_be(1),
5521 msg: Bytes::new(),
5522 signature: Bytes::from(vec![0u8; 128]),
5523 });
5524 assert!(tx.total_gas_limit() >= base + 6700);
5525 assert_eq!(tx.signature_verification_cost(), 2800 + 6700);
5526 }
5527
5528 #[test]
5529 fn golden_frame_tx_rlp_and_sig_hash() {
5530 let tx = FrameTransaction {
5535 chain_id: 1,
5536 nonce: 7,
5537 sender: Address::from_low_u64_be(0xABCD),
5538 frames: vec![
5539 Frame {
5540 mode: 1,
5541 flags: 3,
5542 target: None,
5543 gas_limit: 0x5208,
5544 value: U256::zero(),
5545 data: Bytes::from_static(&[0x11, 0x22]),
5546 },
5547 Frame {
5548 mode: 2,
5549 flags: 0,
5550 target: Some(Address::from_low_u64_be(0x1234)),
5551 gas_limit: 0x9c40,
5552 value: U256::zero(),
5553 data: Bytes::new(),
5554 },
5555 ],
5556 signatures: vec![FrameSignature {
5557 scheme: FRAME_SIG_SCHEME_SECP256K1,
5558 signer: Address::from_low_u64_be(0xABCD),
5559 msg: Bytes::new(),
5560 signature: Bytes::from(vec![0x01u8; 65]),
5561 }],
5562 max_priority_fee_per_gas: 0x3b9aca00,
5563 max_fee_per_gas: 0x6fc23ac00,
5564 max_fee_per_blob_gas: U256::zero(),
5565 blob_versioned_hashes: vec![],
5566 inner_hash: OnceCell::new(),
5567 cached_canonical: OnceCell::new(),
5568 };
5569
5570 let mut buf = Vec::new();
5571 tx.encode(&mut buf);
5572 let rlp_hex = hex::encode(&buf);
5573 assert_eq!(
5575 rlp_hex,
5576 "f8ab010794000000000000000000000000000000000000abcde8ca01038082520880821122dc0280940000000000000000000000000000000000001234829c408080f85cf85a8094000000000000000000000000000000000000abcd80b8410101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101843b9aca008506fc23ac0080c0"
5577 );
5578
5579 let (decoded, rest) = FrameTransaction::decode_unfinished(&buf).unwrap();
5581 assert!(rest.is_empty());
5582 assert_eq!(decoded, tx);
5583
5584 let sig_hash = tx.compute_sig_hash();
5585 assert_eq!(
5587 format!("{:#x}", sig_hash),
5588 "0x87d1a9ce8a1f242345bb20deab5e5111a41780814ea497fbd20700a60a2ecd8d",
5589 );
5590
5591 let mut tx2 = tx.clone();
5593 tx2.signatures[0].signature = Bytes::from(vec![0x02u8; 65]);
5594 assert_eq!(
5595 tx.compute_sig_hash(),
5596 tx2.compute_sig_hash(),
5597 "sig_hash must be independent of empty-msg signature bytes",
5598 );
5599 }
5600
5601 #[test]
5602 fn test_cost_without_base_fee_eip4844_includes_blob_gas() {
5603 use crate::constants::GAS_PER_BLOB;
5607
5608 let max_fee_per_gas: u64 = 100;
5609 let gas: u64 = 21_000;
5610 let value = U256::from(7u64);
5611 let max_fee_per_blob_gas = U256::from(50u64);
5612 let blob_count: usize = 1;
5613
5614 let tx = Transaction::EIP4844Transaction(EIP4844Transaction {
5615 max_fee_per_gas,
5616 gas,
5617 value,
5618 max_fee_per_blob_gas,
5619 blob_versioned_hashes: vec![H256::zero(); blob_count],
5620 ..Default::default()
5621 });
5622
5623 let got = tx.cost_without_base_fee().expect("cost is computable");
5624
5625 let gas_cost = U256::from(max_fee_per_gas) * U256::from(gas);
5626 let blob_gas = U256::from(GAS_PER_BLOB) * U256::from(blob_count as u64);
5627 let blob_cost = blob_gas * max_fee_per_blob_gas;
5628 let expected = gas_cost + blob_cost + value;
5629
5630 assert_eq!(
5631 got, expected,
5632 "blob-gas term missing from cost_without_base_fee() for EIP-4844"
5633 );
5634 }
5635}