Skip to main content

ethrex_common/types/
transaction.rs

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};
12use lru::LruCache;
13pub use mempool::MempoolTransaction;
14
15const MAX_SIGNER_CACHE_ENTRIES: usize = 100_000;
16
17/// Global cache mapping transaction hash → recovered sender address.
18/// Keyed by tx hash (unique per transaction), so each entry is safe to reuse.
19/// Not suitable for EIP-7702 authorization tuples where the same message hash
20/// can correspond to different signers (the message excludes the signature).
21/// Uses LRU eviction to avoid periodic cold-start spikes from clearing all entries.
22///
23/// Lock with `.unwrap_or_else(|e| e.into_inner())` — a poisoned mutex just means
24/// a thread panicked mid-update; the LruCache invariants are maintained by the
25/// std Mutex (data is still accessible), and a missing entry only costs one
26/// redundant recovery, so it's safe to keep using.
27pub 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, GenericTransaction, GenericTransactionError,
36};
37
38/// The serialized length of a default eip1559 transaction
39pub const EIP1559_DEFAULT_SERIALIZED_LENGTH: usize = 15;
40
41use ethrex_rlp::{
42    constants::RLP_NULL,
43    decode::{RLPDecode, decode_rlp_item},
44    encode::{PayloadRLPEncode, RLPEncode},
45    error::RLPDecodeError,
46    structs::{Decoder, Encoder},
47};
48
49#[cfg(all(feature = "eip-8025", target_arch = "riscv64"))]
50use super::eip8025_cell::OnceCell;
51use crate::types::{AccessList, AuthorizationList, BlobsBundle};
52#[cfg(not(all(feature = "eip-8025", target_arch = "riscv64")))]
53use once_cell::sync::OnceCell;
54
55// The `#[serde(untagged)]` attribute allows the `Transaction` enum to be serialized without
56// a tag indicating the variant type. This means that Serde will serialize the enum's variants
57// directly according to the structure of the variant itself.
58// For each variant, Serde will use the serialization logic implemented
59// for the inner type of that variant (like `LegacyTransaction`, `EIP2930Transaction`, etc.).
60// The serialization will fail if the data does not match the structure of any variant.
61//
62// A custom Deserialization method is implemented to match the specific transaction `type`.
63#[derive(Clone, Debug, PartialEq, Eq, Serialize, RSerialize, RDeserialize, Archive)]
64#[serde(untagged)]
65pub enum Transaction {
66    LegacyTransaction(LegacyTransaction),
67    EIP2930Transaction(EIP2930Transaction),
68    EIP1559Transaction(EIP1559Transaction),
69    EIP4844Transaction(EIP4844Transaction),
70    EIP7702Transaction(EIP7702Transaction),
71    PrivilegedL2Transaction(PrivilegedL2Transaction),
72    FeeTokenTransaction(FeeTokenTransaction),
73}
74
75/// The same as a Transaction enum, only that blob transactions are in wrapped format, including
76/// the blobs bundle.
77/// PrivilegedL2Transaction is not included as it is not expected to be sent over P2P.
78#[derive(Clone, Debug, PartialEq, Eq)]
79pub enum P2PTransaction {
80    LegacyTransaction(LegacyTransaction),
81    EIP2930Transaction(EIP2930Transaction),
82    EIP1559Transaction(EIP1559Transaction),
83    EIP4844TransactionWithBlobs(WrappedEIP4844Transaction),
84    EIP7702Transaction(EIP7702Transaction),
85    FeeTokenTransaction(FeeTokenTransaction),
86}
87
88impl TryInto<Transaction> for P2PTransaction {
89    type Error = String;
90
91    fn try_into(self) -> Result<Transaction, Self::Error> {
92        match self {
93            P2PTransaction::LegacyTransaction(itx) => Ok(Transaction::LegacyTransaction(itx)),
94            P2PTransaction::EIP2930Transaction(itx) => Ok(Transaction::EIP2930Transaction(itx)),
95            P2PTransaction::EIP1559Transaction(itx) => Ok(Transaction::EIP1559Transaction(itx)),
96            P2PTransaction::EIP7702Transaction(itx) => Ok(Transaction::EIP7702Transaction(itx)),
97            _ => Err("Can't convert blob p2p transaction into regular transaction. Blob bundle would be lost.".to_string()),
98        }
99    }
100}
101
102impl RLPEncode for P2PTransaction {
103    fn encode(&self, buf: &mut dyn bytes::BufMut) {
104        match self {
105            P2PTransaction::LegacyTransaction(t) => t.encode(buf),
106            tx => <[u8] as RLPEncode>::encode(&tx.encode_canonical_to_vec(), buf),
107        };
108    }
109}
110
111impl RLPDecode for P2PTransaction {
112    fn decode_unfinished(rlp: &[u8]) -> Result<(Self, &[u8]), RLPDecodeError> {
113        let (is_list, payload, remainder) = decode_rlp_item(rlp)?;
114        if !is_list {
115            let tx_type = payload.first().ok_or(RLPDecodeError::InvalidLength)?;
116            let tx_encoding = &payload.get(1..).ok_or(RLPDecodeError::InvalidLength)?;
117            // Look at the first byte to check if it corresponds to a TransactionType
118            match *tx_type {
119                // Legacy
120                0x0 => LegacyTransaction::decode(tx_encoding)
121                    .map(|tx| (P2PTransaction::LegacyTransaction(tx), remainder)), // TODO: check if this is a real case scenario
122                // EIP2930
123                0x1 => EIP2930Transaction::decode(tx_encoding)
124                    .map(|tx| (P2PTransaction::EIP2930Transaction(tx), remainder)),
125                // EIP1559
126                0x2 => EIP1559Transaction::decode(tx_encoding)
127                    .map(|tx| (P2PTransaction::EIP1559Transaction(tx), remainder)),
128                // EIP4844
129                0x3 => WrappedEIP4844Transaction::decode(tx_encoding)
130                    .map(|tx| (P2PTransaction::EIP4844TransactionWithBlobs(tx), remainder)),
131                // EIP7702
132                0x4 => EIP7702Transaction::decode(tx_encoding)
133                    .map(|tx| (P2PTransaction::EIP7702Transaction(tx), remainder)),
134                // FeeToken
135                0x7d => FeeTokenTransaction::decode(tx_encoding)
136                    .map(|tx| (P2PTransaction::FeeTokenTransaction(tx), remainder)),
137                ty => Err(RLPDecodeError::Custom(format!(
138                    "Invalid transaction type: {ty}"
139                ))),
140            }
141        } else {
142            // LegacyTransaction
143            LegacyTransaction::decode_unfinished(rlp)
144                .map(|(tx, rem)| (P2PTransaction::LegacyTransaction(tx), rem))
145        }
146    }
147}
148
149#[derive(Clone, Debug, PartialEq, Eq)]
150pub struct WrappedEIP4844Transaction {
151    pub tx: EIP4844Transaction,
152    pub wrapper_version: Option<u8>,
153    pub blobs_bundle: BlobsBundle,
154}
155
156impl RLPEncode for WrappedEIP4844Transaction {
157    fn encode(&self, buf: &mut dyn bytes::BufMut) {
158        let encoder = Encoder::new(buf);
159        encoder
160            .encode_field(&self.tx)
161            .encode_optional_field(&self.wrapper_version)
162            .encode_field(&self.blobs_bundle.blobs)
163            .encode_field(&self.blobs_bundle.commitments)
164            .encode_field(&self.blobs_bundle.proofs)
165            .finish();
166    }
167}
168
169impl RLPDecode for WrappedEIP4844Transaction {
170    fn decode_unfinished(rlp: &[u8]) -> Result<(WrappedEIP4844Transaction, &[u8]), RLPDecodeError> {
171        let decoder = Decoder::new(rlp)?;
172        let Ok((tx, decoder)) = decoder.decode_field("tx") else {
173            // Handle the case of blobless transaction
174            let (tx, rest) = EIP4844Transaction::decode_unfinished(rlp)?;
175            return Ok((
176                WrappedEIP4844Transaction {
177                    tx,
178                    wrapper_version: None,
179                    // Empty blobs bundles are not valid
180                    blobs_bundle: BlobsBundle::empty(),
181                },
182                rest,
183            ));
184        };
185
186        let (wrapper_version, decoder) = decoder.decode_optional_field();
187        let (blobs, decoder) = decoder.decode_field("blobs")?;
188        let (commitments, decoder) = decoder.decode_field("commitments")?;
189        let (proofs, decoder) = decoder.decode_field("proofs")?;
190
191        let wrapped = WrappedEIP4844Transaction {
192            tx,
193            wrapper_version,
194            blobs_bundle: BlobsBundle {
195                blobs,
196                commitments,
197                proofs,
198                version: wrapper_version.unwrap_or_default(),
199            },
200        };
201        Ok((wrapped, decoder.finish()?))
202    }
203}
204
205#[derive(Clone, Debug, PartialEq, Eq, Default, RSerialize, RDeserialize, Archive)]
206pub struct LegacyTransaction {
207    pub nonce: u64,
208    #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
209    pub gas_price: U256,
210    pub gas: u64,
211    /// The recipient of the transaction.
212    /// Create transactions contain a [`null`](RLP_NULL) value in this field.
213    pub to: TxKind,
214    #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
215    pub value: U256,
216    #[rkyv(with=crate::rkyv_utils::BytesWrapper)]
217    pub data: Bytes,
218    #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
219    pub v: U256,
220    #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
221    pub r: U256,
222    #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
223    pub s: U256,
224    #[rkyv(with=rkyv::with::Skip)]
225    pub inner_hash: OnceCell<H256>,
226    #[rkyv(with=rkyv::with::Skip)]
227    pub sender_cache: OnceCell<Address>,
228}
229
230#[derive(Clone, Debug, PartialEq, Eq, Default, RSerialize, RDeserialize, Archive)]
231pub struct EIP2930Transaction {
232    pub chain_id: u64,
233    pub nonce: u64,
234    #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
235    pub gas_price: U256,
236    pub gas_limit: u64,
237    pub to: TxKind,
238    #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
239    pub value: U256,
240    #[rkyv(with=crate::rkyv_utils::BytesWrapper)]
241    pub data: Bytes,
242    #[rkyv(with=rkyv::with::Map<crate::rkyv_utils::AccessListItemWrapper>)]
243    pub access_list: AccessList,
244    pub signature_y_parity: bool,
245    #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
246    pub signature_r: U256,
247    #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
248    pub signature_s: U256,
249    #[rkyv(with=rkyv::with::Skip)]
250    pub inner_hash: OnceCell<H256>,
251    #[rkyv(with=rkyv::with::Skip)]
252    pub sender_cache: OnceCell<Address>,
253    #[rkyv(with=rkyv::with::Skip)]
254    pub cached_canonical: OnceCell<Vec<u8>>,
255}
256
257#[derive(Clone, Debug, PartialEq, Eq, Default, RSerialize, RDeserialize, Archive)]
258pub struct EIP1559Transaction {
259    pub chain_id: u64,
260    pub nonce: u64,
261    pub max_priority_fee_per_gas: u64,
262    pub max_fee_per_gas: u64,
263    pub gas_limit: u64,
264    pub to: TxKind,
265    #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
266    pub value: U256,
267    #[rkyv(with=crate::rkyv_utils::BytesWrapper)]
268    pub data: Bytes,
269    #[rkyv(with=rkyv::with::Map<crate::rkyv_utils::AccessListItemWrapper>)]
270    pub access_list: AccessList,
271    pub signature_y_parity: bool,
272    #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
273    pub signature_r: U256,
274    #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
275    pub signature_s: U256,
276    #[rkyv(with=rkyv::with::Skip)]
277    pub inner_hash: OnceCell<H256>,
278    #[rkyv(with=rkyv::with::Skip)]
279    pub sender_cache: OnceCell<Address>,
280    #[rkyv(with=rkyv::with::Skip)]
281    pub cached_canonical: OnceCell<Vec<u8>>,
282}
283
284#[derive(Clone, Debug, PartialEq, Eq, Default, RSerialize, RDeserialize, Archive)]
285pub struct EIP4844Transaction {
286    pub chain_id: u64,
287    pub nonce: u64,
288    pub max_priority_fee_per_gas: u64,
289    pub max_fee_per_gas: u64,
290    pub gas: u64,
291    #[rkyv(with=crate::rkyv_utils::H160Wrapper)]
292    pub to: Address,
293    #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
294    pub value: U256,
295    #[rkyv(with=crate::rkyv_utils::BytesWrapper)]
296    pub data: Bytes,
297    #[rkyv(with=rkyv::with::Map<crate::rkyv_utils::AccessListItemWrapper>)]
298    pub access_list: AccessList,
299    #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
300    pub max_fee_per_blob_gas: U256,
301    #[rkyv(with=rkyv::with::Map<crate::rkyv_utils::H256Wrapper>)]
302    pub blob_versioned_hashes: Vec<H256>,
303    pub signature_y_parity: bool,
304    #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
305    pub signature_r: U256,
306    #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
307    pub signature_s: U256,
308    #[rkyv(with=rkyv::with::Skip)]
309    pub inner_hash: OnceCell<H256>,
310    #[rkyv(with=rkyv::with::Skip)]
311    pub sender_cache: OnceCell<Address>,
312    #[rkyv(with=rkyv::with::Skip)]
313    pub cached_canonical: OnceCell<Vec<u8>>,
314}
315
316#[derive(Clone, Debug, PartialEq, Eq, Default, RSerialize, RDeserialize, Archive)]
317pub struct EIP7702Transaction {
318    pub chain_id: u64,
319    pub nonce: u64,
320    pub max_priority_fee_per_gas: u64,
321    pub max_fee_per_gas: u64,
322    pub gas_limit: u64,
323    #[rkyv(with=crate::rkyv_utils::H160Wrapper)]
324    pub to: Address,
325    #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
326    pub value: U256,
327    #[rkyv(with=crate::rkyv_utils::BytesWrapper)]
328    pub data: Bytes,
329    #[rkyv(with=rkyv::with::Map<crate::rkyv_utils::AccessListItemWrapper>)]
330    pub access_list: AccessList,
331    pub authorization_list: AuthorizationList,
332    pub signature_y_parity: bool,
333    #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
334    pub signature_r: U256,
335    #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
336    pub signature_s: U256,
337    #[rkyv(with=rkyv::with::Skip)]
338    pub inner_hash: OnceCell<H256>,
339    #[rkyv(with=rkyv::with::Skip)]
340    pub sender_cache: OnceCell<Address>,
341    #[rkyv(with=rkyv::with::Skip)]
342    pub cached_canonical: OnceCell<Vec<u8>>,
343}
344#[derive(Clone, Debug, PartialEq, Eq, Default, RSerialize, RDeserialize, Archive)]
345pub struct PrivilegedL2Transaction {
346    pub chain_id: u64,
347    pub nonce: u64,
348    pub max_priority_fee_per_gas: u64,
349    pub max_fee_per_gas: u64,
350    pub gas_limit: u64,
351    pub to: TxKind,
352    #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
353    pub value: U256,
354    #[rkyv(with=crate::rkyv_utils::BytesWrapper)]
355    pub data: Bytes,
356    #[rkyv(with=rkyv::with::Map<crate::rkyv_utils::AccessListItemWrapper>)]
357    pub access_list: AccessList,
358    #[rkyv(with=crate::rkyv_utils::H160Wrapper)]
359    pub from: Address,
360    #[rkyv(with=rkyv::with::Skip)]
361    pub inner_hash: OnceCell<H256>,
362    #[rkyv(with=rkyv::with::Skip)]
363    pub sender_cache: OnceCell<Address>,
364    #[rkyv(with=rkyv::with::Skip)]
365    pub cached_canonical: OnceCell<Vec<u8>>,
366}
367
368#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
369pub enum TxType {
370    #[default]
371    Legacy = 0x00,
372    EIP2930 = 0x01,
373    EIP1559 = 0x02,
374    EIP4844 = 0x03,
375    EIP7702 = 0x04,
376    FeeToken = 0x7d,
377    // We take the same approach as Optimism to define the privileged tx prefix
378    // https://github.com/ethereum-optimism/specs/blob/c6903a3b2cad575653e1f5ef472debb573d83805/specs/protocol/deposits.md#the-deposited-transaction-type
379    Privileged = 0x7e,
380}
381
382impl From<TxType> for u8 {
383    fn from(val: TxType) -> Self {
384        match val {
385            TxType::Legacy => 0x00,
386            TxType::EIP2930 => 0x01,
387            TxType::EIP1559 => 0x02,
388            TxType::EIP4844 => 0x03,
389            TxType::EIP7702 => 0x04,
390            TxType::FeeToken => 0x7d,
391            TxType::Privileged => 0x7e,
392        }
393    }
394}
395
396impl Display for TxType {
397    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
398        match self {
399            TxType::Legacy => write!(f, "Legacy"),
400            TxType::EIP2930 => write!(f, "EIP2930"),
401            TxType::EIP1559 => write!(f, "EIP1559"),
402            TxType::EIP4844 => write!(f, "EIP4844"),
403            TxType::EIP7702 => write!(f, "EIP7702"),
404            TxType::Privileged => write!(f, "Privileged"),
405            TxType::FeeToken => write!(f, "FeeToken"),
406        }
407    }
408}
409
410impl Transaction {
411    pub fn tx_type(&self) -> TxType {
412        match self {
413            Transaction::LegacyTransaction(_) => TxType::Legacy,
414            Transaction::EIP2930Transaction(_) => TxType::EIP2930,
415            Transaction::EIP1559Transaction(_) => TxType::EIP1559,
416            Transaction::EIP4844Transaction(_) => TxType::EIP4844,
417            Transaction::EIP7702Transaction(_) => TxType::EIP7702,
418            Transaction::FeeTokenTransaction(_) => TxType::FeeToken,
419            Transaction::PrivilegedL2Transaction(_) => TxType::Privileged,
420        }
421    }
422
423    fn calc_effective_gas_price(&self, base_fee_per_gas: Option<u64>) -> Option<U256> {
424        let base_fee = base_fee_per_gas?;
425        let max_fee = self.max_fee_per_gas()?;
426        if max_fee < base_fee {
427            // This is invalid, can't calculate
428            return None;
429        }
430
431        let priority_fee_per_gas = min(self.max_priority_fee()?, max_fee.saturating_sub(base_fee));
432        Some(U256::from(priority_fee_per_gas) + U256::from(base_fee))
433    }
434
435    pub fn effective_gas_price(&self, base_fee_per_gas: Option<u64>) -> Option<U256> {
436        match self.tx_type() {
437            TxType::Legacy => Some(self.gas_price()),
438            TxType::EIP2930 => Some(self.gas_price()),
439            TxType::EIP1559 => self.calc_effective_gas_price(base_fee_per_gas),
440            TxType::EIP4844 => self.calc_effective_gas_price(base_fee_per_gas),
441            TxType::EIP7702 => self.calc_effective_gas_price(base_fee_per_gas),
442            TxType::FeeToken => self.calc_effective_gas_price(base_fee_per_gas),
443            TxType::Privileged => Some(self.gas_price()),
444        }
445    }
446
447    pub fn cost_without_base_fee(&self) -> Option<U256> {
448        let price = match self.tx_type() {
449            TxType::Legacy => self.gas_price(),
450            TxType::EIP2930 => self.gas_price(),
451            TxType::EIP1559 => U256::from(self.max_fee_per_gas()?),
452            TxType::EIP4844 => U256::from(self.max_fee_per_gas()?),
453            TxType::EIP7702 => U256::from(self.max_fee_per_gas()?),
454            TxType::FeeToken => U256::from(self.max_fee_per_gas()?),
455            TxType::Privileged => self.gas_price(),
456        };
457
458        let base = U256::saturating_add(
459            U256::saturating_mul(price, self.gas_limit().into()),
460            self.value(),
461        );
462
463        // EIP-4844 blob txs pay an additional `blob_gas * max_fee_per_blob_gas`
464        // upfront. Every peer client (geth, reth, nethermind, erigon, besu)
465        // includes this in the balance-sufficiency check.
466        if let Transaction::EIP4844Transaction(tx) = self {
467            let blob_gas = U256::from(crate::constants::GAS_PER_BLOB)
468                .saturating_mul(U256::from(tx.blob_versioned_hashes.len() as u64));
469            let blob_cost = blob_gas.saturating_mul(tx.max_fee_per_blob_gas);
470            return Some(base.saturating_add(blob_cost));
471        }
472
473        Some(base)
474    }
475
476    pub fn fee_token(&self) -> Option<Address> {
477        if let Transaction::FeeTokenTransaction(tx) = self {
478            Some(tx.fee_token)
479        } else {
480            None
481        }
482    }
483
484    /// Returns a reference to the `cached_canonical` cell for non-legacy
485    /// transaction types, or `None` for legacy transactions.
486    fn cached_canonical_cell(&self) -> Option<&OnceCell<Vec<u8>>> {
487        match self {
488            Transaction::LegacyTransaction(_) => None,
489            Transaction::EIP2930Transaction(t) => Some(&t.cached_canonical),
490            Transaction::EIP1559Transaction(t) => Some(&t.cached_canonical),
491            Transaction::EIP4844Transaction(t) => Some(&t.cached_canonical),
492            Transaction::EIP7702Transaction(t) => Some(&t.cached_canonical),
493            Transaction::PrivilegedL2Transaction(t) => Some(&t.cached_canonical),
494            Transaction::FeeTokenTransaction(t) => Some(&t.cached_canonical),
495        }
496    }
497}
498
499impl RLPEncode for Transaction {
500    /// Transactions can be encoded in the following formats:
501    /// A) Legacy transactions: rlp(LegacyTransaction)
502    /// B) Non legacy transactions: rlp(Bytes) where Bytes represents the canonical encoding for the transaction as a bytes object.
503    /// Checkout [Transaction::encode_canonical] for more information
504    fn encode(&self, buf: &mut dyn bytes::BufMut) {
505        match self {
506            Transaction::LegacyTransaction(t) => t.encode(buf),
507            _ => {
508                let canonical = self.encode_canonical_to_vec();
509                <[u8] as RLPEncode>::encode(canonical.as_slice(), buf)
510            }
511        };
512    }
513}
514
515impl RLPDecode for Transaction {
516    /// Transactions can be encoded in the following formats:
517    /// A) Legacy transactions: rlp(LegacyTransaction)
518    /// B) Non legacy transactions: rlp(Bytes) where Bytes represents the canonical encoding for the transaction as a bytes object.
519    /// Checkout [Transaction::decode_canonical] for more information
520    fn decode_unfinished(rlp: &[u8]) -> Result<(Self, &[u8]), RLPDecodeError> {
521        let (is_list, payload, remainder) = decode_rlp_item(rlp)?;
522        if !is_list {
523            let tx_type = payload.first().ok_or(RLPDecodeError::InvalidLength)?;
524            let tx_encoding = &payload.get(1..).ok_or(RLPDecodeError::InvalidLength)?;
525            // Look at the first byte to check if it corresponds to a TransactionType
526            match *tx_type {
527                // Legacy
528                0x0 => LegacyTransaction::decode(tx_encoding)
529                    .map(|tx| (Transaction::LegacyTransaction(tx), remainder)), // TODO: check if this is a real case scenario
530                // EIP2930
531                0x1 => EIP2930Transaction::decode(tx_encoding)
532                    .map(|tx| (Transaction::EIP2930Transaction(tx), remainder)),
533                // EIP1559
534                0x2 => EIP1559Transaction::decode(tx_encoding)
535                    .map(|tx| (Transaction::EIP1559Transaction(tx), remainder)),
536                // EIP4844
537                0x3 => EIP4844Transaction::decode(tx_encoding)
538                    .map(|tx| (Transaction::EIP4844Transaction(tx), remainder)),
539                // EIP7702
540                0x4 => EIP7702Transaction::decode(tx_encoding)
541                    .map(|tx| (Transaction::EIP7702Transaction(tx), remainder)),
542                // FeeToken
543                0x7d => FeeTokenTransaction::decode(tx_encoding)
544                    .map(|tx| (Transaction::FeeTokenTransaction(tx), remainder)),
545                // PrivilegedL2
546                0x7e => PrivilegedL2Transaction::decode(tx_encoding)
547                    .map(|tx| (Transaction::PrivilegedL2Transaction(tx), remainder)),
548                ty => Err(RLPDecodeError::Custom(format!(
549                    "Invalid transaction type: {ty}"
550                ))),
551            }
552        } else {
553            // LegacyTransaction
554            LegacyTransaction::decode_unfinished(rlp)
555                .map(|(tx, rem)| (Transaction::LegacyTransaction(tx), rem))
556        }
557    }
558}
559
560/// The transaction's kind: call or create.
561#[derive(Clone, Debug, PartialEq, Eq, Default, RSerialize, RDeserialize, Archive)]
562pub enum TxKind {
563    Call(#[rkyv(with=crate::rkyv_utils::H160Wrapper)] Address),
564    #[default]
565    Create,
566}
567
568impl RLPEncode for TxKind {
569    fn encode(&self, buf: &mut dyn bytes::BufMut) {
570        match self {
571            Self::Call(address) => address.encode(buf),
572            Self::Create => buf.put_u8(RLP_NULL),
573        }
574    }
575}
576
577impl RLPDecode for TxKind {
578    fn decode_unfinished(rlp: &[u8]) -> Result<(Self, &[u8]), RLPDecodeError> {
579        let first_byte = rlp.first().ok_or(RLPDecodeError::InvalidLength)?;
580        if *first_byte == RLP_NULL {
581            return Ok((Self::Create, &rlp[1..]));
582        }
583        Address::decode_unfinished(rlp).map(|(t, rest)| (Self::Call(t), rest))
584    }
585}
586
587impl RLPEncode for LegacyTransaction {
588    fn encode(&self, buf: &mut dyn bytes::BufMut) {
589        Encoder::new(buf)
590            .encode_field(&self.nonce)
591            .encode_field(&self.gas_price)
592            .encode_field(&self.gas)
593            .encode_field(&self.to)
594            .encode_field(&self.value)
595            .encode_field(&self.data)
596            .encode_field(&self.v)
597            .encode_field(&self.r)
598            .encode_field(&self.s)
599            .finish();
600    }
601}
602
603impl RLPEncode for EIP2930Transaction {
604    fn encode(&self, buf: &mut dyn bytes::BufMut) {
605        Encoder::new(buf)
606            .encode_field(&self.chain_id)
607            .encode_field(&self.nonce)
608            .encode_field(&self.gas_price)
609            .encode_field(&self.gas_limit)
610            .encode_field(&self.to)
611            .encode_field(&self.value)
612            .encode_field(&self.data)
613            .encode_field(&self.access_list)
614            .encode_field(&self.signature_y_parity)
615            .encode_field(&self.signature_r)
616            .encode_field(&self.signature_s)
617            .finish()
618    }
619}
620
621impl RLPEncode for EIP1559Transaction {
622    fn encode(&self, buf: &mut dyn bytes::BufMut) {
623        Encoder::new(buf)
624            .encode_field(&self.chain_id)
625            .encode_field(&self.nonce)
626            .encode_field(&self.max_priority_fee_per_gas)
627            .encode_field(&self.max_fee_per_gas)
628            .encode_field(&self.gas_limit)
629            .encode_field(&self.to)
630            .encode_field(&self.value)
631            .encode_field(&self.data)
632            .encode_field(&self.access_list)
633            .encode_field(&self.signature_y_parity)
634            .encode_field(&self.signature_r)
635            .encode_field(&self.signature_s)
636            .finish()
637    }
638}
639
640impl RLPEncode for EIP4844Transaction {
641    fn encode(&self, buf: &mut dyn bytes::BufMut) {
642        Encoder::new(buf)
643            .encode_field(&self.chain_id)
644            .encode_field(&self.nonce)
645            .encode_field(&self.max_priority_fee_per_gas)
646            .encode_field(&self.max_fee_per_gas)
647            .encode_field(&self.gas)
648            .encode_field(&self.to)
649            .encode_field(&self.value)
650            .encode_field(&self.data)
651            .encode_field(&self.access_list)
652            .encode_field(&self.max_fee_per_blob_gas)
653            .encode_field(&self.blob_versioned_hashes)
654            .encode_field(&self.signature_y_parity)
655            .encode_field(&self.signature_r)
656            .encode_field(&self.signature_s)
657            .finish()
658    }
659}
660
661impl RLPEncode for EIP7702Transaction {
662    fn encode(&self, buf: &mut dyn bytes::BufMut) {
663        Encoder::new(buf)
664            .encode_field(&self.chain_id)
665            .encode_field(&self.nonce)
666            .encode_field(&self.max_priority_fee_per_gas)
667            .encode_field(&self.max_fee_per_gas)
668            .encode_field(&self.gas_limit)
669            .encode_field(&self.to)
670            .encode_field(&self.value)
671            .encode_field(&self.data)
672            .encode_field(&self.access_list)
673            .encode_field(&self.authorization_list)
674            .encode_field(&self.signature_y_parity)
675            .encode_field(&self.signature_r)
676            .encode_field(&self.signature_s)
677            .finish()
678    }
679}
680
681impl RLPEncode for PrivilegedL2Transaction {
682    fn encode(&self, buf: &mut dyn bytes::BufMut) {
683        Encoder::new(buf)
684            .encode_field(&self.chain_id)
685            .encode_field(&self.nonce)
686            .encode_field(&self.max_priority_fee_per_gas)
687            .encode_field(&self.max_fee_per_gas)
688            .encode_field(&self.gas_limit)
689            .encode_field(&self.to)
690            .encode_field(&self.value)
691            .encode_field(&self.data)
692            .encode_field(&self.access_list)
693            .encode_field(&self.from)
694            .finish()
695    }
696}
697
698impl RLPEncode for FeeTokenTransaction {
699    fn encode(&self, buf: &mut dyn bytes::BufMut) {
700        Encoder::new(buf)
701            .encode_field(&self.chain_id)
702            .encode_field(&self.nonce)
703            .encode_field(&self.max_priority_fee_per_gas)
704            .encode_field(&self.max_fee_per_gas)
705            .encode_field(&self.gas_limit)
706            .encode_field(&self.to)
707            .encode_field(&self.value)
708            .encode_field(&self.data)
709            .encode_field(&self.access_list)
710            .encode_field(&self.fee_token)
711            .encode_field(&self.signature_y_parity)
712            .encode_field(&self.signature_r)
713            .encode_field(&self.signature_s)
714            .finish()
715    }
716}
717
718impl PayloadRLPEncode for Transaction {
719    fn encode_payload(&self, buf: &mut dyn bytes::BufMut) {
720        match self {
721            Transaction::LegacyTransaction(tx) => tx.encode_payload(buf),
722            Transaction::EIP1559Transaction(tx) => tx.encode_payload(buf),
723            Transaction::EIP2930Transaction(tx) => tx.encode_payload(buf),
724            Transaction::EIP4844Transaction(tx) => tx.encode_payload(buf),
725            Transaction::EIP7702Transaction(tx) => tx.encode_payload(buf),
726            Transaction::PrivilegedL2Transaction(tx) => tx.encode_payload(buf),
727            Transaction::FeeTokenTransaction(tx) => tx.encode_payload(buf),
728        }
729    }
730}
731
732impl PayloadRLPEncode for LegacyTransaction {
733    fn encode_payload(&self, buf: &mut dyn bytes::BufMut) {
734        Encoder::new(buf)
735            .encode_field(&self.nonce)
736            .encode_field(&self.gas_price)
737            .encode_field(&self.gas)
738            .encode_field(&self.to)
739            .encode_field(&self.value)
740            .encode_field(&self.data)
741            .finish();
742    }
743}
744
745impl PayloadRLPEncode for EIP1559Transaction {
746    fn encode_payload(&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            .finish();
758    }
759}
760
761impl PayloadRLPEncode for EIP2930Transaction {
762    fn encode_payload(&self, buf: &mut dyn bytes::BufMut) {
763        Encoder::new(buf)
764            .encode_field(&self.chain_id)
765            .encode_field(&self.nonce)
766            .encode_field(&self.gas_price)
767            .encode_field(&self.gas_limit)
768            .encode_field(&self.to)
769            .encode_field(&self.value)
770            .encode_field(&self.data)
771            .encode_field(&self.access_list)
772            .finish();
773    }
774}
775
776impl PayloadRLPEncode for EIP4844Transaction {
777    fn encode_payload(&self, buf: &mut dyn bytes::BufMut) {
778        Encoder::new(buf)
779            .encode_field(&self.chain_id)
780            .encode_field(&self.nonce)
781            .encode_field(&self.max_priority_fee_per_gas)
782            .encode_field(&self.max_fee_per_gas)
783            .encode_field(&self.gas)
784            .encode_field(&self.to)
785            .encode_field(&self.value)
786            .encode_field(&self.data)
787            .encode_field(&self.access_list)
788            .encode_field(&self.max_fee_per_blob_gas)
789            .encode_field(&self.blob_versioned_hashes)
790            .finish();
791    }
792}
793
794impl PayloadRLPEncode for EIP7702Transaction {
795    fn encode_payload(&self, buf: &mut dyn bytes::BufMut) {
796        Encoder::new(buf)
797            .encode_field(&self.chain_id)
798            .encode_field(&self.nonce)
799            .encode_field(&self.max_priority_fee_per_gas)
800            .encode_field(&self.max_fee_per_gas)
801            .encode_field(&self.gas_limit)
802            .encode_field(&self.to)
803            .encode_field(&self.value)
804            .encode_field(&self.data)
805            .encode_field(&self.access_list)
806            .encode_field(&self.authorization_list)
807            .finish();
808    }
809}
810
811impl PayloadRLPEncode for PrivilegedL2Transaction {
812    fn encode_payload(&self, buf: &mut dyn bytes::BufMut) {
813        Encoder::new(buf)
814            .encode_field(&self.chain_id)
815            .encode_field(&self.nonce)
816            .encode_field(&self.max_priority_fee_per_gas)
817            .encode_field(&self.max_fee_per_gas)
818            .encode_field(&self.gas_limit)
819            .encode_field(&self.to)
820            .encode_field(&self.value)
821            .encode_field(&self.data)
822            .encode_field(&self.access_list)
823            .encode_field(&self.from)
824            .finish();
825    }
826}
827
828impl PayloadRLPEncode for FeeTokenTransaction {
829    fn encode_payload(&self, buf: &mut dyn bytes::BufMut) {
830        Encoder::new(buf)
831            .encode_field(&self.chain_id)
832            .encode_field(&self.nonce)
833            .encode_field(&self.max_priority_fee_per_gas)
834            .encode_field(&self.max_fee_per_gas)
835            .encode_field(&self.gas_limit)
836            .encode_field(&self.to)
837            .encode_field(&self.value)
838            .encode_field(&self.data)
839            .encode_field(&self.access_list)
840            .encode_field(&self.fee_token)
841            .finish();
842    }
843}
844
845impl RLPDecode for LegacyTransaction {
846    fn decode_unfinished(rlp: &[u8]) -> Result<(LegacyTransaction, &[u8]), RLPDecodeError> {
847        let decoder = Decoder::new(rlp)?;
848        let (nonce, decoder) = decoder.decode_field("nonce")?;
849        let (gas_price, decoder) = decoder.decode_field("gas_price")?;
850        let (gas, decoder) = decoder.decode_field("gas")?;
851        let (to, decoder) = decoder.decode_field("to")?;
852        let (value, decoder) = decoder.decode_field("value")?;
853        let (data, decoder) = decoder.decode_field("data")?;
854        let (v, decoder) = decoder.decode_field("v")?;
855        let (r, decoder) = decoder.decode_field("r")?;
856        let (s, decoder) = decoder.decode_field("s")?;
857        let inner_hash = OnceCell::new();
858        let sender_cache = OnceCell::new();
859
860        let tx = LegacyTransaction {
861            nonce,
862            gas_price,
863            gas,
864            to,
865            value,
866            data,
867            v,
868            r,
869            s,
870            inner_hash,
871            sender_cache,
872        };
873        Ok((tx, decoder.finish()?))
874    }
875}
876
877impl RLPDecode for EIP2930Transaction {
878    fn decode_unfinished(rlp: &[u8]) -> Result<(EIP2930Transaction, &[u8]), RLPDecodeError> {
879        let decoder = Decoder::new(rlp)?;
880        let (chain_id, decoder) = decoder.decode_field("chain_id")?;
881        let (nonce, decoder) = decoder.decode_field("nonce")?;
882        let (gas_price, decoder) = decoder.decode_field("gas_price")?;
883        let (gas_limit, decoder) = decoder.decode_field("gas_limit")?;
884        let (to, decoder) = decoder.decode_field("to")?;
885        let (value, decoder) = decoder.decode_field("value")?;
886        let (data, decoder) = decoder.decode_field("data")?;
887        let (access_list, decoder) = decoder.decode_field("access_list")?;
888        let (signature_y_parity, decoder) = decoder.decode_field("signature_y_parity")?;
889        let (signature_r, decoder) = decoder.decode_field("signature_r")?;
890        let (signature_s, decoder) = decoder.decode_field("signature_s")?;
891        let inner_hash = OnceCell::new();
892        let sender_cache = OnceCell::new();
893        let cached_canonical = OnceCell::new();
894
895        let tx = EIP2930Transaction {
896            chain_id,
897            nonce,
898            gas_price,
899            gas_limit,
900            to,
901            value,
902            data,
903            access_list,
904            signature_y_parity,
905            signature_r,
906            signature_s,
907            inner_hash,
908            sender_cache,
909            cached_canonical,
910        };
911        Ok((tx, decoder.finish()?))
912    }
913}
914
915impl RLPDecode for EIP1559Transaction {
916    fn decode_unfinished(rlp: &[u8]) -> Result<(EIP1559Transaction, &[u8]), RLPDecodeError> {
917        let decoder = Decoder::new(rlp)?;
918        let (chain_id, decoder) = decoder.decode_field("chain_id")?;
919        let (nonce, decoder) = decoder.decode_field("nonce")?;
920        let (max_priority_fee_per_gas, decoder) =
921            decoder.decode_field("max_priority_fee_per_gas")?;
922        let (max_fee_per_gas, decoder) = decoder.decode_field("max_fee_per_gas")?;
923        let (gas_limit, decoder) = decoder.decode_field("gas_limit")?;
924        let (to, decoder) = decoder.decode_field("to")?;
925        let (value, decoder) = decoder.decode_field("value")?;
926        let (data, decoder) = decoder.decode_field("data")?;
927        let (access_list, decoder) = decoder.decode_field("access_list")?;
928        let (signature_y_parity, decoder) = decoder.decode_field("signature_y_parity")?;
929        let (signature_r, decoder) = decoder.decode_field("signature_r")?;
930        let (signature_s, decoder) = decoder.decode_field("signature_s")?;
931        let inner_hash = OnceCell::new();
932        let sender_cache = OnceCell::new();
933        let cached_canonical = OnceCell::new();
934
935        let tx = EIP1559Transaction {
936            chain_id,
937            nonce,
938            max_priority_fee_per_gas,
939            max_fee_per_gas,
940            gas_limit,
941            to,
942            value,
943            data,
944            access_list,
945            signature_y_parity,
946            signature_r,
947            signature_s,
948            inner_hash,
949            sender_cache,
950            cached_canonical,
951        };
952        Ok((tx, decoder.finish()?))
953    }
954}
955
956impl RLPDecode for EIP4844Transaction {
957    fn decode_unfinished(rlp: &[u8]) -> Result<(EIP4844Transaction, &[u8]), RLPDecodeError> {
958        let decoder = Decoder::new(rlp)?;
959        let (chain_id, decoder) = decoder.decode_field("chain_id")?;
960        let (nonce, decoder) = decoder.decode_field("nonce")?;
961        let (max_priority_fee_per_gas, decoder) =
962            decoder.decode_field("max_priority_fee_per_gas")?;
963        let (max_fee_per_gas, decoder) = decoder.decode_field("max_fee_per_gas")?;
964        let (gas, decoder) = decoder.decode_field("gas")?;
965        let (to, decoder) = decoder.decode_field("to")?;
966        let (value, decoder) = decoder.decode_field("value")?;
967        let (data, decoder) = decoder.decode_field("data")?;
968        let (access_list, decoder) = decoder.decode_field("access_list")?;
969        let (max_fee_per_blob_gas, decoder) = decoder.decode_field("max_fee_per_blob_gas")?;
970        let (blob_versioned_hashes, decoder) = decoder.decode_field("blob_versioned_hashes")?;
971        let (signature_y_parity, decoder) = decoder.decode_field("signature_y_parity")?;
972        let (signature_r, decoder) = decoder.decode_field("signature_r")?;
973        let (signature_s, decoder) = decoder.decode_field("signature_s")?;
974        let inner_hash = OnceCell::new();
975        let sender_cache = OnceCell::new();
976        let cached_canonical = OnceCell::new();
977
978        let tx = EIP4844Transaction {
979            chain_id,
980            nonce,
981            max_priority_fee_per_gas,
982            max_fee_per_gas,
983            gas,
984            to,
985            value,
986            data,
987            access_list,
988            max_fee_per_blob_gas,
989            blob_versioned_hashes,
990            signature_y_parity,
991            signature_r,
992            signature_s,
993            inner_hash,
994            sender_cache,
995            cached_canonical,
996        };
997        Ok((tx, decoder.finish()?))
998    }
999}
1000
1001impl RLPDecode for EIP7702Transaction {
1002    fn decode_unfinished(rlp: &[u8]) -> Result<(EIP7702Transaction, &[u8]), RLPDecodeError> {
1003        let decoder = Decoder::new(rlp)?;
1004        let (chain_id, decoder) = decoder.decode_field("chain_id")?;
1005        let (nonce, decoder) = decoder.decode_field("nonce")?;
1006        let (max_priority_fee_per_gas, decoder) =
1007            decoder.decode_field("max_priority_fee_per_gas")?;
1008        let (max_fee_per_gas, decoder) = decoder.decode_field("max_fee_per_gas")?;
1009        let (gas_limit, decoder) = decoder.decode_field("gas_limit")?;
1010        let (to, decoder) = decoder.decode_field("to")?;
1011        let (value, decoder) = decoder.decode_field("value")?;
1012        let (data, decoder) = decoder.decode_field("data")?;
1013        let (access_list, decoder) = decoder.decode_field("access_list")?;
1014        let (authorization_list, decoder) = decoder.decode_field("authorization_list")?;
1015        let (signature_y_parity, decoder) = decoder.decode_field("signature_y_parity")?;
1016        let (signature_r, decoder) = decoder.decode_field("signature_r")?;
1017        let (signature_s, decoder) = decoder.decode_field("signature_s")?;
1018        let inner_hash = OnceCell::new();
1019        let sender_cache = OnceCell::new();
1020        let cached_canonical = OnceCell::new();
1021
1022        let tx = EIP7702Transaction {
1023            chain_id,
1024            nonce,
1025            max_priority_fee_per_gas,
1026            max_fee_per_gas,
1027            gas_limit,
1028            to,
1029            value,
1030            data,
1031            access_list,
1032            authorization_list,
1033            signature_y_parity,
1034            signature_r,
1035            signature_s,
1036            inner_hash,
1037            sender_cache,
1038            cached_canonical,
1039        };
1040        Ok((tx, decoder.finish()?))
1041    }
1042}
1043
1044impl RLPDecode for PrivilegedL2Transaction {
1045    fn decode_unfinished(rlp: &[u8]) -> Result<(PrivilegedL2Transaction, &[u8]), RLPDecodeError> {
1046        let decoder = Decoder::new(rlp)?;
1047        let (chain_id, decoder) = decoder.decode_field("chain_id")?;
1048        let (nonce, decoder) = decoder.decode_field("nonce")?;
1049        let (max_priority_fee_per_gas, decoder) =
1050            decoder.decode_field("max_priority_fee_per_gas")?;
1051        let (max_fee_per_gas, decoder) = decoder.decode_field("max_fee_per_gas")?;
1052        let (gas_limit, decoder) = decoder.decode_field::<u64>("gas_limit")?;
1053        let (to, decoder) = decoder.decode_field("to")?;
1054        let (value, decoder) = decoder.decode_field("value")?;
1055        let (data, decoder) = decoder.decode_field("data")?;
1056        let (access_list, decoder) = decoder.decode_field("access_list")?;
1057        let (from, decoder) = decoder.decode_field("from")?;
1058        let inner_hash = OnceCell::new();
1059        let sender_cache = OnceCell::new();
1060        let cached_canonical = OnceCell::new();
1061
1062        let tx = PrivilegedL2Transaction {
1063            chain_id,
1064            nonce,
1065            max_priority_fee_per_gas,
1066            max_fee_per_gas,
1067            gas_limit,
1068            to,
1069            value,
1070            data,
1071            access_list,
1072            from,
1073            inner_hash,
1074            sender_cache,
1075            cached_canonical,
1076        };
1077        Ok((tx, decoder.finish()?))
1078    }
1079}
1080
1081impl RLPDecode for FeeTokenTransaction {
1082    fn decode_unfinished(rlp: &[u8]) -> Result<(FeeTokenTransaction, &[u8]), RLPDecodeError> {
1083        let decoder = Decoder::new(rlp)?;
1084        let (chain_id, decoder) = decoder.decode_field("chain_id")?;
1085        let (nonce, decoder) = decoder.decode_field("nonce")?;
1086        let (max_priority_fee_per_gas, decoder) =
1087            decoder.decode_field("max_priority_fee_per_gas")?;
1088        let (max_fee_per_gas, decoder) = decoder.decode_field("max_fee_per_gas")?;
1089        let (gas_limit, decoder) = decoder.decode_field("gas_limit")?;
1090        let (to, decoder) = decoder.decode_field("to")?;
1091        let (value, decoder) = decoder.decode_field("value")?;
1092        let (data, decoder) = decoder.decode_field("data")?;
1093        let (access_list, decoder) = decoder.decode_field("access_list")?;
1094        let (fee_token, decoder) = decoder.decode_field("fee_token")?;
1095        let (signature_y_parity, decoder) = decoder.decode_field("signature_y_parity")?;
1096        let (signature_r, decoder) = decoder.decode_field("signature_r")?;
1097        let (signature_s, decoder) = decoder.decode_field("signature_s")?;
1098        let inner_hash = OnceCell::new();
1099        let sender_cache = OnceCell::new();
1100        let cached_canonical = OnceCell::new();
1101
1102        let tx = FeeTokenTransaction {
1103            chain_id,
1104            nonce,
1105            max_priority_fee_per_gas,
1106            max_fee_per_gas,
1107            gas_limit,
1108            to,
1109            value,
1110            data,
1111            access_list,
1112            fee_token,
1113            signature_y_parity,
1114            signature_r,
1115            signature_s,
1116            inner_hash,
1117            sender_cache,
1118            cached_canonical,
1119        };
1120        Ok((tx, decoder.finish()?))
1121    }
1122}
1123
1124impl Transaction {
1125    pub fn sender(&self, crypto: &dyn Crypto) -> Result<Address, CryptoError> {
1126        let sender_cache = match self {
1127            Transaction::LegacyTransaction(tx) => &tx.sender_cache,
1128            Transaction::EIP2930Transaction(tx) => &tx.sender_cache,
1129            Transaction::EIP1559Transaction(tx) => &tx.sender_cache,
1130            Transaction::EIP4844Transaction(tx) => &tx.sender_cache,
1131            Transaction::EIP7702Transaction(tx) => &tx.sender_cache,
1132            Transaction::PrivilegedL2Transaction(tx) => &tx.sender_cache,
1133            Transaction::FeeTokenTransaction(tx) => &tx.sender_cache,
1134        };
1135        sender_cache
1136            .get_or_try_init(|| {
1137                let tx_hash = self.hash();
1138                // Fast path: check process-level signer cache
1139                let mut cache = GLOBAL_SIGNER_CACHE
1140                    .lock()
1141                    .unwrap_or_else(|e| e.into_inner());
1142                if let Some(&addr) = cache.get(&tx_hash) {
1143                    return Ok(addr);
1144                }
1145                drop(cache);
1146                // Slow path: actual secp256k1 recovery
1147                let sender = self.compute_sender(crypto)?;
1148                // Store in global cache for future lookups (LRU evicts oldest on overflow)
1149                GLOBAL_SIGNER_CACHE
1150                    .lock()
1151                    .unwrap_or_else(|e| e.into_inner())
1152                    .put(tx_hash, sender);
1153                Ok(sender)
1154            })
1155            .copied()
1156    }
1157
1158    fn compute_sender(&self, crypto: &dyn Crypto) -> Result<Address, CryptoError> {
1159        let (buf, sig) = match self {
1160            Transaction::LegacyTransaction(tx) => {
1161                let v = u64::try_from(tx.v).map_err(|_| CryptoError::InvalidSignature)?;
1162                let signature_y_parity = match self.chain_id() {
1163                    Some(chain_id) => v.saturating_sub(35 + chain_id * 2) != 0,
1164                    None => v.saturating_sub(27) != 0,
1165                };
1166                let mut buf = vec![];
1167                match self.chain_id() {
1168                    None => Encoder::new(&mut buf)
1169                        .encode_field(&tx.nonce)
1170                        .encode_field(&tx.gas_price)
1171                        .encode_field(&tx.gas)
1172                        .encode_field(&tx.to)
1173                        .encode_field(&tx.value)
1174                        .encode_field(&tx.data)
1175                        .finish(),
1176                    Some(chain_id) => Encoder::new(&mut buf)
1177                        .encode_field(&tx.nonce)
1178                        .encode_field(&tx.gas_price)
1179                        .encode_field(&tx.gas)
1180                        .encode_field(&tx.to)
1181                        .encode_field(&tx.value)
1182                        .encode_field(&tx.data)
1183                        .encode_field(&chain_id)
1184                        .encode_field(&0u8)
1185                        .encode_field(&0u8)
1186                        .finish(),
1187                }
1188                let mut sig = [0u8; 65];
1189                sig[..32].copy_from_slice(&tx.r.to_big_endian());
1190                sig[32..64].copy_from_slice(&tx.s.to_big_endian());
1191                sig[64] = signature_y_parity as u8;
1192                (buf, sig)
1193            }
1194            Transaction::EIP2930Transaction(tx) => {
1195                let mut buf = vec![self.tx_type() as u8];
1196                Encoder::new(&mut buf)
1197                    .encode_field(&tx.chain_id)
1198                    .encode_field(&tx.nonce)
1199                    .encode_field(&tx.gas_price)
1200                    .encode_field(&tx.gas_limit)
1201                    .encode_field(&tx.to)
1202                    .encode_field(&tx.value)
1203                    .encode_field(&tx.data)
1204                    .encode_field(&tx.access_list)
1205                    .finish();
1206                let mut sig = [0u8; 65];
1207                sig[..32].copy_from_slice(&tx.signature_r.to_big_endian());
1208                sig[32..64].copy_from_slice(&tx.signature_s.to_big_endian());
1209                sig[64] = tx.signature_y_parity as u8;
1210                (buf, sig)
1211            }
1212            Transaction::EIP1559Transaction(tx) => {
1213                let mut buf = vec![self.tx_type() as u8];
1214                Encoder::new(&mut buf)
1215                    .encode_field(&tx.chain_id)
1216                    .encode_field(&tx.nonce)
1217                    .encode_field(&tx.max_priority_fee_per_gas)
1218                    .encode_field(&tx.max_fee_per_gas)
1219                    .encode_field(&tx.gas_limit)
1220                    .encode_field(&tx.to)
1221                    .encode_field(&tx.value)
1222                    .encode_field(&tx.data)
1223                    .encode_field(&tx.access_list)
1224                    .finish();
1225                let mut sig = [0u8; 65];
1226                sig[..32].copy_from_slice(&tx.signature_r.to_big_endian());
1227                sig[32..64].copy_from_slice(&tx.signature_s.to_big_endian());
1228                sig[64] = tx.signature_y_parity as u8;
1229                (buf, sig)
1230            }
1231            Transaction::EIP4844Transaction(tx) => {
1232                let mut buf = vec![self.tx_type() as u8];
1233                Encoder::new(&mut buf)
1234                    .encode_field(&tx.chain_id)
1235                    .encode_field(&tx.nonce)
1236                    .encode_field(&tx.max_priority_fee_per_gas)
1237                    .encode_field(&tx.max_fee_per_gas)
1238                    .encode_field(&tx.gas)
1239                    .encode_field(&tx.to)
1240                    .encode_field(&tx.value)
1241                    .encode_field(&tx.data)
1242                    .encode_field(&tx.access_list)
1243                    .encode_field(&tx.max_fee_per_blob_gas)
1244                    .encode_field(&tx.blob_versioned_hashes)
1245                    .finish();
1246                let mut sig = [0u8; 65];
1247                sig[..32].copy_from_slice(&tx.signature_r.to_big_endian());
1248                sig[32..64].copy_from_slice(&tx.signature_s.to_big_endian());
1249                sig[64] = tx.signature_y_parity as u8;
1250                (buf, sig)
1251            }
1252            Transaction::EIP7702Transaction(tx) => {
1253                let mut buf = vec![self.tx_type() as u8];
1254                Encoder::new(&mut buf)
1255                    .encode_field(&tx.chain_id)
1256                    .encode_field(&tx.nonce)
1257                    .encode_field(&tx.max_priority_fee_per_gas)
1258                    .encode_field(&tx.max_fee_per_gas)
1259                    .encode_field(&tx.gas_limit)
1260                    .encode_field(&tx.to)
1261                    .encode_field(&tx.value)
1262                    .encode_field(&tx.data)
1263                    .encode_field(&tx.access_list)
1264                    .encode_field(&tx.authorization_list)
1265                    .finish();
1266                let mut sig = [0u8; 65];
1267                sig[..32].copy_from_slice(&tx.signature_r.to_big_endian());
1268                sig[32..64].copy_from_slice(&tx.signature_s.to_big_endian());
1269                sig[64] = tx.signature_y_parity as u8;
1270                (buf, sig)
1271            }
1272            Transaction::PrivilegedL2Transaction(tx) => return Ok(tx.from),
1273            Transaction::FeeTokenTransaction(tx) => {
1274                let mut buf = vec![self.tx_type() as u8];
1275                Encoder::new(&mut buf)
1276                    .encode_field(&tx.chain_id)
1277                    .encode_field(&tx.nonce)
1278                    .encode_field(&tx.max_priority_fee_per_gas)
1279                    .encode_field(&tx.max_fee_per_gas)
1280                    .encode_field(&tx.gas_limit)
1281                    .encode_field(&tx.to)
1282                    .encode_field(&tx.value)
1283                    .encode_field(&tx.data)
1284                    .encode_field(&tx.access_list)
1285                    .encode_field(&tx.fee_token)
1286                    .finish();
1287                let mut sig = [0u8; 65];
1288                sig[..32].copy_from_slice(&tx.signature_r.to_big_endian());
1289                sig[32..64].copy_from_slice(&tx.signature_s.to_big_endian());
1290                sig[64] = tx.signature_y_parity as u8;
1291                (buf, sig)
1292            }
1293        };
1294        let msg = keccak(&buf).to_fixed_bytes();
1295        crypto.recover_signer(&sig, &msg)
1296    }
1297
1298    pub fn gas_limit(&self) -> u64 {
1299        match self {
1300            Transaction::LegacyTransaction(tx) => tx.gas,
1301            Transaction::EIP2930Transaction(tx) => tx.gas_limit,
1302            Transaction::EIP1559Transaction(tx) => tx.gas_limit,
1303            Transaction::EIP7702Transaction(tx) => tx.gas_limit,
1304            Transaction::EIP4844Transaction(tx) => tx.gas,
1305            Transaction::PrivilegedL2Transaction(tx) => tx.gas_limit,
1306            Transaction::FeeTokenTransaction(tx) => tx.gas_limit,
1307        }
1308    }
1309
1310    //TODO: It's not very correct to return gas price for legacy and eip-2930 txs but return the max fee per gas for the others, make necessary changes for it to be technically correct.
1311    pub fn gas_price(&self) -> U256 {
1312        match self {
1313            Transaction::LegacyTransaction(tx) => tx.gas_price,
1314            Transaction::EIP2930Transaction(tx) => tx.gas_price,
1315            Transaction::EIP1559Transaction(tx) => U256::from(tx.max_fee_per_gas),
1316            Transaction::EIP7702Transaction(tx) => U256::from(tx.max_fee_per_gas),
1317            Transaction::EIP4844Transaction(tx) => U256::from(tx.max_fee_per_gas),
1318            Transaction::PrivilegedL2Transaction(tx) => U256::from(tx.max_fee_per_gas),
1319            Transaction::FeeTokenTransaction(tx) => U256::from(tx.max_fee_per_gas),
1320        }
1321    }
1322
1323    pub fn to(&self) -> TxKind {
1324        match self {
1325            Transaction::LegacyTransaction(tx) => tx.to.clone(),
1326            Transaction::EIP2930Transaction(tx) => tx.to.clone(),
1327            Transaction::EIP1559Transaction(tx) => tx.to.clone(),
1328            Transaction::EIP4844Transaction(tx) => TxKind::Call(tx.to),
1329            Transaction::EIP7702Transaction(tx) => TxKind::Call(tx.to),
1330            Transaction::PrivilegedL2Transaction(tx) => tx.to.clone(),
1331            Transaction::FeeTokenTransaction(tx) => tx.to.clone(),
1332        }
1333    }
1334
1335    pub fn value(&self) -> U256 {
1336        match self {
1337            Transaction::LegacyTransaction(tx) => tx.value,
1338            Transaction::EIP2930Transaction(tx) => tx.value,
1339            Transaction::EIP1559Transaction(tx) => tx.value,
1340            Transaction::EIP4844Transaction(tx) => tx.value,
1341            Transaction::EIP7702Transaction(tx) => tx.value,
1342            Transaction::PrivilegedL2Transaction(tx) => tx.value,
1343            Transaction::FeeTokenTransaction(tx) => tx.value,
1344        }
1345    }
1346
1347    pub fn max_priority_fee(&self) -> Option<u64> {
1348        match self {
1349            Transaction::LegacyTransaction(_tx) => None,
1350            Transaction::EIP2930Transaction(_tx) => None,
1351            Transaction::EIP1559Transaction(tx) => Some(tx.max_priority_fee_per_gas),
1352            Transaction::EIP4844Transaction(tx) => Some(tx.max_priority_fee_per_gas),
1353            Transaction::EIP7702Transaction(tx) => Some(tx.max_priority_fee_per_gas),
1354            Transaction::PrivilegedL2Transaction(tx) => Some(tx.max_priority_fee_per_gas),
1355            Transaction::FeeTokenTransaction(tx) => Some(tx.max_priority_fee_per_gas),
1356        }
1357    }
1358
1359    pub fn chain_id(&self) -> Option<u64> {
1360        match self {
1361            Transaction::LegacyTransaction(tx) => derive_legacy_chain_id(tx.v),
1362            Transaction::EIP2930Transaction(tx) => Some(tx.chain_id),
1363            Transaction::EIP1559Transaction(tx) => Some(tx.chain_id),
1364            Transaction::EIP4844Transaction(tx) => Some(tx.chain_id),
1365            Transaction::EIP7702Transaction(tx) => Some(tx.chain_id),
1366            Transaction::PrivilegedL2Transaction(tx) => Some(tx.chain_id),
1367            Transaction::FeeTokenTransaction(tx) => Some(tx.chain_id),
1368        }
1369    }
1370
1371    pub fn access_list(&self) -> &AccessList {
1372        static EMPTY_ACCESS_LIST: AccessList = Vec::new();
1373        match self {
1374            Transaction::LegacyTransaction(_tx) => &EMPTY_ACCESS_LIST,
1375            Transaction::EIP2930Transaction(tx) => &tx.access_list,
1376            Transaction::EIP1559Transaction(tx) => &tx.access_list,
1377            Transaction::EIP4844Transaction(tx) => &tx.access_list,
1378            Transaction::EIP7702Transaction(tx) => &tx.access_list,
1379            Transaction::PrivilegedL2Transaction(tx) => &tx.access_list,
1380            Transaction::FeeTokenTransaction(tx) => &tx.access_list,
1381        }
1382    }
1383    pub fn authorization_list(&self) -> Option<&AuthorizationList> {
1384        match self {
1385            Transaction::LegacyTransaction(_) => None,
1386            Transaction::EIP2930Transaction(_) => None,
1387            Transaction::EIP1559Transaction(_) => None,
1388            Transaction::EIP4844Transaction(_) => None,
1389            Transaction::EIP7702Transaction(tx) => Some(&tx.authorization_list),
1390            Transaction::PrivilegedL2Transaction(_) => None,
1391            Transaction::FeeTokenTransaction(_) => None,
1392        }
1393    }
1394
1395    pub fn nonce(&self) -> u64 {
1396        match self {
1397            Transaction::LegacyTransaction(tx) => tx.nonce,
1398            Transaction::EIP2930Transaction(tx) => tx.nonce,
1399            Transaction::EIP1559Transaction(tx) => tx.nonce,
1400            Transaction::EIP4844Transaction(tx) => tx.nonce,
1401            Transaction::EIP7702Transaction(tx) => tx.nonce,
1402            Transaction::PrivilegedL2Transaction(tx) => tx.nonce,
1403            Transaction::FeeTokenTransaction(tx) => tx.nonce,
1404        }
1405    }
1406
1407    pub fn data(&self) -> &Bytes {
1408        match self {
1409            Transaction::LegacyTransaction(tx) => &tx.data,
1410            Transaction::EIP2930Transaction(tx) => &tx.data,
1411            Transaction::EIP1559Transaction(tx) => &tx.data,
1412            Transaction::EIP4844Transaction(tx) => &tx.data,
1413            Transaction::EIP7702Transaction(tx) => &tx.data,
1414            Transaction::PrivilegedL2Transaction(tx) => &tx.data,
1415            Transaction::FeeTokenTransaction(tx) => &tx.data,
1416        }
1417    }
1418
1419    pub fn blob_versioned_hashes(&self) -> Vec<H256> {
1420        match self {
1421            Transaction::LegacyTransaction(_) => Vec::new(),
1422            Transaction::EIP2930Transaction(_) => Vec::new(),
1423            Transaction::EIP1559Transaction(_) => Vec::new(),
1424            Transaction::EIP4844Transaction(tx) => tx.blob_versioned_hashes.clone(),
1425            Transaction::EIP7702Transaction(_) => Vec::new(),
1426            Transaction::PrivilegedL2Transaction(_) => Vec::new(),
1427            Transaction::FeeTokenTransaction(_) => Vec::new(),
1428        }
1429    }
1430
1431    pub fn max_fee_per_blob_gas(&self) -> Option<U256> {
1432        match self {
1433            Transaction::LegacyTransaction(_) => None,
1434            Transaction::EIP2930Transaction(_) => None,
1435            Transaction::EIP1559Transaction(_) => None,
1436            Transaction::EIP4844Transaction(tx) => Some(tx.max_fee_per_blob_gas),
1437            Transaction::EIP7702Transaction(_) => None,
1438            Transaction::PrivilegedL2Transaction(_) => None,
1439            Transaction::FeeTokenTransaction(_) => None,
1440        }
1441    }
1442
1443    pub fn is_contract_creation(&self) -> bool {
1444        match &self {
1445            Transaction::LegacyTransaction(t) => matches!(t.to, TxKind::Create),
1446            Transaction::EIP2930Transaction(t) => matches!(t.to, TxKind::Create),
1447            Transaction::EIP1559Transaction(t) => matches!(t.to, TxKind::Create),
1448            Transaction::EIP4844Transaction(_) => false,
1449            Transaction::EIP7702Transaction(_) => false,
1450            Transaction::PrivilegedL2Transaction(t) => matches!(t.to, TxKind::Create),
1451            Transaction::FeeTokenTransaction(t) => matches!(t.to, TxKind::Create),
1452        }
1453    }
1454
1455    pub fn is_privileged(&self) -> bool {
1456        matches!(self, Transaction::PrivilegedL2Transaction(_))
1457    }
1458
1459    pub fn max_fee_per_gas(&self) -> Option<u64> {
1460        match self {
1461            Transaction::LegacyTransaction(_tx) => None,
1462            Transaction::EIP2930Transaction(_tx) => None,
1463            Transaction::EIP1559Transaction(tx) => Some(tx.max_fee_per_gas),
1464            Transaction::EIP4844Transaction(tx) => Some(tx.max_fee_per_gas),
1465            Transaction::EIP7702Transaction(tx) => Some(tx.max_fee_per_gas),
1466            Transaction::PrivilegedL2Transaction(tx) => Some(tx.max_fee_per_gas),
1467            Transaction::FeeTokenTransaction(tx) => Some(tx.max_fee_per_gas),
1468        }
1469    }
1470
1471    fn compute_hash(&self) -> H256 {
1472        if let Transaction::PrivilegedL2Transaction(tx) = self {
1473            return tx.get_privileged_hash().unwrap_or_default();
1474        }
1475        crate::utils::keccak(self.encode_canonical_to_vec())
1476    }
1477
1478    pub fn hash(&self) -> H256 {
1479        let inner_hash = match self {
1480            Transaction::LegacyTransaction(tx) => &tx.inner_hash,
1481            Transaction::EIP2930Transaction(tx) => &tx.inner_hash,
1482            Transaction::EIP1559Transaction(tx) => &tx.inner_hash,
1483            Transaction::EIP4844Transaction(tx) => &tx.inner_hash,
1484            Transaction::EIP7702Transaction(tx) => &tx.inner_hash,
1485            Transaction::PrivilegedL2Transaction(tx) => &tx.inner_hash,
1486            Transaction::FeeTokenTransaction(tx) => &tx.inner_hash,
1487        };
1488
1489        *inner_hash.get_or_init(|| self.compute_hash())
1490    }
1491
1492    pub fn gas_tip_cap(&self) -> U256 {
1493        self.max_priority_fee()
1494            .map(U256::from)
1495            .unwrap_or_else(|| self.gas_price())
1496    }
1497
1498    pub fn gas_fee_cap(&self) -> U256 {
1499        self.max_fee_per_gas()
1500            .map(U256::from)
1501            .unwrap_or_else(|| self.gas_price())
1502    }
1503
1504    /// Returns the effective tip per gas for this transaction.
1505    /// Returns `None` if the transaction's fee cap is below the base fee (i.e. the
1506    /// transaction cannot pay for its inclusion).
1507    pub fn effective_gas_tip(&self, base_fee: Option<u64>) -> Option<U256> {
1508        let tip_cap = self.gas_tip_cap();
1509        let Some(base_fee) = base_fee else {
1510            return Some(tip_cap);
1511        };
1512        let base_fee = U256::from(base_fee);
1513        let fee_cap = self.gas_fee_cap();
1514        let tip = fee_cap.checked_sub(base_fee)?;
1515        Some(min(tip, tip_cap))
1516    }
1517
1518    /// Returns whether the transaction is replay-protected.
1519    /// For more information check out [EIP-155](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md)
1520    pub fn protected(&self) -> bool {
1521        match self {
1522            Transaction::LegacyTransaction(tx) if tx.v.bits() <= 8 => {
1523                let v = tx.v.low_u64();
1524                v != 27 && v != 28 && v != 1 && v != 0
1525            }
1526            _ => true,
1527        }
1528    }
1529}
1530
1531fn derive_legacy_chain_id(v: U256) -> Option<u64> {
1532    let v = u64::try_from(v).ok()?;
1533    // EIP-155 encodes the chain id as `v = chain_id * 2 + 35` (or 36), so any
1534    // replay-protected `v` is >= 35. Pre-EIP-155 txs use v=27/28, and malformed
1535    // signatures (e.g. v=0 from an unsigned IL transaction) are < 35 too; none
1536    // carry a chain id. Guard the subtraction to avoid an underflow panic.
1537    if v < 35 { None } else { Some((v - 35) / 2) }
1538}
1539
1540impl TxType {
1541    pub fn from_u8(value: u8) -> Option<Self> {
1542        match value {
1543            0x00 => Some(Self::Legacy),
1544            0x01 => Some(Self::EIP2930),
1545            0x02 => Some(Self::EIP1559),
1546            0x03 => Some(Self::EIP4844),
1547            0x04 => Some(Self::EIP7702),
1548            0x7d => Some(Self::FeeToken),
1549            0x7e => Some(Self::Privileged),
1550            _ => None,
1551        }
1552    }
1553
1554    /// Transaction types that only exist on the L2 rollup and must never appear in
1555    /// an L1 block (`FeeToken` 0x7d, `Privileged` 0x7e). Privileged transactions in
1556    /// particular take their sender from an unsigned, caller-chosen `from` field.
1557    ///
1558    /// This match is intentionally exhaustive (no wildcard arm): adding a new
1559    /// `TxType` variant will not compile until it is explicitly classified here,
1560    /// so an L2-only type can never be silently accepted on L1 by omission.
1561    pub fn is_l2_only(self) -> bool {
1562        match self {
1563            Self::Legacy | Self::EIP2930 | Self::EIP1559 | Self::EIP4844 | Self::EIP7702 => false,
1564            Self::FeeToken | Self::Privileged => true,
1565        }
1566    }
1567}
1568
1569impl PrivilegedL2Transaction {
1570    /// Returns the formatted hash of the privileged transaction,
1571    /// or None if the transaction is not a privileged transaction.
1572    /// The hash is computed as keccak256(chain_id || from || to || transaction_id  || value || gas_limit || keccak256(calldata))
1573    pub fn get_privileged_hash(&self) -> Option<H256> {
1574        // Should this function be changed?
1575        let to = match self.to {
1576            TxKind::Call(to) => to,
1577            _ => return None,
1578        };
1579
1580        let value = self.value.to_big_endian();
1581
1582        // The nonce should be a U256,
1583        // in solidity the transactionId is a U256.
1584        let u256_nonce = U256::from(self.nonce);
1585        let nonce = u256_nonce.to_big_endian();
1586
1587        Some(crate::utils::keccak(
1588            [
1589                U256::from(self.chain_id).to_big_endian().as_ref(),
1590                self.from.as_bytes(),
1591                to.as_bytes(),
1592                &nonce,
1593                &value,
1594                &U256::from(self.gas_limit).to_big_endian(),
1595                keccak(&self.data).as_bytes(),
1596            ]
1597            .concat(),
1598        ))
1599    }
1600}
1601
1602#[derive(Clone, Debug, PartialEq, Eq, Default, RSerialize, RDeserialize, Archive)]
1603pub struct FeeTokenTransaction {
1604    pub chain_id: u64,
1605    pub nonce: u64,
1606    pub max_priority_fee_per_gas: u64,
1607    pub max_fee_per_gas: u64,
1608    pub gas_limit: u64,
1609    pub to: TxKind,
1610    #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
1611    pub value: U256,
1612    #[rkyv(with=crate::rkyv_utils::BytesWrapper)]
1613    pub data: Bytes,
1614    #[rkyv(with=rkyv::with::Map<crate::rkyv_utils::AccessListItemWrapper>)]
1615    pub access_list: AccessList,
1616    #[rkyv(with=crate::rkyv_utils::H160Wrapper)]
1617    pub fee_token: Address,
1618    pub signature_y_parity: bool,
1619    #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
1620    pub signature_r: U256,
1621    #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
1622    pub signature_s: U256,
1623    #[rkyv(with=rkyv::with::Skip)]
1624    pub inner_hash: OnceCell<H256>,
1625    #[rkyv(with=rkyv::with::Skip)]
1626    pub sender_cache: OnceCell<Address>,
1627    #[rkyv(with=rkyv::with::Skip)]
1628    pub cached_canonical: OnceCell<Vec<u8>>,
1629}
1630
1631/// Canonical Transaction Encoding
1632/// Based on [EIP-2718]
1633/// Transactions can be encoded in the following formats:
1634/// A) `TransactionType || Transaction` (Where Transaction type is an 8-bit number between 0 and 0x7f, and Transaction is an rlp encoded transaction of type TransactionType)
1635/// B) `LegacyTransaction` (An rlp encoded LegacyTransaction)
1636mod canonic_encoding {
1637    use super::*;
1638
1639    impl Transaction {
1640        /// Decodes a single transaction in canonical format
1641        /// Based on [EIP-2718]
1642        /// Transactions can be encoded in the following formats:
1643        /// A) `TransactionType || Transaction` (Where Transaction type is an 8-bit number between 0 and 0x7f, and Transaction is an rlp encoded transaction of type TransactionType)
1644        /// B) `LegacyTransaction` (An rlp encoded LegacyTransaction)
1645        pub fn decode_canonical(bytes: &[u8]) -> Result<Self, RLPDecodeError> {
1646            // Look at the first byte to check if it corresponds to a TransactionType
1647            match bytes.first() {
1648                // First byte is a valid TransactionType
1649                Some(tx_type) if *tx_type < 0x7f => {
1650                    // Decode tx based on type
1651                    let tx_bytes = &bytes[1..];
1652                    match *tx_type {
1653                        // Legacy
1654                        0x0 => {
1655                            LegacyTransaction::decode(tx_bytes).map(Transaction::LegacyTransaction)
1656                        } // TODO: check if this is a real case scenario
1657                        // EIP2930
1658                        0x1 => EIP2930Transaction::decode(tx_bytes)
1659                            .map(Transaction::EIP2930Transaction),
1660                        // EIP1559
1661                        0x2 => EIP1559Transaction::decode(tx_bytes)
1662                            .map(Transaction::EIP1559Transaction),
1663                        // EIP4844
1664                        0x3 => EIP4844Transaction::decode(tx_bytes)
1665                            .map(Transaction::EIP4844Transaction),
1666                        // EIP7702
1667                        0x4 => EIP7702Transaction::decode(tx_bytes)
1668                            .map(Transaction::EIP7702Transaction),
1669                        // FeeTokenTransaction
1670                        0x7d => FeeTokenTransaction::decode(tx_bytes)
1671                            .map(Transaction::FeeTokenTransaction),
1672                        // PrivilegedL2Transaction
1673                        0x7e => PrivilegedL2Transaction::decode(tx_bytes)
1674                            .map(Transaction::PrivilegedL2Transaction),
1675                        ty => Err(RLPDecodeError::Custom(format!(
1676                            "Invalid transaction type: {ty}"
1677                        ))),
1678                    }
1679                }
1680                // LegacyTransaction
1681                _ => LegacyTransaction::decode(bytes).map(Transaction::LegacyTransaction),
1682            }
1683        }
1684
1685        /// Encodes a transaction in canonical format
1686        /// Based on [EIP-2718]
1687        /// Transactions can be encoded in the following formats:
1688        /// A) `TransactionType || Transaction` (Where Transaction type is an 8-bit number between 0 and 0x7f, and Transaction is an rlp encoded transaction of type TransactionType)
1689        /// B) `LegacyTransaction` (An rlp encoded LegacyTransaction)
1690        pub fn encode_canonical(&self, buf: &mut dyn bytes::BufMut) {
1691            match self {
1692                // Legacy transactions don't have a prefix
1693                Transaction::LegacyTransaction(_) => {}
1694                _ => buf.put_u8(self.tx_type() as u8),
1695            }
1696            match self {
1697                Transaction::LegacyTransaction(t) => t.encode(buf),
1698                Transaction::EIP2930Transaction(t) => t.encode(buf),
1699                Transaction::EIP1559Transaction(t) => t.encode(buf),
1700                Transaction::EIP4844Transaction(t) => t.encode(buf),
1701                Transaction::EIP7702Transaction(t) => t.encode(buf),
1702                Transaction::FeeTokenTransaction(t) => t.encode(buf),
1703                Transaction::PrivilegedL2Transaction(t) => t.encode(buf),
1704            };
1705        }
1706
1707        /// Encodes a transaction in canonical format into a newly created buffer
1708        /// Based on [EIP-2718]
1709        /// Transactions can be encoded in the following formats:
1710        /// A) `TransactionType || Transaction` (Where Transaction type is an 8-bit number between 0 and 0x7f, and Transaction is an rlp encoded transaction of type TransactionType)
1711        /// B) `LegacyTransaction` (An rlp encoded LegacyTransaction)
1712        pub fn encode_canonical_to_vec(&self) -> Vec<u8> {
1713            if let Some(cell) = self.cached_canonical_cell() {
1714                return cell
1715                    .get_or_init(|| {
1716                        let mut buf = Vec::new();
1717                        self.encode_canonical(&mut buf);
1718                        buf
1719                    })
1720                    .clone();
1721            }
1722            let mut buf = Vec::new();
1723            self.encode_canonical(&mut buf);
1724            buf
1725        }
1726
1727        /// Canonical-encoded length without allocating a buffer. Counts the
1728        /// 1-byte type prefix for typed txs (EIP-2718) plus the inner RLP
1729        /// payload length. Use this when only the size is needed (e.g.
1730        /// admission-time size caps) to avoid `encode_canonical_to_vec().len()`.
1731        pub fn encode_canonical_len(&self) -> usize {
1732            let prefix_len = match self {
1733                Transaction::LegacyTransaction(_) => 0,
1734                _ => 1,
1735            };
1736            let inner_len = match self {
1737                Transaction::LegacyTransaction(t) => t.length(),
1738                Transaction::EIP2930Transaction(t) => t.length(),
1739                Transaction::EIP1559Transaction(t) => t.length(),
1740                Transaction::EIP4844Transaction(t) => t.length(),
1741                Transaction::EIP7702Transaction(t) => t.length(),
1742                Transaction::FeeTokenTransaction(t) => t.length(),
1743                Transaction::PrivilegedL2Transaction(t) => t.length(),
1744            };
1745            prefix_len + inner_len
1746        }
1747    }
1748
1749    impl P2PTransaction {
1750        pub fn tx_type(&self) -> TxType {
1751            match self {
1752                P2PTransaction::LegacyTransaction(_) => TxType::Legacy,
1753                P2PTransaction::EIP2930Transaction(_) => TxType::EIP2930,
1754                P2PTransaction::EIP1559Transaction(_) => TxType::EIP1559,
1755                P2PTransaction::EIP4844TransactionWithBlobs(_) => TxType::EIP4844,
1756                P2PTransaction::EIP7702Transaction(_) => TxType::EIP7702,
1757                P2PTransaction::FeeTokenTransaction(_) => TxType::FeeToken,
1758            }
1759        }
1760
1761        pub fn encode_canonical(&self, buf: &mut dyn bytes::BufMut) {
1762            match self {
1763                // Legacy transactions don't have a prefix
1764                P2PTransaction::LegacyTransaction(_) => {}
1765                _ => buf.put_u8(self.tx_type() as u8),
1766            }
1767            match self {
1768                P2PTransaction::LegacyTransaction(t) => t.encode(buf),
1769                P2PTransaction::EIP2930Transaction(t) => t.encode(buf),
1770                P2PTransaction::EIP1559Transaction(t) => t.encode(buf),
1771                P2PTransaction::EIP4844TransactionWithBlobs(t) => t.encode(buf),
1772                P2PTransaction::EIP7702Transaction(t) => t.encode(buf),
1773                P2PTransaction::FeeTokenTransaction(t) => t.encode(buf),
1774            };
1775        }
1776
1777        pub fn encode_canonical_to_vec(&self) -> Vec<u8> {
1778            let mut buf = Vec::new();
1779            self.encode_canonical(&mut buf);
1780            buf
1781        }
1782
1783        pub fn compute_hash(&self) -> H256 {
1784            match self {
1785                P2PTransaction::LegacyTransaction(t) => {
1786                    Transaction::LegacyTransaction(t.clone()).compute_hash()
1787                }
1788                P2PTransaction::EIP2930Transaction(t) => {
1789                    Transaction::EIP2930Transaction(t.clone()).compute_hash()
1790                }
1791                P2PTransaction::EIP1559Transaction(t) => {
1792                    Transaction::EIP1559Transaction(t.clone()).compute_hash()
1793                }
1794                P2PTransaction::EIP4844TransactionWithBlobs(t) => {
1795                    Transaction::EIP4844Transaction(t.tx.clone()).compute_hash()
1796                }
1797                P2PTransaction::EIP7702Transaction(t) => {
1798                    Transaction::EIP7702Transaction(t.clone()).compute_hash()
1799                }
1800                P2PTransaction::FeeTokenTransaction(t) => {
1801                    Transaction::FeeTokenTransaction(t.clone()).compute_hash()
1802                }
1803            }
1804        }
1805    }
1806}
1807
1808// Serialization
1809// This is used for RPC messaging and passing data into a RISC-V zkVM
1810
1811mod serde_impl {
1812    use ethereum_types::H160;
1813    use serde::Deserialize;
1814    use serde::{Deserializer, de::Error};
1815    use serde_json::Value;
1816    use std::{collections::HashMap, str::FromStr};
1817
1818    #[cfg(feature = "c-kzg")]
1819    use crate::types::BYTES_PER_BLOB;
1820    use crate::types::{AccessListItem, AuthorizationTuple, BlobsBundleError};
1821
1822    use super::*;
1823
1824    impl Serialize for TxKind {
1825        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1826        where
1827            S: serde::Serializer,
1828        {
1829            match self {
1830                TxKind::Call(address) => serializer.serialize_str(&format!("{address:#x}")),
1831                TxKind::Create => serializer.serialize_none(),
1832            }
1833        }
1834    }
1835
1836    impl<'de> Deserialize<'de> for TxKind {
1837        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1838        where
1839            D: serde::Deserializer<'de>,
1840        {
1841            let str_option = Option::<String>::deserialize(deserializer)?;
1842            match str_option {
1843                Some(str) if !str.is_empty() => Ok(TxKind::Call(
1844                    Address::from_str(str.trim_start_matches("0x")).map_err(|_| {
1845                        serde::de::Error::custom(format!("Failed to deserialize hex value {str}"))
1846                    })?,
1847                )),
1848                _ => Ok(TxKind::Create),
1849            }
1850        }
1851    }
1852
1853    impl Serialize for TxType {
1854        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1855        where
1856            S: serde::Serializer,
1857        {
1858            serializer.serialize_str(&format!("{:#x}", *self as u8))
1859        }
1860    }
1861
1862    impl<'de> Deserialize<'de> for TxType {
1863        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1864        where
1865            D: serde::Deserializer<'de>,
1866        {
1867            let str = String::deserialize(deserializer)?;
1868            let tx_num = u8::from_str_radix(str.trim_start_matches("0x"), 16).map_err(|_| {
1869                serde::de::Error::custom(format!("Failed to deserialize hex value {str}"))
1870            })?;
1871            TxType::from_u8(tx_num).ok_or_else(|| {
1872                serde::de::Error::custom(format!("Invalid transaction type {tx_num}"))
1873            })
1874        }
1875    }
1876
1877    #[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
1878    #[serde(rename_all = "camelCase")]
1879    pub struct AccessListEntry {
1880        pub address: Address,
1881        pub storage_keys: Vec<H256>,
1882    }
1883
1884    impl From<&AccessListItem> for AccessListEntry {
1885        fn from(value: &AccessListItem) -> AccessListEntry {
1886            AccessListEntry {
1887                address: value.0,
1888                storage_keys: value.1.clone(),
1889            }
1890        }
1891    }
1892
1893    #[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
1894    #[serde(rename_all = "camelCase")]
1895    pub struct AuthorizationTupleEntry {
1896        pub chain_id: U256,
1897        pub address: Address,
1898        #[serde(default, with = "crate::serde_utils::u64::hex_str")]
1899        pub nonce: u64,
1900        pub y_parity: U256,
1901        pub r: U256,
1902        pub s: U256,
1903    }
1904
1905    impl From<&AuthorizationTuple> for AuthorizationTupleEntry {
1906        fn from(value: &AuthorizationTuple) -> AuthorizationTupleEntry {
1907            AuthorizationTupleEntry {
1908                chain_id: value.chain_id,
1909                address: value.address,
1910                nonce: value.nonce,
1911                y_parity: value.y_parity,
1912                r: value.r_signature,
1913                s: value.s_signature,
1914            }
1915        }
1916    }
1917
1918    impl From<AuthorizationTupleEntry> for AuthorizationTuple {
1919        fn from(entry: AuthorizationTupleEntry) -> AuthorizationTuple {
1920            AuthorizationTuple {
1921                chain_id: entry.chain_id,
1922                address: entry.address,
1923                nonce: entry.nonce,
1924                y_parity: entry.y_parity,
1925                r_signature: entry.r,
1926                s_signature: entry.s,
1927            }
1928        }
1929    }
1930
1931    impl Serialize for LegacyTransaction {
1932        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1933        where
1934            S: serde::Serializer,
1935        {
1936            let mut struct_serializer = serializer.serialize_struct("LegacyTransaction", 11)?;
1937            struct_serializer.serialize_field("type", &TxType::Legacy)?;
1938            struct_serializer.serialize_field("nonce", &format!("{:#x}", self.nonce))?;
1939            struct_serializer.serialize_field("to", &self.to)?;
1940            struct_serializer.serialize_field("gas", &format!("{:#x}", self.gas))?;
1941            struct_serializer.serialize_field("value", &self.value)?;
1942            struct_serializer.serialize_field("input", &format!("0x{:x}", self.data))?;
1943            struct_serializer.serialize_field("gasPrice", &format!("{:#x}", self.gas_price))?;
1944            struct_serializer.serialize_field(
1945                "chainId",
1946                &format!("{:#x}", derive_legacy_chain_id(self.v).unwrap_or_default()),
1947            )?;
1948            struct_serializer.serialize_field("v", &self.v)?;
1949            struct_serializer.serialize_field("r", &self.r)?;
1950            struct_serializer.serialize_field("s", &self.s)?;
1951            struct_serializer.end()
1952        }
1953    }
1954
1955    impl Serialize for EIP2930Transaction {
1956        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1957        where
1958            S: serde::Serializer,
1959        {
1960            let mut struct_serializer = serializer.serialize_struct("Eip2930Transaction", 12)?;
1961            struct_serializer.serialize_field("type", &TxType::EIP2930)?;
1962            struct_serializer.serialize_field("nonce", &format!("{:#x}", self.nonce))?;
1963            struct_serializer.serialize_field("to", &self.to)?;
1964            struct_serializer.serialize_field("gas", &format!("{:#x}", self.gas_limit))?;
1965            struct_serializer.serialize_field("value", &self.value)?;
1966            struct_serializer.serialize_field("input", &format!("0x{:x}", self.data))?;
1967            struct_serializer.serialize_field("gasPrice", &format!("{:#x}", self.gas_price))?;
1968            struct_serializer.serialize_field(
1969                "accessList",
1970                &self
1971                    .access_list
1972                    .iter()
1973                    .map(AccessListEntry::from)
1974                    .collect::<Vec<_>>(),
1975            )?;
1976            struct_serializer.serialize_field("chainId", &format!("{:#x}", self.chain_id))?;
1977            struct_serializer
1978                .serialize_field("yParity", &format!("{:#x}", self.signature_y_parity as u8))?;
1979            struct_serializer
1980                .serialize_field("v", &format!("{:#x}", self.signature_y_parity as u8))?; // added to match Hive tests
1981            struct_serializer.serialize_field("r", &self.signature_r)?;
1982            struct_serializer.serialize_field("s", &self.signature_s)?;
1983            struct_serializer.end()
1984        }
1985    }
1986
1987    impl Serialize for EIP1559Transaction {
1988        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1989        where
1990            S: serde::Serializer,
1991        {
1992            let mut struct_serializer = serializer.serialize_struct("Eip1559Transaction", 14)?;
1993            struct_serializer.serialize_field("type", &TxType::EIP1559)?;
1994            struct_serializer.serialize_field("nonce", &format!("{:#x}", self.nonce))?;
1995            struct_serializer.serialize_field("to", &self.to)?;
1996            struct_serializer.serialize_field("gas", &format!("{:#x}", self.gas_limit))?;
1997            struct_serializer.serialize_field("value", &self.value)?;
1998            struct_serializer.serialize_field("input", &format!("0x{:x}", self.data))?;
1999            struct_serializer.serialize_field(
2000                "maxPriorityFeePerGas",
2001                &format!("{:#x}", self.max_priority_fee_per_gas),
2002            )?;
2003            struct_serializer
2004                .serialize_field("maxFeePerGas", &format!("{:#x}", self.max_fee_per_gas))?;
2005            struct_serializer
2006                .serialize_field("gasPrice", &format!("{:#x}", self.max_fee_per_gas))?;
2007            struct_serializer.serialize_field(
2008                "accessList",
2009                &self
2010                    .access_list
2011                    .iter()
2012                    .map(AccessListEntry::from)
2013                    .collect::<Vec<_>>(),
2014            )?;
2015            struct_serializer.serialize_field("chainId", &format!("{:#x}", self.chain_id))?;
2016            struct_serializer
2017                .serialize_field("yParity", &format!("{:#x}", self.signature_y_parity as u8))?;
2018            struct_serializer
2019                .serialize_field("v", &format!("{:#x}", self.signature_y_parity as u8))?; // added to match Hive tests
2020            struct_serializer.serialize_field("r", &self.signature_r)?;
2021            struct_serializer.serialize_field("s", &self.signature_s)?;
2022            struct_serializer.end()
2023        }
2024    }
2025
2026    impl Serialize for EIP4844Transaction {
2027        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
2028        where
2029            S: serde::Serializer,
2030        {
2031            let mut struct_serializer = serializer.serialize_struct("Eip4844Transaction", 15)?;
2032            struct_serializer.serialize_field("type", &TxType::EIP4844)?;
2033            struct_serializer.serialize_field("nonce", &format!("{:#x}", self.nonce))?;
2034            struct_serializer.serialize_field("to", &self.to)?;
2035            struct_serializer.serialize_field("gas", &format!("{:#x}", self.gas))?;
2036            struct_serializer.serialize_field("value", &self.value)?;
2037            struct_serializer.serialize_field("input", &format!("0x{:x}", self.data))?;
2038            struct_serializer.serialize_field(
2039                "maxPriorityFeePerGas",
2040                &format!("{:#x}", self.max_priority_fee_per_gas),
2041            )?;
2042            struct_serializer
2043                .serialize_field("maxFeePerGas", &format!("{:#x}", self.max_fee_per_gas))?;
2044            struct_serializer
2045                .serialize_field("gasPrice", &format!("{:#x}", self.max_fee_per_gas))?;
2046            struct_serializer.serialize_field(
2047                "maxFeePerBlobGas",
2048                &format!("{:#x}", self.max_fee_per_blob_gas),
2049            )?;
2050            struct_serializer.serialize_field(
2051                "accessList",
2052                &self
2053                    .access_list
2054                    .iter()
2055                    .map(AccessListEntry::from)
2056                    .collect::<Vec<_>>(),
2057            )?;
2058            struct_serializer
2059                .serialize_field("blobVersionedHashes", &self.blob_versioned_hashes)?;
2060            struct_serializer.serialize_field("chainId", &format!("{:#x}", self.chain_id))?;
2061            struct_serializer
2062                .serialize_field("yParity", &format!("{:#x}", self.signature_y_parity as u8))?;
2063            struct_serializer
2064                .serialize_field("v", &format!("{:#x}", self.signature_y_parity as u8))?; // added to match Hive tests
2065            struct_serializer.serialize_field("r", &self.signature_r)?;
2066            struct_serializer.serialize_field("s", &self.signature_s)?;
2067            struct_serializer.end()
2068        }
2069    }
2070
2071    impl Serialize for EIP7702Transaction {
2072        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
2073        where
2074            S: serde::Serializer,
2075        {
2076            let mut struct_serializer = serializer.serialize_struct("Eip7702Transaction", 15)?;
2077            struct_serializer.serialize_field("type", &TxType::EIP7702)?;
2078            struct_serializer.serialize_field("nonce", &format!("{:#x}", self.nonce))?;
2079            struct_serializer.serialize_field("to", &self.to)?;
2080            struct_serializer.serialize_field("gas", &format!("{:#x}", self.gas_limit))?;
2081            struct_serializer.serialize_field("value", &self.value)?;
2082            struct_serializer.serialize_field("input", &format!("0x{:x}", self.data))?;
2083            struct_serializer.serialize_field(
2084                "maxPriorityFeePerGas",
2085                &format!("{:#x}", self.max_priority_fee_per_gas),
2086            )?;
2087            struct_serializer
2088                .serialize_field("maxFeePerGas", &format!("{:#x}", self.max_fee_per_gas))?;
2089            struct_serializer
2090                .serialize_field("gasPrice", &format!("{:#x}", self.max_fee_per_gas))?;
2091            struct_serializer.serialize_field(
2092                "accessList",
2093                &self
2094                    .access_list
2095                    .iter()
2096                    .map(AccessListEntry::from)
2097                    .collect::<Vec<_>>(),
2098            )?;
2099            struct_serializer.serialize_field(
2100                "authorizationList",
2101                &self
2102                    .authorization_list
2103                    .iter()
2104                    .map(AuthorizationTupleEntry::from)
2105                    .collect::<Vec<_>>(),
2106            )?;
2107            struct_serializer.serialize_field("chainId", &format!("{:#x}", self.chain_id))?;
2108            struct_serializer
2109                .serialize_field("yParity", &format!("{:#x}", self.signature_y_parity as u8))?;
2110            struct_serializer
2111                .serialize_field("v", &format!("{:#x}", self.signature_y_parity as u8))?; // added to match Hive tests
2112            struct_serializer.serialize_field("r", &self.signature_r)?;
2113            struct_serializer.serialize_field("s", &self.signature_s)?;
2114            struct_serializer.end()
2115        }
2116    }
2117
2118    impl Serialize for PrivilegedL2Transaction {
2119        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
2120        where
2121            S: serde::Serializer,
2122        {
2123            let mut struct_serializer = serializer.serialize_struct("Eip1559Transaction", 14)?;
2124            struct_serializer.serialize_field("type", &TxType::Privileged)?;
2125            struct_serializer.serialize_field("nonce", &format!("{:#x}", self.nonce))?;
2126            struct_serializer.serialize_field("to", &self.to)?;
2127            struct_serializer.serialize_field("gas", &format!("{:#x}", self.gas_limit))?;
2128            struct_serializer.serialize_field("value", &self.value)?;
2129            struct_serializer.serialize_field("input", &format!("0x{:x}", self.data))?;
2130            struct_serializer.serialize_field(
2131                "maxPriorityFeePerGas",
2132                &format!("{:#x}", self.max_priority_fee_per_gas),
2133            )?;
2134            struct_serializer
2135                .serialize_field("maxFeePerGas", &format!("{:#x}", self.max_fee_per_gas))?;
2136            struct_serializer
2137                .serialize_field("gasPrice", &format!("{:#x}", self.max_fee_per_gas))?;
2138            struct_serializer.serialize_field(
2139                "accessList",
2140                &self
2141                    .access_list
2142                    .iter()
2143                    .map(AccessListEntry::from)
2144                    .collect::<Vec<_>>(),
2145            )?;
2146            struct_serializer.serialize_field("chainId", &format!("{:#x}", self.chain_id))?;
2147            struct_serializer.serialize_field("sender", &self.from)?;
2148            struct_serializer.end()
2149        }
2150    }
2151
2152    impl Serialize for FeeTokenTransaction {
2153        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
2154        where
2155            S: serde::Serializer,
2156        {
2157            let mut struct_serializer = serializer.serialize_struct("FeeTokenTransaction", 15)?;
2158            struct_serializer.serialize_field("type", &TxType::FeeToken)?;
2159            struct_serializer.serialize_field("nonce", &format!("{:#x}", self.nonce))?;
2160            struct_serializer.serialize_field("to", &self.to)?;
2161            struct_serializer.serialize_field("gas", &format!("{:#x}", self.gas_limit))?;
2162            struct_serializer.serialize_field("value", &self.value)?;
2163            struct_serializer.serialize_field("input", &format!("0x{:x}", self.data))?;
2164            struct_serializer.serialize_field(
2165                "maxPriorityFeePerGas",
2166                &format!("{:#x}", self.max_priority_fee_per_gas),
2167            )?;
2168            struct_serializer
2169                .serialize_field("maxFeePerGas", &format!("{:#x}", self.max_fee_per_gas))?;
2170            struct_serializer
2171                .serialize_field("gasPrice", &format!("{:#x}", self.max_fee_per_gas))?;
2172            struct_serializer.serialize_field(
2173                "accessList",
2174                &self
2175                    .access_list
2176                    .iter()
2177                    .map(AccessListEntry::from)
2178                    .collect::<Vec<_>>(),
2179            )?;
2180            struct_serializer.serialize_field("feeToken", &format!("{:#x}", self.fee_token))?;
2181            struct_serializer.serialize_field("chainId", &format!("{:#x}", self.chain_id))?;
2182            struct_serializer
2183                .serialize_field("yParity", &format!("{:#x}", self.signature_y_parity as u8))?;
2184            struct_serializer
2185                .serialize_field("v", &format!("{:#x}", self.signature_y_parity as u8))?;
2186            struct_serializer.serialize_field("r", &self.signature_r)?;
2187            struct_serializer.serialize_field("s", &self.signature_s)?;
2188            struct_serializer.end()
2189        }
2190    }
2191
2192    impl<'de> Deserialize<'de> for Transaction {
2193        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2194        where
2195            D: serde::Deserializer<'de>,
2196        {
2197            let mut map = <HashMap<String, serde_json::Value>>::deserialize(deserializer)?;
2198            let tx_type =
2199                serde_json::from_value::<TxType>(map.remove("type").unwrap_or(Value::default()))
2200                    .unwrap_or_else(|_| {
2201                        if map.contains_key("tx_type") {
2202                            return TxType::Privileged;
2203                        }
2204                        TxType::EIP1559
2205                    });
2206
2207            let iter = map.into_iter();
2208            match tx_type {
2209                TxType::Legacy => {
2210                    LegacyTransaction::deserialize(serde::de::value::MapDeserializer::new(iter))
2211                        .map(Transaction::LegacyTransaction)
2212                        .map_err(|e| {
2213                            serde::de::Error::custom(format!("Couldn't Deserialize Legacy {e}"))
2214                        })
2215                }
2216                TxType::EIP2930 => {
2217                    EIP2930Transaction::deserialize(serde::de::value::MapDeserializer::new(iter))
2218                        .map(Transaction::EIP2930Transaction)
2219                        .map_err(|e| {
2220                            serde::de::Error::custom(format!("Couldn't Deserialize EIP2930 {e}"))
2221                        })
2222                }
2223                TxType::EIP1559 => {
2224                    EIP1559Transaction::deserialize(serde::de::value::MapDeserializer::new(iter))
2225                        .map(Transaction::EIP1559Transaction)
2226                        .map_err(|e| {
2227                            serde::de::Error::custom(format!("Couldn't Deserialize EIP1559 {e}"))
2228                        })
2229                }
2230                TxType::EIP4844 => {
2231                    EIP4844Transaction::deserialize(serde::de::value::MapDeserializer::new(iter))
2232                        .map(Transaction::EIP4844Transaction)
2233                        .map_err(|e| {
2234                            serde::de::Error::custom(format!("Couldn't Deserialize EIP4844 {e}"))
2235                        })
2236                }
2237                TxType::EIP7702 => {
2238                    EIP7702Transaction::deserialize(serde::de::value::MapDeserializer::new(iter))
2239                        .map(Transaction::EIP7702Transaction)
2240                        .map_err(|e| {
2241                            serde::de::Error::custom(format!("Couldn't Deserialize EIP7702 {e}"))
2242                        })
2243                }
2244                TxType::Privileged => PrivilegedL2Transaction::deserialize(
2245                    serde::de::value::MapDeserializer::new(iter),
2246                )
2247                .map(Transaction::PrivilegedL2Transaction)
2248                .map_err(|e| {
2249                    serde::de::Error::custom(format!("Couldn't Deserialize Privileged: {e}"))
2250                }),
2251                TxType::FeeToken => {
2252                    FeeTokenTransaction::deserialize(serde::de::value::MapDeserializer::new(iter))
2253                        .map(Transaction::FeeTokenTransaction)
2254                        .map_err(|e| {
2255                            serde::de::Error::custom(format!("Couldn't Deserialize FeeToken {e}"))
2256                        })
2257                }
2258            }
2259        }
2260    }
2261
2262    fn deserialize_input_field(
2263        map: &mut std::collections::HashMap<String, Value>,
2264    ) -> Result<Bytes, serde_json::Error> {
2265        let data_str: String = serde_json::from_value(
2266            map.remove("input")
2267                .ok_or_else(|| serde::de::Error::missing_field("input"))?,
2268        )
2269        .map_err(serde::de::Error::custom)?;
2270        if let Some(stripped) = data_str.strip_prefix("0x") {
2271            match hex::decode(stripped) {
2272                Ok(decoded_bytes) => Ok(Bytes::from(decoded_bytes)),
2273                Err(_) => Err(serde::de::Error::custom(
2274                    "Invalid hex format in 'input' field",
2275                ))?,
2276            }
2277        } else {
2278            Err(serde::de::Error::custom(
2279                "'input' field must start with '0x'",
2280            ))?
2281        }
2282    }
2283
2284    fn deserialize_field<'de, T, D>(
2285        map: &mut HashMap<String, serde_json::Value>,
2286        key: &str,
2287    ) -> Result<T, D::Error>
2288    where
2289        D: serde::Deserializer<'de>,
2290        T: serde::de::DeserializeOwned,
2291    {
2292        map.remove(key)
2293            .ok_or_else(|| D::Error::custom(format!("Missing field: {key}")))
2294            .and_then(|value| {
2295                serde_json::from_value(value).map_err(|err| D::Error::custom(err.to_string()))
2296            })
2297    }
2298
2299    fn deserialize_u64_field<'de, D>(
2300        map: &mut HashMap<String, serde_json::Value>,
2301        key: &str,
2302    ) -> Result<u64, D::Error>
2303    where
2304        D: serde::Deserializer<'de>,
2305    {
2306        let value = deserialize_field::<U256, D>(map, key)?;
2307        u64::try_from(value).map_err(|_| D::Error::custom(format!("{key} value overflows u64")))
2308    }
2309
2310    impl<'de> Deserialize<'de> for LegacyTransaction {
2311        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2312        where
2313            D: serde::Deserializer<'de>,
2314        {
2315            let mut map = <HashMap<String, serde_json::Value>>::deserialize(deserializer)?;
2316
2317            Ok(LegacyTransaction {
2318                nonce: deserialize_u64_field::<D>(&mut map, "nonce")?,
2319                gas_price: deserialize_field::<U256, D>(&mut map, "gasPrice")?,
2320                gas: deserialize_u64_field::<D>(&mut map, "gas")?,
2321                to: deserialize_field::<TxKind, D>(&mut map, "to")?,
2322                value: deserialize_field::<U256, D>(&mut map, "value")?,
2323                data: deserialize_input_field(&mut map).map_err(serde::de::Error::custom)?,
2324                v: deserialize_field::<U256, D>(&mut map, "v")?,
2325                r: deserialize_field::<U256, D>(&mut map, "r")?,
2326                s: deserialize_field::<U256, D>(&mut map, "s")?,
2327                ..Default::default()
2328            })
2329        }
2330    }
2331
2332    impl<'de> Deserialize<'de> for EIP2930Transaction {
2333        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2334        where
2335            D: serde::Deserializer<'de>,
2336        {
2337            let mut map = <HashMap<String, serde_json::Value>>::deserialize(deserializer)?;
2338
2339            Ok(EIP2930Transaction {
2340                chain_id: deserialize_u64_field::<D>(&mut map, "chainId")?,
2341                nonce: deserialize_u64_field::<D>(&mut map, "nonce")?,
2342                gas_price: deserialize_field::<U256, D>(&mut map, "gasPrice")?,
2343                gas_limit: deserialize_u64_field::<D>(&mut map, "gas")?,
2344                to: deserialize_field::<TxKind, D>(&mut map, "to")?,
2345                value: deserialize_field::<U256, D>(&mut map, "value")?,
2346                data: deserialize_input_field(&mut map).map_err(serde::de::Error::custom)?,
2347                access_list: deserialize_field::<Vec<AccessListEntry>, D>(&mut map, "accessList")?
2348                    .into_iter()
2349                    .map(|v| (v.address, v.storage_keys))
2350                    .collect::<Vec<_>>(),
2351                signature_y_parity: u8::from_str_radix(
2352                    deserialize_field::<String, D>(&mut map, "yParity")?.trim_start_matches("0x"),
2353                    16,
2354                )
2355                .map_err(serde::de::Error::custom)?
2356                    != 0,
2357                signature_r: deserialize_field::<U256, D>(&mut map, "r")?,
2358                signature_s: deserialize_field::<U256, D>(&mut map, "s")?,
2359                ..Default::default()
2360            })
2361        }
2362    }
2363
2364    impl<'de> Deserialize<'de> for EIP1559Transaction {
2365        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2366        where
2367            D: serde::Deserializer<'de>,
2368        {
2369            let mut map = <HashMap<String, serde_json::Value>>::deserialize(deserializer)?;
2370            Ok(EIP1559Transaction {
2371                chain_id: deserialize_u64_field::<D>(&mut map, "chainId")?,
2372                nonce: deserialize_u64_field::<D>(&mut map, "nonce")?,
2373                max_priority_fee_per_gas: deserialize_u64_field::<D>(
2374                    &mut map,
2375                    "maxPriorityFeePerGas",
2376                )?,
2377                max_fee_per_gas: deserialize_u64_field::<D>(&mut map, "maxFeePerGas")?,
2378                gas_limit: deserialize_u64_field::<D>(&mut map, "gas")?,
2379                to: deserialize_field::<TxKind, D>(&mut map, "to")?,
2380                value: deserialize_field::<U256, D>(&mut map, "value")?,
2381                data: deserialize_input_field(&mut map).map_err(serde::de::Error::custom)?,
2382                access_list: deserialize_field::<Vec<AccessListEntry>, D>(&mut map, "accessList")?
2383                    .into_iter()
2384                    .map(|v| (v.address, v.storage_keys))
2385                    .collect::<Vec<_>>(),
2386                signature_y_parity: u8::from_str_radix(
2387                    deserialize_field::<String, D>(&mut map, "yParity")?.trim_start_matches("0x"),
2388                    16,
2389                )
2390                .map_err(serde::de::Error::custom)?
2391                    != 0,
2392                signature_r: deserialize_field::<U256, D>(&mut map, "r")?,
2393                signature_s: deserialize_field::<U256, D>(&mut map, "s")?,
2394                ..Default::default()
2395            })
2396        }
2397    }
2398
2399    impl<'de> Deserialize<'de> for EIP4844Transaction {
2400        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2401        where
2402            D: serde::Deserializer<'de>,
2403        {
2404            let mut map = <HashMap<String, serde_json::Value>>::deserialize(deserializer)?;
2405
2406            Ok(EIP4844Transaction {
2407                chain_id: deserialize_u64_field::<D>(&mut map, "chainId")?,
2408                nonce: deserialize_u64_field::<D>(&mut map, "nonce")?,
2409                max_priority_fee_per_gas: deserialize_u64_field::<D>(
2410                    &mut map,
2411                    "maxPriorityFeePerGas",
2412                )?,
2413                max_fee_per_gas: deserialize_u64_field::<D>(&mut map, "maxFeePerGas")?,
2414                gas: deserialize_u64_field::<D>(&mut map, "gas")?,
2415                to: deserialize_field::<Address, D>(&mut map, "to")?,
2416                value: deserialize_field::<U256, D>(&mut map, "value")?,
2417                data: deserialize_input_field(&mut map).map_err(serde::de::Error::custom)?,
2418                access_list: deserialize_field::<Vec<AccessListEntry>, D>(&mut map, "accessList")?
2419                    .into_iter()
2420                    .map(|v| (v.address, v.storage_keys))
2421                    .collect::<Vec<_>>(),
2422                max_fee_per_blob_gas: deserialize_field::<U256, D>(&mut map, "maxFeePerBlobGas")?,
2423                blob_versioned_hashes: deserialize_field::<Vec<H256>, D>(
2424                    &mut map,
2425                    "blobVersionedHashes",
2426                )?,
2427                signature_y_parity: u8::from_str_radix(
2428                    deserialize_field::<String, D>(&mut map, "yParity")?.trim_start_matches("0x"),
2429                    16,
2430                )
2431                .map_err(serde::de::Error::custom)?
2432                    != 0,
2433                signature_r: deserialize_field::<U256, D>(&mut map, "r")?,
2434                signature_s: deserialize_field::<U256, D>(&mut map, "s")?,
2435                ..Default::default()
2436            })
2437        }
2438    }
2439
2440    impl<'de> Deserialize<'de> for EIP7702Transaction {
2441        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2442        where
2443            D: serde::Deserializer<'de>,
2444        {
2445            let mut map = <HashMap<String, serde_json::Value>>::deserialize(deserializer)?;
2446
2447            Ok(EIP7702Transaction {
2448                chain_id: deserialize_u64_field::<D>(&mut map, "chainId")?,
2449                nonce: deserialize_u64_field::<D>(&mut map, "nonce")?,
2450                max_priority_fee_per_gas: deserialize_u64_field::<D>(
2451                    &mut map,
2452                    "maxPriorityFeePerGas",
2453                )?,
2454                max_fee_per_gas: deserialize_u64_field::<D>(&mut map, "maxFeePerGas")?,
2455                gas_limit: deserialize_u64_field::<D>(&mut map, "gas")?,
2456                to: deserialize_field::<Address, D>(&mut map, "to")?,
2457                value: deserialize_field::<U256, D>(&mut map, "value")?,
2458                data: deserialize_input_field(&mut map).map_err(serde::de::Error::custom)?,
2459                access_list: deserialize_field::<Vec<AccessListEntry>, D>(&mut map, "accessList")?
2460                    .into_iter()
2461                    .map(|v| (v.address, v.storage_keys))
2462                    .collect::<Vec<_>>(),
2463                authorization_list: deserialize_field::<Vec<AuthorizationTupleEntry>, D>(
2464                    &mut map,
2465                    "authorizationList",
2466                )?
2467                .into_iter()
2468                .map(AuthorizationTuple::from)
2469                .collect::<Vec<_>>(),
2470                signature_y_parity: u8::from_str_radix(
2471                    deserialize_field::<String, D>(&mut map, "yParity")?.trim_start_matches("0x"),
2472                    16,
2473                )
2474                .map_err(serde::de::Error::custom)?
2475                    != 0,
2476                signature_r: deserialize_field::<U256, D>(&mut map, "r")?,
2477                signature_s: deserialize_field::<U256, D>(&mut map, "s")?,
2478                ..Default::default()
2479            })
2480        }
2481    }
2482
2483    impl<'de> Deserialize<'de> for PrivilegedL2Transaction {
2484        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2485        where
2486            D: serde::Deserializer<'de>,
2487        {
2488            let mut map = <HashMap<String, serde_json::Value>>::deserialize(deserializer)?;
2489
2490            Ok(PrivilegedL2Transaction {
2491                chain_id: deserialize_u64_field::<D>(&mut map, "chainId")?,
2492                nonce: deserialize_u64_field::<D>(&mut map, "nonce")?,
2493                max_priority_fee_per_gas: deserialize_u64_field::<D>(
2494                    &mut map,
2495                    "maxPriorityFeePerGas",
2496                )?,
2497                max_fee_per_gas: deserialize_u64_field::<D>(&mut map, "maxFeePerGas")?,
2498                gas_limit: deserialize_u64_field::<D>(&mut map, "gas")?,
2499                to: deserialize_field::<TxKind, D>(&mut map, "to")?,
2500                value: deserialize_field::<U256, D>(&mut map, "value")?,
2501                data: deserialize_input_field(&mut map).map_err(serde::de::Error::custom)?,
2502                access_list: deserialize_field::<Vec<AccessListEntry>, D>(&mut map, "accessList")?
2503                    .into_iter()
2504                    .map(|v| (v.address, v.storage_keys))
2505                    .collect::<Vec<_>>(),
2506                from: deserialize_field::<Address, D>(&mut map, "sender")?,
2507                ..Default::default()
2508            })
2509        }
2510    }
2511
2512    impl<'de> Deserialize<'de> for FeeTokenTransaction {
2513        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2514        where
2515            D: serde::Deserializer<'de>,
2516        {
2517            let mut map = <HashMap<String, serde_json::Value>>::deserialize(deserializer)?;
2518
2519            Ok(FeeTokenTransaction {
2520                chain_id: deserialize_u64_field::<D>(&mut map, "chainId")?,
2521                nonce: deserialize_u64_field::<D>(&mut map, "nonce")?,
2522                max_priority_fee_per_gas: deserialize_u64_field::<D>(
2523                    &mut map,
2524                    "maxPriorityFeePerGas",
2525                )?,
2526                max_fee_per_gas: deserialize_u64_field::<D>(&mut map, "maxFeePerGas")?,
2527                gas_limit: deserialize_u64_field::<D>(&mut map, "gas")?,
2528                to: deserialize_field::<TxKind, D>(&mut map, "to")?,
2529                value: deserialize_field::<U256, D>(&mut map, "value")?,
2530                data: deserialize_input_field(&mut map).map_err(serde::de::Error::custom)?,
2531                access_list: deserialize_field::<Vec<AccessListEntry>, D>(&mut map, "accessList")?
2532                    .into_iter()
2533                    .map(|v| (v.address, v.storage_keys))
2534                    .collect::<Vec<_>>(),
2535                fee_token: deserialize_field::<Address, D>(&mut map, "feeToken")?,
2536                signature_y_parity: u8::from_str_radix(
2537                    deserialize_field::<String, D>(&mut map, "yParity")?.trim_start_matches("0x"),
2538                    16,
2539                )
2540                .map_err(serde::de::Error::custom)?
2541                    != 0,
2542                signature_r: deserialize_field::<U256, D>(&mut map, "r")?,
2543                signature_s: deserialize_field::<U256, D>(&mut map, "s")?,
2544                ..Default::default()
2545            })
2546        }
2547    }
2548
2549    #[derive(Debug, thiserror::Error)]
2550    pub enum GenericTransactionError {
2551        #[error("Invalid transaction type: {0}")]
2552        InvalidTxType(TxType),
2553        #[error("Blob bundle error: {0}")]
2554        BlobBundleError(#[from] BlobsBundleError),
2555        #[error("Missing field: {0}")]
2556        MissingField(String),
2557        #[error("Invalid field: {0}")]
2558        InvalidField(String),
2559    }
2560
2561    /// Unsigned Transaction struct generic to all types which may not contain all required transaction fields
2562    /// Used to perform gas estimations and access list creation
2563    #[derive(Deserialize, Debug, PartialEq, Clone, Default)]
2564    #[serde(rename_all = "camelCase")]
2565    pub struct GenericTransaction {
2566        #[serde(default)]
2567        pub r#type: TxType,
2568        #[serde(default, with = "crate::serde_utils::u64::hex_str_opt")]
2569        pub nonce: Option<u64>,
2570        pub to: TxKind,
2571        #[serde(default)]
2572        pub from: Address,
2573        #[serde(default, with = "crate::serde_utils::u64::hex_str_opt")]
2574        pub gas: Option<u64>,
2575        #[serde(default)]
2576        pub value: U256,
2577        #[serde(default)]
2578        pub gas_price: U256,
2579        #[serde(default, with = "crate::serde_utils::u64::hex_str_opt")]
2580        pub max_priority_fee_per_gas: Option<u64>,
2581        #[serde(default, with = "crate::serde_utils::u64::hex_str_opt")]
2582        pub max_fee_per_gas: Option<u64>,
2583        pub max_fee_per_blob_gas: Option<U256>,
2584        #[serde(default)]
2585        pub access_list: Vec<AccessListEntry>,
2586        #[serde(default)]
2587        pub fee_token: Option<Address>,
2588        #[serde(default)]
2589        pub authorization_list: Option<Vec<AuthorizationTupleEntry>>,
2590        #[serde(default)]
2591        pub blob_versioned_hashes: Vec<H256>,
2592        pub wrapper_version: Option<u8>,
2593        #[serde(default, with = "crate::serde_utils::bytes::vec")]
2594        pub blobs: Vec<Bytes>,
2595        #[serde(default, with = "crate::serde_utils::u64::hex_str_opt")]
2596        pub chain_id: Option<u64>,
2597        // rename is needed here so we dont attempt to deserialize the `input` field rather than the remainder of the fields
2598        #[serde(
2599            flatten,
2600            rename = "input_or_data",
2601            deserialize_with = "deserialize_input",
2602            serialize_with = "crate::serde_utils::bytes::serialize"
2603        )]
2604        pub input: Bytes,
2605    }
2606    /// Custom deserialization function to parse either `data` or `input` fields, or both as long as they have the same value
2607    pub fn deserialize_input<'de, D>(deserializer: D) -> Result<Bytes, D::Error>
2608    where
2609        D: Deserializer<'de>,
2610    {
2611        // The input field can be named either input or data
2612        // In case we have both fields both should be named the same
2613        let variables = HashMap::<String, Value>::deserialize(deserializer)?;
2614        let data = variables.get("data");
2615        let input = variables.get("input");
2616        let value = match (data, input) {
2617            // This replaces `default` attribute for this custom implementation
2618            (None, None) => return Ok(Bytes::new()),
2619            (None, Some(val)) => val,
2620            (Some(val), None) => val,
2621            (Some(val_a), Some(val_b)) => {
2622                if val_a == val_b {
2623                    val_a
2624                } else {
2625                    return Err(D::Error::custom(
2626                        "Transaction has both `data` and `input` fields with different values",
2627                    ));
2628                }
2629            }
2630        };
2631        let value = String::deserialize(value).map_err(D::Error::custom)?;
2632        let bytes = hex::decode(value.trim_start_matches("0x"))
2633            .map_err(|e| D::Error::custom(e.to_string()))?;
2634        Ok(Bytes::from(bytes))
2635    }
2636
2637    impl From<EIP1559Transaction> for GenericTransaction {
2638        fn from(value: EIP1559Transaction) -> Self {
2639            Self {
2640                r#type: TxType::EIP1559,
2641                nonce: Some(value.nonce),
2642                to: value.to,
2643                gas: Some(value.gas_limit),
2644                value: value.value,
2645                input: value.data.clone(),
2646                gas_price: U256::from(value.max_fee_per_gas),
2647                max_priority_fee_per_gas: Some(value.max_priority_fee_per_gas),
2648                max_fee_per_gas: Some(value.max_fee_per_gas),
2649                max_fee_per_blob_gas: None,
2650                access_list: value
2651                    .access_list
2652                    .iter()
2653                    .map(AccessListEntry::from)
2654                    .collect(),
2655                fee_token: None,
2656                authorization_list: None,
2657                blob_versioned_hashes: vec![],
2658                blobs: vec![],
2659                wrapper_version: None,
2660                chain_id: Some(value.chain_id),
2661                from: Address::default(),
2662            }
2663        }
2664    }
2665
2666    impl TryFrom<GenericTransaction> for EIP1559Transaction {
2667        type Error = GenericTransactionError;
2668
2669        fn try_from(value: GenericTransaction) -> Result<Self, Self::Error> {
2670            if value.r#type != TxType::EIP1559 {
2671                return Err(GenericTransactionError::InvalidTxType(value.r#type));
2672            }
2673
2674            Ok(Self {
2675                nonce: value.nonce.unwrap_or_default(),
2676                to: value.to,
2677                gas_limit: value.gas.unwrap_or_default(),
2678                value: value.value,
2679                data: value.input.clone(),
2680                max_priority_fee_per_gas: value.max_priority_fee_per_gas.unwrap_or_default(),
2681                max_fee_per_gas: value.max_fee_per_gas.unwrap_or(
2682                    u64::try_from(value.gas_price).map_err(|_| {
2683                        GenericTransactionError::InvalidField("gas_price overflows u64".to_owned())
2684                    })?,
2685                ),
2686                access_list: value
2687                    .access_list
2688                    .into_iter()
2689                    .map(|v| (v.address, v.storage_keys))
2690                    .collect::<Vec<_>>(),
2691                chain_id: value.chain_id.unwrap_or_default(),
2692                ..Default::default()
2693            })
2694        }
2695    }
2696
2697    impl From<EIP4844Transaction> for GenericTransaction {
2698        fn from(value: EIP4844Transaction) -> Self {
2699            Self {
2700                r#type: TxType::EIP4844,
2701                nonce: Some(value.nonce),
2702                to: TxKind::Call(value.to),
2703                gas: Some(value.gas),
2704                value: value.value,
2705                input: value.data.clone(),
2706                gas_price: U256::from(value.max_fee_per_gas),
2707                max_priority_fee_per_gas: Some(value.max_priority_fee_per_gas),
2708                max_fee_per_gas: Some(value.max_fee_per_gas),
2709                max_fee_per_blob_gas: Some(value.max_fee_per_blob_gas),
2710                access_list: value
2711                    .access_list
2712                    .iter()
2713                    .map(AccessListEntry::from)
2714                    .collect(),
2715                fee_token: None,
2716                authorization_list: None,
2717                blob_versioned_hashes: value.blob_versioned_hashes,
2718                blobs: vec![],
2719                wrapper_version: None,
2720                chain_id: Some(value.chain_id),
2721                from: Address::default(),
2722            }
2723        }
2724    }
2725
2726    #[cfg(feature = "c-kzg")]
2727    impl TryFrom<GenericTransaction> for WrappedEIP4844Transaction {
2728        type Error = GenericTransactionError;
2729
2730        fn try_from(value: GenericTransaction) -> Result<Self, Self::Error> {
2731            let blobs = value
2732                .blobs
2733                .iter()
2734                .map(|bytes| {
2735                    let slice = bytes.as_ref();
2736                    let mut blob = [0u8; BYTES_PER_BLOB];
2737                    blob.copy_from_slice(slice);
2738                    blob
2739                })
2740                .collect();
2741
2742            let wrapper_version = value.wrapper_version;
2743            Ok(Self {
2744                tx: value.try_into()?,
2745                wrapper_version,
2746                blobs_bundle: BlobsBundle::create_from_blobs(&blobs, wrapper_version)?,
2747            })
2748        }
2749    }
2750
2751    impl TryFrom<GenericTransaction> for EIP4844Transaction {
2752        type Error = GenericTransactionError;
2753
2754        fn try_from(value: GenericTransaction) -> Result<Self, Self::Error> {
2755            if value.r#type != TxType::EIP4844 {
2756                return Err(GenericTransactionError::InvalidTxType(value.r#type));
2757            }
2758            Ok(Self {
2759                nonce: value.nonce.unwrap_or_default(),
2760                to: match value.to {
2761                    TxKind::Call(to) => to,
2762                    _ => H160::default(),
2763                },
2764                gas: value.gas.unwrap_or_default(),
2765                value: value.value,
2766                data: value.input.clone(),
2767                max_priority_fee_per_gas: value.max_priority_fee_per_gas.unwrap_or_default(),
2768                max_fee_per_gas: value.max_fee_per_gas.unwrap_or(
2769                    u64::try_from(value.gas_price).map_err(|_| {
2770                        GenericTransactionError::InvalidField("gas_price overflows u64".to_owned())
2771                    })?,
2772                ),
2773                max_fee_per_blob_gas: value.max_fee_per_blob_gas.unwrap_or_default(),
2774                access_list: value
2775                    .access_list
2776                    .into_iter()
2777                    .map(|v| (v.address, v.storage_keys))
2778                    .collect::<Vec<_>>(),
2779                blob_versioned_hashes: value.blob_versioned_hashes,
2780                chain_id: value.chain_id.unwrap_or_default(),
2781                ..Default::default()
2782            })
2783        }
2784    }
2785
2786    impl From<EIP7702Transaction> for GenericTransaction {
2787        fn from(value: EIP7702Transaction) -> Self {
2788            Self {
2789                r#type: TxType::EIP7702,
2790                nonce: Some(value.nonce),
2791                to: TxKind::Call(value.to),
2792                gas: Some(value.gas_limit),
2793                value: value.value,
2794                input: value.data.clone(),
2795                gas_price: U256::from(value.max_fee_per_gas),
2796                max_priority_fee_per_gas: Some(value.max_priority_fee_per_gas),
2797                max_fee_per_gas: Some(value.max_fee_per_gas),
2798                max_fee_per_blob_gas: None,
2799                access_list: value
2800                    .access_list
2801                    .iter()
2802                    .map(AccessListEntry::from)
2803                    .collect(),
2804                fee_token: None,
2805                authorization_list: Some(
2806                    value
2807                        .authorization_list
2808                        .iter()
2809                        .map(AuthorizationTupleEntry::from)
2810                        .collect(),
2811                ),
2812                blob_versioned_hashes: vec![],
2813                blobs: vec![],
2814                wrapper_version: None,
2815                chain_id: Some(value.chain_id),
2816                from: Address::default(),
2817            }
2818        }
2819    }
2820
2821    impl TryFrom<GenericTransaction> for EIP7702Transaction {
2822        type Error = GenericTransactionError;
2823
2824        fn try_from(value: GenericTransaction) -> Result<Self, Self::Error> {
2825            if value.r#type != TxType::EIP7702 {
2826                return Err(GenericTransactionError::InvalidTxType(value.r#type));
2827            }
2828            let TxKind::Call(to) = value.to else {
2829                return Err(GenericTransactionError::MissingField("to".to_owned()));
2830            };
2831            Ok(Self {
2832                chain_id: value.chain_id.unwrap_or_default(),
2833                nonce: value.nonce.unwrap_or_default(),
2834                max_priority_fee_per_gas: value.max_priority_fee_per_gas.unwrap_or_default(),
2835                max_fee_per_gas: value.max_fee_per_gas.unwrap_or(
2836                    u64::try_from(value.gas_price).map_err(|_| {
2837                        GenericTransactionError::InvalidField("gas_price overflows u64".to_owned())
2838                    })?,
2839                ),
2840                gas_limit: value.gas.unwrap_or_default(),
2841                to,
2842                value: value.value,
2843                data: value.input,
2844                access_list: value
2845                    .access_list
2846                    .into_iter()
2847                    .map(|v| (v.address, v.storage_keys))
2848                    .collect::<Vec<_>>(),
2849                authorization_list: value
2850                    .authorization_list
2851                    .unwrap_or_default()
2852                    .into_iter()
2853                    .map(AuthorizationTuple::from)
2854                    .collect(),
2855                ..Default::default()
2856            })
2857        }
2858    }
2859
2860    impl From<PrivilegedL2Transaction> for GenericTransaction {
2861        fn from(value: PrivilegedL2Transaction) -> Self {
2862            Self {
2863                r#type: TxType::Privileged,
2864                nonce: Some(value.nonce),
2865                to: value.to,
2866                gas: Some(value.gas_limit),
2867                value: value.value,
2868                input: value.data.clone(),
2869                gas_price: U256::from(value.max_fee_per_gas),
2870                max_priority_fee_per_gas: Some(value.max_priority_fee_per_gas),
2871                max_fee_per_gas: Some(value.max_fee_per_gas),
2872                max_fee_per_blob_gas: None,
2873                access_list: value
2874                    .access_list
2875                    .iter()
2876                    .map(AccessListEntry::from)
2877                    .collect(),
2878                fee_token: None,
2879                authorization_list: None,
2880                blob_versioned_hashes: vec![],
2881                blobs: vec![],
2882                wrapper_version: None,
2883                chain_id: Some(value.chain_id),
2884                from: value.from,
2885            }
2886        }
2887    }
2888
2889    impl TryFrom<GenericTransaction> for PrivilegedL2Transaction {
2890        type Error = GenericTransactionError;
2891
2892        fn try_from(value: GenericTransaction) -> Result<Self, Self::Error> {
2893            if value.r#type != TxType::Privileged {
2894                return Err(GenericTransactionError::InvalidTxType(value.r#type));
2895            }
2896            Ok(Self {
2897                nonce: value.nonce.unwrap_or_default(),
2898                to: value.to,
2899                gas_limit: value.gas.unwrap_or_default(),
2900                value: value.value,
2901                data: value.input.clone(),
2902                max_priority_fee_per_gas: value.max_priority_fee_per_gas.unwrap_or_default(),
2903                max_fee_per_gas: value.max_fee_per_gas.unwrap_or(
2904                    u64::try_from(value.gas_price).map_err(|_| {
2905                        GenericTransactionError::InvalidField("gas_price overflows u64".to_owned())
2906                    })?,
2907                ),
2908                access_list: value
2909                    .access_list
2910                    .into_iter()
2911                    .map(|v| (v.address, v.storage_keys))
2912                    .collect::<Vec<_>>(),
2913                chain_id: value.chain_id.unwrap_or_default(),
2914                from: value.from,
2915                ..Default::default()
2916            })
2917        }
2918    }
2919
2920    impl From<FeeTokenTransaction> for GenericTransaction {
2921        fn from(value: FeeTokenTransaction) -> Self {
2922            Self {
2923                r#type: TxType::FeeToken,
2924                nonce: Some(value.nonce),
2925                to: value.to,
2926                gas: Some(value.gas_limit),
2927                value: value.value,
2928                input: value.data.clone(),
2929                gas_price: U256::from(value.max_fee_per_gas),
2930                max_priority_fee_per_gas: Some(value.max_priority_fee_per_gas),
2931                max_fee_per_gas: Some(value.max_fee_per_gas),
2932                max_fee_per_blob_gas: None,
2933                access_list: value
2934                    .access_list
2935                    .iter()
2936                    .map(AccessListEntry::from)
2937                    .collect(),
2938                fee_token: Some(value.fee_token),
2939                authorization_list: None,
2940                blob_versioned_hashes: vec![],
2941                blobs: vec![],
2942                chain_id: Some(value.chain_id),
2943                from: Address::default(),
2944                wrapper_version: None,
2945            }
2946        }
2947    }
2948
2949    impl TryFrom<GenericTransaction> for FeeTokenTransaction {
2950        type Error = GenericTransactionError;
2951
2952        fn try_from(value: GenericTransaction) -> Result<Self, Self::Error> {
2953            if value.r#type != TxType::FeeToken {
2954                return Err(GenericTransactionError::InvalidTxType(value.r#type));
2955            }
2956
2957            Ok(Self {
2958                nonce: value.nonce.unwrap_or_default(),
2959                to: value.to,
2960                gas_limit: value.gas.unwrap_or_default(),
2961                value: value.value,
2962                data: value.input.clone(),
2963                max_priority_fee_per_gas: value.max_priority_fee_per_gas.unwrap_or_default(),
2964                max_fee_per_gas: value.max_fee_per_gas.unwrap_or(
2965                    u64::try_from(value.gas_price).map_err(|_| {
2966                        GenericTransactionError::InvalidField("gas_price overflows u64".to_owned())
2967                    })?,
2968                ),
2969                access_list: value
2970                    .access_list
2971                    .into_iter()
2972                    .map(|v| (v.address, v.storage_keys))
2973                    .collect::<Vec<_>>(),
2974                fee_token: value
2975                    .fee_token
2976                    .ok_or(GenericTransactionError::MissingField(
2977                        "fee token".to_owned(),
2978                    ))?,
2979                chain_id: value.chain_id.unwrap_or_default(),
2980                ..Default::default()
2981            })
2982        }
2983    }
2984
2985    impl From<LegacyTransaction> for GenericTransaction {
2986        fn from(value: LegacyTransaction) -> Self {
2987            Self {
2988                r#type: TxType::Legacy,
2989                nonce: Some(value.nonce),
2990                to: value.to,
2991                from: Address::default(),
2992                gas: Some(value.gas),
2993                value: value.value,
2994                gas_price: value.gas_price,
2995                max_priority_fee_per_gas: None,
2996                max_fee_per_gas: None,
2997                max_fee_per_blob_gas: None,
2998                access_list: vec![],
2999                fee_token: None,
3000                authorization_list: None,
3001                blob_versioned_hashes: vec![],
3002                blobs: vec![],
3003                wrapper_version: None,
3004                chain_id: None,
3005                input: value.data,
3006            }
3007        }
3008    }
3009
3010    impl From<EIP2930Transaction> for GenericTransaction {
3011        fn from(value: EIP2930Transaction) -> Self {
3012            Self {
3013                r#type: TxType::EIP2930,
3014                nonce: Some(value.nonce),
3015                to: value.to,
3016                from: Address::default(),
3017                gas: Some(value.gas_limit),
3018                value: value.value,
3019                gas_price: value.gas_price,
3020                max_priority_fee_per_gas: None,
3021                max_fee_per_gas: None,
3022                max_fee_per_blob_gas: None,
3023                access_list: value
3024                    .access_list
3025                    .into_iter()
3026                    .map(|(address, storage_keys)| AccessListEntry {
3027                        address,
3028                        storage_keys,
3029                    })
3030                    .collect(),
3031                fee_token: None,
3032                authorization_list: None,
3033                blob_versioned_hashes: vec![],
3034                blobs: vec![],
3035                wrapper_version: None,
3036                chain_id: Some(value.chain_id),
3037                input: value.data,
3038            }
3039        }
3040    }
3041
3042    impl From<Transaction> for GenericTransaction {
3043        fn from(value: Transaction) -> Self {
3044            match value {
3045                Transaction::LegacyTransaction(tx) => tx.into(),
3046                Transaction::EIP2930Transaction(tx) => tx.into(),
3047                Transaction::EIP1559Transaction(tx) => tx.into(),
3048                Transaction::EIP4844Transaction(tx) => tx.into(),
3049                Transaction::EIP7702Transaction(tx) => tx.into(),
3050                Transaction::PrivilegedL2Transaction(tx) => tx.into(),
3051                Transaction::FeeTokenTransaction(tx) => tx.into(),
3052            }
3053        }
3054    }
3055}
3056
3057mod mempool {
3058    use super::*;
3059    use std::{
3060        cmp::Ordering,
3061        sync::Arc,
3062        time::{SystemTime, UNIX_EPOCH},
3063    };
3064
3065    #[derive(Debug, Clone, PartialEq, Eq)]
3066    pub struct MempoolTransaction {
3067        // Unix timestamp (in microseconds) created once the transaction reached the MemPool
3068        timestamp: u128,
3069        sender: Address,
3070        inner: Arc<Transaction>,
3071    }
3072
3073    impl MempoolTransaction {
3074        pub fn new(tx: Transaction, sender: Address) -> Self {
3075            Self {
3076                timestamp: SystemTime::now()
3077                    .duration_since(UNIX_EPOCH)
3078                    .expect("Invalid system time")
3079                    .as_micros(),
3080                sender,
3081                inner: Arc::new(tx),
3082            }
3083        }
3084        pub fn time(&self) -> u128 {
3085            self.timestamp
3086        }
3087
3088        pub fn sender(&self) -> Address {
3089            self.sender
3090        }
3091
3092        pub fn transaction(&self) -> &Transaction {
3093            &self.inner
3094        }
3095    }
3096
3097    impl RLPEncode for MempoolTransaction {
3098        fn encode(&self, buf: &mut dyn bytes::BufMut) {
3099            Encoder::new(buf)
3100                .encode_field(&self.timestamp)
3101                .encode_field(&*self.inner)
3102                .finish();
3103        }
3104    }
3105
3106    impl RLPDecode for MempoolTransaction {
3107        fn decode_unfinished(rlp: &[u8]) -> Result<(Self, &[u8]), RLPDecodeError> {
3108            let decoder = Decoder::new(rlp)?;
3109            let (timestamp, decoder) = decoder.decode_field("timestamp")?;
3110            let (sender, decoder) = decoder.decode_field("sender")?;
3111            let (inner, decoder) = decoder.decode_field("inner")?;
3112            Ok((
3113                Self {
3114                    timestamp,
3115                    sender,
3116                    inner: Arc::new(inner),
3117                },
3118                decoder.finish()?,
3119            ))
3120        }
3121    }
3122
3123    impl std::ops::Deref for MempoolTransaction {
3124        type Target = Transaction;
3125
3126        fn deref(&self) -> &Self::Target {
3127            &self.inner
3128        }
3129    }
3130
3131    // Orders transactions by lowest nonce, if the nonce is equal, orders by highest timestamp
3132    impl Ord for MempoolTransaction {
3133        fn cmp(&self, other: &Self) -> Ordering {
3134            match (self.tx_type(), other.tx_type()) {
3135                (TxType::Privileged, TxType::Privileged) => {
3136                    return self.nonce().cmp(&other.nonce());
3137                }
3138                (TxType::Privileged, _) => return Ordering::Less,
3139                (_, TxType::Privileged) => return Ordering::Greater,
3140                _ => (),
3141            };
3142            match self.nonce().cmp(&other.nonce()) {
3143                Ordering::Equal => other.time().cmp(&self.time()),
3144                ordering => ordering,
3145            }
3146        }
3147    }
3148
3149    impl PartialOrd for MempoolTransaction {
3150        fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
3151            Some(self.cmp(other))
3152        }
3153    }
3154}
3155
3156#[cfg(test)]
3157mod tests {
3158
3159    use super::*;
3160    use crate::types::{
3161        AuthorizationTuple, BlockBody, Receipt, compute_receipts_root, compute_transactions_root,
3162    };
3163    use ethereum_types::H160;
3164    use hex_literal::hex;
3165    use serde_impl::{AccessListEntry, GenericTransaction};
3166    use std::str::FromStr;
3167
3168    #[test]
3169    fn legacy_chain_id_handles_out_of_range_v_without_underflow() {
3170        // A legacy transaction decodes `v` as an arbitrary U256, so a malformed tx can
3171        // carry any value. `derive_legacy_chain_id` must return None for the pre-EIP-155
3172        // values (27/28) and any v < 35 rather than underflowing `v - 35`, which panics
3173        // in debug and wraps to a bogus chain id in release. This is reachable on the
3174        // block-import path (every tx's chain id is now checked pre-execution).
3175        let legacy_chain_id = |v: u64| {
3176            Transaction::LegacyTransaction(LegacyTransaction {
3177                v: U256::from(v),
3178                ..Default::default()
3179            })
3180            .chain_id()
3181        };
3182        for v in [0u64, 1, 26, 27, 28, 34] {
3183            assert_eq!(legacy_chain_id(v), None, "v={v} must yield no chain id");
3184        }
3185        // EIP-155 values (v = chain_id * 2 + 35/36) still derive correctly.
3186        assert_eq!(legacy_chain_id(35), Some(0));
3187        assert_eq!(legacy_chain_id(36), Some(0));
3188        assert_eq!(legacy_chain_id(37), Some(1));
3189    }
3190
3191    #[test]
3192    fn test_compute_transactions_root() {
3193        let mut body = BlockBody::empty();
3194        let tx = LegacyTransaction {
3195            nonce: 0,
3196            gas_price: U256::from(0x0a),
3197            gas: 0x05f5e100,
3198            to: TxKind::Call(hex!("1000000000000000000000000000000000000000").into()),
3199            value: 0.into(),
3200            data: Default::default(),
3201            v: U256::from(0x1b),
3202            r: U256::from_big_endian(&hex!(
3203                "7e09e26678ed4fac08a249ebe8ed680bf9051a5e14ad223e4b2b9d26e0208f37"
3204            )),
3205            s: U256::from_big_endian(&hex!(
3206                "5f6e3f188e3e6eab7d7d3b6568f5eac7d687b08d307d3154ccd8c87b4630509b"
3207            )),
3208            ..Default::default()
3209        };
3210        body.transactions.push(Transaction::LegacyTransaction(tx));
3211        let expected_root =
3212            hex!("8151d548273f6683169524b66ca9fe338b9ce42bc3540046c828fd939ae23bcb");
3213        let result = compute_transactions_root(&body.transactions, &ethrex_crypto::NativeCrypto);
3214
3215        assert_eq!(result, expected_root.into());
3216    }
3217    #[test]
3218    fn test_compute_hash() {
3219        // taken from Hive
3220        let tx_eip2930 = EIP2930Transaction {
3221            chain_id: 3503995874084926u64,
3222            nonce: 7,
3223            gas_price: U256::from(0x2dbf1f9a_u64),
3224            gas_limit: 0x186A0,
3225            to: TxKind::Call(hex!("7dcd17433742f4c0ca53122ab541d0ba67fc27df").into()),
3226            value: 2.into(),
3227            data: Bytes::from(&b"\xdbS\x06$\x8e\x03\x13\xe7emit"[..]),
3228            access_list: vec![(
3229                hex!("7dcd17433742f4c0ca53122ab541d0ba67fc27df").into(),
3230                vec![
3231                    hex!("0000000000000000000000000000000000000000000000000000000000000000").into(),
3232                    hex!("a3d07a7d68fbd49ec2f8e6befdd86c885f86c272819f6f345f365dec35ae6707").into(),
3233                ],
3234            )],
3235            signature_y_parity: false,
3236            signature_r: U256::from_dec_str(
3237                "75813812796588349127366022588733264074091236448495248199152066031778895768879",
3238            )
3239            .unwrap(),
3240            signature_s: U256::from_dec_str(
3241                "25476208226281085290728123165613764315157904411823916642262684106502155457829",
3242            )
3243            .unwrap(),
3244            ..Default::default()
3245        };
3246        let tx = Transaction::EIP2930Transaction(tx_eip2930);
3247
3248        let expected_hash =
3249            hex!("a0762610d794acddd2dca15fb7c437ada3611c886f3bea675d53d8da8a6c41b2");
3250        let hash = tx.compute_hash();
3251        assert_eq!(hash, expected_hash.into());
3252    }
3253
3254    #[test]
3255    fn test_compute_receipts_root() {
3256        // example taken from
3257        // https://github.com/ethereum/go-ethereum/blob/f8aa62353666a6368fb3f1a378bd0a82d1542052/cmd/evm/testdata/1/exp.json#L18
3258        let tx_type = TxType::Legacy;
3259        let succeeded = true;
3260        let cumulative_gas_used = 0x5208;
3261        let logs = vec![];
3262        let receipt = Receipt::new(tx_type, succeeded, cumulative_gas_used, logs);
3263
3264        let result = compute_receipts_root(&[receipt], &ethrex_crypto::NativeCrypto);
3265        let expected_root =
3266            hex!("056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2");
3267        assert_eq!(result, expected_root.into());
3268    }
3269
3270    #[test]
3271    fn legacy_tx_rlp_decode() {
3272        let encoded_tx = "f86d80843baa0c4082f618946177843db3138ae69679a54b95cf345ed759450d870aa87bee538000808360306ba0151ccc02146b9b11adf516e6787b59acae3e76544fdcd75e77e67c6b598ce65da064c5dd5aae2fbb535830ebbdad0234975cd7ece3562013b63ea18cc0df6c97d4";
3273        let encoded_tx_bytes = hex::decode(encoded_tx).unwrap();
3274        let tx = LegacyTransaction::decode(&encoded_tx_bytes).unwrap();
3275        let expected_tx = LegacyTransaction {
3276            nonce: 0,
3277            gas_price: U256::from(1001000000u64),
3278            gas: 63000,
3279            to: TxKind::Call(Address::from_slice(
3280                &hex::decode("6177843db3138ae69679A54b95cf345ED759450d").unwrap(),
3281            )),
3282            value: 3000000000000000_u64.into(),
3283            data: Bytes::new(),
3284            r: U256::from_str_radix(
3285                "151ccc02146b9b11adf516e6787b59acae3e76544fdcd75e77e67c6b598ce65d",
3286                16,
3287            )
3288            .unwrap(),
3289            s: U256::from_str_radix(
3290                "64c5dd5aae2fbb535830ebbdad0234975cd7ece3562013b63ea18cc0df6c97d4",
3291                16,
3292            )
3293            .unwrap(),
3294            v: 6303851.into(),
3295            ..Default::default()
3296        };
3297        assert_eq!(tx, expected_tx);
3298    }
3299
3300    #[test]
3301    fn eip1559_tx_rlp_decode() {
3302        let encoded_tx = "f86c8330182480114e82f618946177843db3138ae69679a54b95cf345ed759450d870aa87bee53800080c080a0151ccc02146b9b11adf516e6787b59acae3e76544fdcd75e77e67c6b598ce65da064c5dd5aae2fbb535830ebbdad0234975cd7ece3562013b63ea18cc0df6c97d4";
3303        let encoded_tx_bytes = hex::decode(encoded_tx).unwrap();
3304        let tx = EIP1559Transaction::decode(&encoded_tx_bytes).unwrap();
3305        let expected_tx = EIP1559Transaction {
3306            nonce: 0,
3307            max_fee_per_gas: 78,
3308            max_priority_fee_per_gas: 17,
3309            to: TxKind::Call(Address::from_slice(
3310                &hex::decode("6177843db3138ae69679A54b95cf345ED759450d").unwrap(),
3311            )),
3312            value: 3000000000000000_u64.into(),
3313            data: Bytes::new(),
3314            signature_r: U256::from_str_radix(
3315                "151ccc02146b9b11adf516e6787b59acae3e76544fdcd75e77e67c6b598ce65d",
3316                16,
3317            )
3318            .unwrap(),
3319            signature_s: U256::from_str_radix(
3320                "64c5dd5aae2fbb535830ebbdad0234975cd7ece3562013b63ea18cc0df6c97d4",
3321                16,
3322            )
3323            .unwrap(),
3324            signature_y_parity: false,
3325            chain_id: 3151908,
3326            gas_limit: 63000,
3327            access_list: vec![],
3328            ..Default::default()
3329        };
3330        assert_eq!(tx, expected_tx);
3331    }
3332
3333    #[test]
3334    fn deserialize_tx_kind() {
3335        let tx_kind_create = r#""""#;
3336        let tx_kind_call = r#""0x6177843db3138ae69679A54b95cf345ED759450d""#;
3337        let deserialized_tx_kind_create = TxKind::Create;
3338        let deserialized_tx_kind_call = TxKind::Call(Address::from_slice(
3339            &hex::decode("6177843db3138ae69679A54b95cf345ED759450d").unwrap(),
3340        ));
3341        assert_eq!(
3342            deserialized_tx_kind_create,
3343            serde_json::from_str(tx_kind_create).unwrap()
3344        );
3345        assert_eq!(
3346            deserialized_tx_kind_call,
3347            serde_json::from_str(tx_kind_call).unwrap()
3348        )
3349    }
3350
3351    #[test]
3352    fn deserialize_tx_type() {
3353        let tx_type_eip2930 = r#""0x01""#;
3354        let tx_type_eip1559 = r#""0x02""#;
3355        let deserialized_tx_type_eip2930 = TxType::EIP2930;
3356        let deserialized_tx_type_eip1559 = TxType::EIP1559;
3357        assert_eq!(
3358            deserialized_tx_type_eip2930,
3359            serde_json::from_str(tx_type_eip2930).unwrap()
3360        );
3361        assert_eq!(
3362            deserialized_tx_type_eip1559,
3363            serde_json::from_str(tx_type_eip1559).unwrap()
3364        )
3365    }
3366
3367    #[test]
3368    fn deserialize_generic_transaction() {
3369        let generic_transaction = r#"{
3370            "type":"0x01",
3371            "nonce":"0x02",
3372            "to":"",
3373            "from":"0x6177843db3138ae69679A54b95cf345ED759450d",
3374            "gas":"0x5208",
3375            "value":"0x01",
3376            "input":"0x010203040506",
3377            "gasPrice":"0x07",
3378            "accessList": [
3379                {
3380                    "address": "0x000f3df6d732807ef1319fb7b8bb8522d0beac02",
3381                    "storageKeys": [
3382                        "0x000000000000000000000000000000000000000000000000000000000000000c",
3383                        "0x000000000000000000000000000000000000000000000000000000000000200b"
3384                    ]
3385                }
3386            ]
3387        }"#;
3388        let deserialized_generic_transaction = GenericTransaction {
3389            r#type: TxType::EIP2930,
3390            nonce: Some(2),
3391            to: TxKind::Create,
3392            from: Address::from_slice(
3393                &hex::decode("6177843db3138ae69679A54b95cf345ED759450d").unwrap(),
3394            ),
3395            gas: Some(0x5208),
3396            value: U256::from(1),
3397            input: Bytes::from(hex::decode("010203040506").unwrap()),
3398            gas_price: U256::from(7),
3399            max_priority_fee_per_gas: Default::default(),
3400            max_fee_per_gas: Default::default(),
3401            max_fee_per_blob_gas: Default::default(),
3402            access_list: vec![AccessListEntry {
3403                address: Address::from_slice(
3404                    &hex::decode("000f3df6d732807ef1319fb7b8bb8522d0beac02").unwrap(),
3405                ),
3406                storage_keys: vec![H256::from_low_u64_be(12), H256::from_low_u64_be(8203)],
3407            }],
3408            fee_token: None,
3409            blob_versioned_hashes: Default::default(),
3410            blobs: Default::default(),
3411            wrapper_version: None,
3412            chain_id: Default::default(),
3413            authorization_list: None,
3414        };
3415        assert_eq!(
3416            deserialized_generic_transaction,
3417            serde_json::from_str(generic_transaction).unwrap()
3418        )
3419    }
3420
3421    #[test]
3422    fn deserialize_generic_transaction_with_data_and_input_fields() {
3423        let generic_transaction = r#"{
3424            "type":"0x01",
3425            "nonce":"0x02",
3426            "to":"",
3427            "from":"0x6177843db3138ae69679A54b95cf345ED759450d",
3428            "gas":"0x5208",
3429            "value":"0x01",
3430            "input":"0x010203040506",
3431            "data":"0x010203040506",
3432            "gasPrice":"0x07",
3433            "accessList": [
3434                {
3435                    "address": "0x000f3df6d732807ef1319fb7b8bb8522d0beac02",
3436                    "storageKeys": [
3437                        "0x000000000000000000000000000000000000000000000000000000000000000c",
3438                        "0x000000000000000000000000000000000000000000000000000000000000200b"
3439                    ]
3440                }
3441            ]
3442        }"#;
3443        let deserialized_generic_transaction = GenericTransaction {
3444            r#type: TxType::EIP2930,
3445            nonce: Some(2),
3446            to: TxKind::Create,
3447            from: Address::from_slice(
3448                &hex::decode("6177843db3138ae69679A54b95cf345ED759450d").unwrap(),
3449            ),
3450            gas: Some(0x5208),
3451            value: U256::from(1),
3452            input: Bytes::from(hex::decode("010203040506").unwrap()),
3453            gas_price: U256::from(7),
3454            max_priority_fee_per_gas: Default::default(),
3455            max_fee_per_gas: Default::default(),
3456            max_fee_per_blob_gas: Default::default(),
3457            access_list: vec![AccessListEntry {
3458                address: Address::from_slice(
3459                    &hex::decode("000f3df6d732807ef1319fb7b8bb8522d0beac02").unwrap(),
3460                ),
3461                storage_keys: vec![H256::from_low_u64_be(12), H256::from_low_u64_be(8203)],
3462            }],
3463            fee_token: None,
3464            blob_versioned_hashes: Default::default(),
3465            blobs: Default::default(),
3466            wrapper_version: None,
3467            chain_id: Default::default(),
3468            authorization_list: None,
3469        };
3470        assert_eq!(
3471            deserialized_generic_transaction,
3472            serde_json::from_str(generic_transaction).unwrap()
3473        )
3474    }
3475
3476    #[test]
3477    fn deserialize_eip4844_transaction() {
3478        let eip4844_transaction = r#"{
3479            "chainId":"0x01",
3480            "nonce":"0x02",
3481            "maxPriorityFeePerGas":"0x01",
3482            "maxFeePerGas":"0x01",
3483            "gas":"0x5208",
3484            "to":"0x6177843db3138ae69679A54b95cf345ED759450d",
3485            "value":"0x01",
3486            "input":"0x3033",
3487            "accessList": [
3488                {
3489                    "address": "0x000f3df6d732807ef1319fb7b8bb8522d0beac02",
3490                    "storageKeys": [
3491                        "0x000000000000000000000000000000000000000000000000000000000000000c",
3492                        "0x000000000000000000000000000000000000000000000000000000000000200b"
3493                    ]
3494                }
3495            ],
3496            "maxFeePerBlobGas":"0x03",
3497            "blobVersionedHashes": [
3498                    "0x0000000000000000000000000000000000000000000000000000000000000001",
3499                    "0x0000000000000000000000000000000000000000000000000000000000000002"
3500            ],
3501            "yParity":"0x0",
3502            "r": "0x01",
3503            "s": "0x02"
3504        }"#;
3505        let deserialized_eip4844_transaction = EIP4844Transaction {
3506            chain_id: 0x01,
3507            nonce: 0x02,
3508            to: Address::from_slice(
3509                &hex::decode("6177843db3138ae69679A54b95cf345ED759450d").unwrap(),
3510            ),
3511            max_priority_fee_per_gas: 1,
3512            max_fee_per_gas: 1,
3513            max_fee_per_blob_gas: U256::from(0x03),
3514            gas: 0x5208,
3515            value: U256::from(0x01),
3516            // 03 in hex is 0x3033, that's why the 'input' has that number.
3517            data: Bytes::from_static(b"03"),
3518            access_list: vec![(
3519                Address::from_slice(
3520                    &hex::decode("000f3df6d732807ef1319fb7b8bb8522d0beac02").unwrap(),
3521                ),
3522                vec![H256::from_low_u64_be(12), H256::from_low_u64_be(8203)],
3523            )],
3524            blob_versioned_hashes: vec![H256::from_low_u64_be(1), H256::from_low_u64_be(2)],
3525            signature_y_parity: false,
3526            signature_r: U256::from(0x01),
3527            signature_s: U256::from(0x02),
3528            ..Default::default()
3529        };
3530
3531        assert_eq!(
3532            deserialized_eip4844_transaction,
3533            serde_json::from_str(eip4844_transaction).unwrap()
3534        )
3535    }
3536
3537    #[test]
3538    fn serialize_deserialize_transaction() {
3539        let eip1559 = EIP1559Transaction {
3540            chain_id: 65536999,
3541            nonce: 1,
3542            max_priority_fee_per_gas: 1000,
3543            max_fee_per_gas: 2000,
3544            gas_limit: 21000,
3545            to: TxKind::Call(H160::from_str("0x000a52D537c4150ec274dcE3962a0d179B7E71B0").unwrap()),
3546            value: U256::from(100000),
3547            data: Bytes::from_static(b"03"),
3548            access_list: vec![(
3549                H160::from_str("0x000a52D537c4150ec274dcE3962a0d179B7E71B3").unwrap(),
3550                vec![H256::zero()],
3551            )],
3552            signature_y_parity: true,
3553            signature_r: U256::one(),
3554            signature_s: U256::zero(),
3555            ..Default::default()
3556        };
3557        let tx_to_serialize = Transaction::EIP1559Transaction(eip1559.clone());
3558        let serialized = serde_json::to_string(&tx_to_serialize).expect("Failed to serialize");
3559
3560        let deserialized_tx: Transaction =
3561            serde_json::from_str(&serialized).expect("Failed to deserialize");
3562
3563        assert!(deserialized_tx.tx_type() == TxType::EIP1559);
3564
3565        if let Transaction::EIP1559Transaction(tx) = deserialized_tx {
3566            assert_eq!(tx, eip1559);
3567        }
3568    }
3569
3570    #[test]
3571    fn serialize_deserialize_eip7702transaction() {
3572        let eip7702 = EIP7702Transaction {
3573            chain_id: 65536999,
3574            nonce: 1,
3575            max_priority_fee_per_gas: 1000,
3576            max_fee_per_gas: 2000,
3577            gas_limit: 21000,
3578            to: Address::from_str("0x000a52D537c4150ec274dcE3962a0d179B7E71B0").unwrap(),
3579            value: U256::from(100000),
3580            data: Bytes::from_static(b"03"),
3581            access_list: vec![],
3582            signature_y_parity: true,
3583            signature_r: U256::one(),
3584            signature_s: U256::zero(),
3585            authorization_list: vec![AuthorizationTuple {
3586                chain_id: U256::from(65536999),
3587                address: H160::from_str("0x000a52D537c4150ec274dcE3962a0d179B7E71B1").unwrap(),
3588                nonce: 2,
3589                y_parity: U256::one(),
3590                r_signature: U256::from(22),
3591                s_signature: U256::from(37),
3592            }],
3593            ..Default::default()
3594        };
3595        let tx_to_serialize = Transaction::EIP7702Transaction(eip7702.clone());
3596        let serialized = serde_json::to_string(&tx_to_serialize).expect("Failed to serialize");
3597
3598        let deserialized_tx: Transaction =
3599            serde_json::from_str(&serialized).expect("Failed to deserialize");
3600
3601        assert!(deserialized_tx.tx_type() == TxType::EIP7702);
3602
3603        if let Transaction::EIP7702Transaction(tx) = deserialized_tx {
3604            assert_eq!(tx, eip7702);
3605        }
3606    }
3607
3608    #[test]
3609    fn serialize_deserialize_privileged_l2_transaction() -> Result<(), RLPDecodeError> {
3610        let privileged_l2 = PrivilegedL2Transaction {
3611            chain_id: 65536999,
3612            nonce: 0,
3613            max_priority_fee_per_gas: 875000000,
3614            max_fee_per_gas: 875000000,
3615            gas_limit: 42000u64,
3616            to: TxKind::Call(
3617                Address::from_str("0x8943545177806ed17b9f23f0a21ee5948ecaa776").unwrap(),
3618            ),
3619            value: U256::from(500000000000000000000000000u128),
3620            data: Bytes::new(),
3621            access_list: vec![],
3622            from: Address::from_str("0x8943545177806ed17b9f23f0a21ee5948ecaa776").unwrap(),
3623            ..Default::default()
3624        };
3625
3626        let encoded = PrivilegedL2Transaction::encode_to_vec(&privileged_l2);
3627        println!("encoded length: {}", encoded.len());
3628        assert_eq!(encoded.len(), privileged_l2.length());
3629
3630        let deserialized_tx = PrivilegedL2Transaction::decode(&encoded)?;
3631
3632        assert_eq!(deserialized_tx, privileged_l2);
3633
3634        Ok(())
3635    }
3636
3637    #[test]
3638    fn test_legacy_transaction_into_generic() {
3639        let legacy_tx = LegacyTransaction {
3640            nonce: 1,
3641            gas_price: U256::from(20_000_000_000u64),
3642            gas: 21000,
3643            to: TxKind::Call(
3644                Address::from_str("0x742d35Cc6634C0532925a3b844Bc454e4438f44e").unwrap(),
3645            ),
3646            value: U256::from(1_000_000_000_000_000_000u64),
3647            data: Bytes::default(),
3648            v: U256::from(27),
3649            r: U256::from(1),
3650            s: U256::from(1),
3651            ..Default::default()
3652        };
3653
3654        let generic_tx: GenericTransaction = legacy_tx.into();
3655        assert_eq!(generic_tx.r#type, TxType::Legacy);
3656        assert_eq!(generic_tx.nonce, Some(1));
3657        assert_eq!(generic_tx.gas_price, U256::from(20_000_000_000u64));
3658        assert_eq!(generic_tx.gas, Some(21000));
3659        assert_eq!(generic_tx.max_priority_fee_per_gas, None);
3660        assert_eq!(generic_tx.max_fee_per_gas, None);
3661        assert_eq!(generic_tx.access_list.len(), 0);
3662        assert_eq!(generic_tx.chain_id, None);
3663    }
3664
3665    #[test]
3666    fn test_eip2930_transaction_into_generic() {
3667        let access_list = vec![(
3668            Address::from_str("0x742d35Cc6634C0532925a3b844Bc454e4438f44e").unwrap(),
3669            vec![
3670                H256::from_str(
3671                    "0x1234567890123456789012345678901234567890123456789012345678901234",
3672                )
3673                .unwrap(),
3674            ],
3675        )];
3676
3677        let eip2930_tx = EIP2930Transaction {
3678            chain_id: 1,
3679            nonce: 1,
3680            gas_price: U256::from(20_000_000_000u64),
3681            gas_limit: 21000,
3682            to: TxKind::Call(
3683                Address::from_str("0x742d35Cc6634C0532925a3b844Bc454e4438f44e").unwrap(),
3684            ),
3685            value: U256::from(1_000_000_000_000_000_000u64),
3686            data: Bytes::default(),
3687            access_list: access_list.clone(),
3688            signature_y_parity: false,
3689            signature_r: U256::from(1),
3690            signature_s: U256::from(1),
3691            ..Default::default()
3692        };
3693
3694        let generic_tx: GenericTransaction = eip2930_tx.into();
3695        assert_eq!(generic_tx.r#type, TxType::EIP2930);
3696        assert_eq!(generic_tx.nonce, Some(1));
3697        assert_eq!(generic_tx.gas_price, U256::from(20_000_000_000u64));
3698        assert_eq!(generic_tx.gas, Some(21000));
3699        assert_eq!(generic_tx.max_priority_fee_per_gas, None);
3700        assert_eq!(generic_tx.max_fee_per_gas, None);
3701        assert_eq!(generic_tx.chain_id, Some(1));
3702        assert_eq!(generic_tx.access_list.len(), 1);
3703        assert_eq!(generic_tx.access_list[0].address, access_list[0].0);
3704        assert_eq!(generic_tx.access_list[0].storage_keys, access_list[0].1);
3705    }
3706
3707    #[test]
3708    fn recover_address_rejects_high_s_signatures() {
3709        use ethrex_crypto::NativeCrypto;
3710        use k256::ecdsa::SigningKey;
3711
3712        let crypto = NativeCrypto;
3713
3714        // 1. Setup: Create a signer and a message
3715        // A random private key for testing
3716        let private_key = hex!("4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318");
3717        let signing_key = SigningKey::from_bytes(&private_key.into()).expect("Valid private key");
3718
3719        // The message we want to sign
3720        let msg = b"Test message for high-s signature rejection";
3721        // Calculate the Keccak256 hash of the message (the payload)
3722        let payload = keccak(msg).to_fixed_bytes();
3723
3724        // 2. Generate a valid low-s signature
3725        // k256's sign_prehash_recoverable produces canonical low-s signatures by default.
3726        let (signature, recovery_id) = signing_key
3727            .sign_prehash_recoverable(&payload)
3728            .expect("Signing failed");
3729
3730        // 3. Construct the signature bytes (r||s||v, 65 bytes)
3731        let mut sig_bytes = [0u8; 65];
3732        sig_bytes[..64].copy_from_slice(&signature.to_bytes());
3733        sig_bytes[64] = recovery_id.to_byte();
3734
3735        // 4. Verify that the valid low-s signature recovers the correct address
3736        // Calculate the expected address from the public key
3737        let uncompressed_pub = signing_key.verifying_key().to_encoded_point(false);
3738        let pub_hash = ethrex_crypto::keccak::keccak_hash(&uncompressed_pub.as_bytes()[1..]);
3739        let expected_address = Address::from_slice(&pub_hash[12..]);
3740
3741        let recovered = crypto
3742            .recover_signer(&sig_bytes, &payload)
3743            .expect("Valid low-s signature should recover successfully");
3744        assert_eq!(recovered, expected_address, "Recovered address mismatch");
3745
3746        // 5. Create a high-s signature: s' = N - s
3747        // The curve order N for secp256k1
3748        let n = U256::from_big_endian(&hex!(
3749            "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141"
3750        ));
3751        let s = U256::from_big_endian(&sig_bytes[32..64]);
3752
3753        // Ensure the generated signature was indeed low-s (standard requirement)
3754        let half_n = n / 2;
3755        assert!(
3756            s <= half_n,
3757            "Generated signature was not low-s, cannot test high-s rejection"
3758        );
3759
3760        // Calculate high-s
3761        let s_high = n - s;
3762
3763        let mut sig_high_bytes = sig_bytes;
3764        // Replace s with s_high
3765        sig_high_bytes[32..64].copy_from_slice(&s_high.to_big_endian());
3766        // When flipping s to -s mod N, we must also flip the recovery ID (v) to maintain validity of the point R
3767        sig_high_bytes[64] ^= 1;
3768
3769        // 6. Verify that the high-s signature is rejected
3770        // EIP-2 requires rejecting s > N/2 to prevent malleability
3771        assert!(
3772            crypto.recover_signer(&sig_high_bytes, &payload).is_err(),
3773            "High-s signature should be rejected (EIP-2 compliance)"
3774        );
3775    }
3776
3777    #[test]
3778    fn encode_decode_low_size_tx() {
3779        let tx = Transaction::EIP2930Transaction(EIP2930Transaction::default());
3780        // Encode a separate copy so the original's cached_canonical stays uninit,
3781        // avoiding a false PartialEq mismatch with the decoded (uncached) tx.
3782        let tx_to_encode = tx.clone();
3783        let encoded = tx_to_encode.encode_to_vec();
3784        let decoded_tx = Transaction::decode(&encoded).unwrap();
3785        assert_eq!(tx, decoded_tx);
3786    }
3787
3788    #[test]
3789    fn test_eip1559_simple_transfer_size() {
3790        let tx = Transaction::EIP1559Transaction(EIP1559Transaction::default());
3791        assert_eq!(tx.encode_to_vec().len(), EIP1559_DEFAULT_SERIALIZED_LENGTH);
3792    }
3793
3794    #[test]
3795    fn test_cost_without_base_fee_eip4844_includes_blob_gas() {
3796        // Regression test for mempool balance check: for EIP-4844 txs,
3797        // cost_without_base_fee() MUST include blob_gas_used * max_fee_per_blob_gas.
3798        // Every peer client (geth, reth, nethermind, erigon, besu) does this.
3799        use crate::constants::GAS_PER_BLOB;
3800
3801        let max_fee_per_gas: u64 = 100;
3802        let gas: u64 = 21_000;
3803        let value = U256::from(7u64);
3804        let max_fee_per_blob_gas = U256::from(50u64);
3805        let blob_count: usize = 1;
3806
3807        let tx = Transaction::EIP4844Transaction(EIP4844Transaction {
3808            max_fee_per_gas,
3809            gas,
3810            value,
3811            max_fee_per_blob_gas,
3812            blob_versioned_hashes: vec![H256::zero(); blob_count],
3813            ..Default::default()
3814        });
3815
3816        let got = tx.cost_without_base_fee().expect("cost is computable");
3817
3818        let gas_cost = U256::from(max_fee_per_gas) * U256::from(gas);
3819        let blob_gas = U256::from(GAS_PER_BLOB) * U256::from(blob_count as u64);
3820        let blob_cost = blob_gas * max_fee_per_blob_gas;
3821        let expected = gas_cost + blob_cost + value;
3822
3823        assert_eq!(
3824            got, expected,
3825            "blob-gas term missing from cost_without_base_fee() for EIP-4844"
3826        );
3827    }
3828}