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