reth_primitives/transaction/
mod.rs

1//! Transaction types.
2
3use alloc::vec::Vec;
4use alloy_consensus::{
5    transaction::{RlpEcdsaTx, TxSeismicElements},
6    SignableTransaction, Signed, Transaction as _, TxEip1559, TxEip2930, TxEip4844,
7    TxEip4844Variant, TxEip7702, TxLegacy, TxSeismic, Typed2718, TypedTransaction,
8};
9use alloy_eips::{
10    eip2718::{Decodable2718, Eip2718Error, Eip2718Result, Encodable2718},
11    eip2930::AccessList,
12    eip712::{Decodable712, Eip712Result, TypedDataRequest},
13    eip7702::SignedAuthorization,
14};
15use alloy_primitives::{
16    keccak256, Address, Bytes, ChainId, PrimitiveSignature as Signature, TxHash, TxKind, B256, U256,
17};
18use alloy_rlp::{Decodable, Encodable, Error as RlpError, Header};
19use alloy_rpc_types_eth::TransactionRequest;
20use core::hash::{Hash, Hasher};
21use derive_more::{AsRef, Deref};
22use once_cell as _;
23#[cfg(not(feature = "std"))]
24use once_cell::sync::{Lazy as LazyLock, OnceCell as OnceLock};
25#[cfg(feature = "optimism")]
26use op_alloy_consensus::DepositTransaction;
27#[cfg(feature = "optimism")]
28use op_alloy_consensus::TxDeposit;
29use rayon::prelude::{IntoParallelIterator, ParallelIterator};
30use reth_primitives_traits::{InMemorySize, SignedTransaction};
31use reth_tracing::tracing::*;
32use revm_primitives::{AuthorizationList, TxEnv};
33use serde::{Deserialize, Serialize};
34use signature::decode_with_eip155_chain_id;
35#[cfg(feature = "std")]
36use std::sync::{LazyLock, OnceLock};
37
38pub use compat::FillTxEnv;
39pub use meta::TransactionMeta;
40pub use pooled::{PooledTransactionsElement, PooledTransactionsElementEcRecovered};
41pub use reth_primitives_traits::{
42    transaction::error::{
43        InvalidTransactionError, TransactionConversionError, TryFromRecoveredTransactionError,
44    },
45    WithEncoded,
46};
47pub use sidecar::BlobTransaction;
48pub use signature::{recover_signer, recover_signer_unchecked};
49pub use tx_type::TxType;
50
51/// Handling transaction signature operations, including signature recovery,
52/// applying chain IDs, and EIP-2 validation.
53pub mod signature;
54pub mod util;
55
56pub(crate) mod access_list;
57mod compat;
58mod meta;
59mod pooled;
60mod sidecar;
61mod tx_type;
62
63#[cfg(any(test, feature = "reth-codec"))]
64pub use tx_type::{
65    COMPACT_EXTENDED_IDENTIFIER_FLAG, COMPACT_IDENTIFIER_EIP1559, COMPACT_IDENTIFIER_EIP2930,
66    COMPACT_IDENTIFIER_LEGACY,
67};
68
69/// Expected number of transactions where we can expect a speed-up by recovering the senders in
70/// parallel.
71pub static PARALLEL_SENDER_RECOVERY_THRESHOLD: LazyLock<usize> =
72    LazyLock::new(|| match rayon::current_num_threads() {
73        0..=1 => usize::MAX,
74        2..=8 => 10,
75        _ => 5,
76    });
77
78/// A raw transaction.
79///
80/// Transaction types were introduced in [EIP-2718](https://eips.ethereum.org/EIPS/eip-2718).
81#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, derive_more::From)]
82#[cfg_attr(any(test, feature = "reth-codec"), reth_codecs::add_arbitrary_tests(compact))]
83pub enum Transaction {
84    /// Legacy transaction (type `0x0`).
85    ///
86    /// Traditional Ethereum transactions, containing parameters `nonce`, `gasPrice`, `gasLimit`,
87    /// `to`, `value`, `data`, `v`, `r`, and `s`.
88    ///
89    /// These transactions do not utilize access lists nor do they incorporate EIP-1559 fee market
90    /// changes.
91    Legacy(TxLegacy),
92    /// Transaction with an [`AccessList`] ([EIP-2930](https://eips.ethereum.org/EIPS/eip-2930)), type `0x1`.
93    ///
94    /// The `accessList` specifies an array of addresses and storage keys that the transaction
95    /// plans to access, enabling gas savings on cross-contract calls by pre-declaring the accessed
96    /// contract and storage slots.
97    Eip2930(TxEip2930),
98    /// A transaction with a priority fee ([EIP-1559](https://eips.ethereum.org/EIPS/eip-1559)), type `0x2`.
99    ///
100    /// Unlike traditional transactions, EIP-1559 transactions use an in-protocol, dynamically
101    /// changing base fee per gas, adjusted at each block to manage network congestion.
102    ///
103    /// - `maxPriorityFeePerGas`, specifying the maximum fee above the base fee the sender is
104    ///   willing to pay
105    /// - `maxFeePerGas`, setting the maximum total fee the sender is willing to pay.
106    ///
107    /// The base fee is burned, while the priority fee is paid to the miner who includes the
108    /// transaction, incentivizing miners to include transactions with higher priority fees per
109    /// gas.
110    Eip1559(TxEip1559),
111    /// Shard Blob Transactions ([EIP-4844](https://eips.ethereum.org/EIPS/eip-4844)), type `0x3`.
112    ///
113    /// Shard Blob Transactions introduce a new transaction type called a blob-carrying transaction
114    /// to reduce gas costs. These transactions are similar to regular Ethereum transactions but
115    /// include additional data called a blob.
116    ///
117    /// Blobs are larger (~125 kB) and cheaper than the current calldata, providing an immutable
118    /// and read-only memory for storing transaction data.
119    ///
120    /// EIP-4844, also known as proto-danksharding, implements the framework and logic of
121    /// danksharding, introducing new transaction formats and verification rules.
122    Eip4844(TxEip4844),
123    /// EOA Set Code Transactions ([EIP-7702](https://eips.ethereum.org/EIPS/eip-7702)), type `0x4`.
124    ///
125    /// EOA Set Code Transactions give the ability to temporarily set contract code for an
126    /// EOA for a single transaction. This allows for temporarily adding smart contract
127    /// functionality to the EOA.
128    Eip7702(TxEip7702),
129    /// Optimism deposit transaction.
130    #[cfg(feature = "optimism")]
131    Deposit(TxDeposit),
132    /// Seismic transaction
133    Seismic(TxSeismic),
134}
135
136#[cfg(feature = "optimism")]
137impl DepositTransaction for Transaction {
138    fn source_hash(&self) -> Option<B256> {
139        match self {
140            Self::Deposit(tx) => tx.source_hash(),
141            _ => None,
142        }
143    }
144    fn mint(&self) -> Option<u128> {
145        match self {
146            Self::Deposit(tx) => tx.mint(),
147            _ => None,
148        }
149    }
150    fn is_system_transaction(&self) -> bool {
151        match self {
152            Self::Deposit(tx) => tx.is_system_transaction(),
153            _ => false,
154        }
155    }
156    fn is_deposit(&self) -> bool {
157        matches!(self, Self::Deposit(_))
158    }
159}
160
161#[cfg(any(test, feature = "arbitrary"))]
162impl<'a> arbitrary::Arbitrary<'a> for Transaction {
163    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
164        let mut tx = match TxType::arbitrary(u)? {
165            TxType::Legacy => {
166                let tx = TxLegacy::arbitrary(u)?;
167                Self::Legacy(tx)
168            }
169            TxType::Eip2930 => {
170                let tx = TxEip2930::arbitrary(u)?;
171                Self::Eip2930(tx)
172            }
173            TxType::Eip1559 => {
174                let tx = TxEip1559::arbitrary(u)?;
175                Self::Eip1559(tx)
176            }
177            TxType::Eip4844 => {
178                let tx = TxEip4844::arbitrary(u)?;
179                Self::Eip4844(tx)
180            }
181
182            TxType::Eip7702 => {
183                let tx = TxEip7702::arbitrary(u)?;
184                Self::Eip7702(tx)
185            }
186            TxType::Seismic => {
187                let tx = TxSeismic::arbitrary(u)?;
188                Self::Seismic(tx)
189            }
190            #[cfg(feature = "optimism")]
191            TxType::Deposit => {
192                let tx = TxDeposit::arbitrary(u)?;
193                Self::Deposit(tx)
194            }
195        };
196
197        // Otherwise we might overflow when calculating `v` on `recalculate_hash`
198        if let Some(chain_id) = tx.chain_id() {
199            tx.set_chain_id(chain_id % (u64::MAX / 2 - 36));
200        }
201
202        Ok(tx)
203    }
204}
205
206impl Typed2718 for Transaction {
207    fn ty(&self) -> u8 {
208        match self {
209            Self::Legacy(tx) => tx.ty(),
210            Self::Eip2930(tx) => tx.ty(),
211            Self::Eip1559(tx) => tx.ty(),
212            Self::Eip4844(tx) => tx.ty(),
213            Self::Eip7702(tx) => tx.ty(),
214            Self::Seismic(tx) => tx.ty(),
215            #[cfg(feature = "optimism")]
216            Self::Deposit(tx) => tx.ty(),
217        }
218    }
219}
220
221// === impl Transaction ===
222
223impl Transaction {
224    /// Heavy operation that return signature hash over rlp encoded transaction.
225    /// It is only for signature signing or signer recovery.
226    pub fn signature_hash(&self) -> B256 {
227        match self {
228            Self::Legacy(tx) => tx.signature_hash(),
229            Self::Eip2930(tx) => tx.signature_hash(),
230            Self::Eip1559(tx) => tx.signature_hash(),
231            Self::Eip4844(tx) => tx.signature_hash(),
232            Self::Eip7702(tx) => tx.signature_hash(),
233            Self::Seismic(tx) => tx.signature_hash(),
234            #[cfg(feature = "optimism")]
235            Self::Deposit(_) => B256::ZERO,
236        }
237    }
238
239    /// Sets the transaction's chain id to the provided value.
240    pub fn set_chain_id(&mut self, chain_id: u64) {
241        match self {
242            Self::Legacy(TxLegacy { chain_id: ref mut c, .. }) => *c = Some(chain_id),
243            Self::Eip2930(TxEip2930 { chain_id: ref mut c, .. }) |
244            Self::Seismic(TxSeismic { chain_id: ref mut c, .. }) |
245            Self::Eip1559(TxEip1559 { chain_id: ref mut c, .. }) |
246            Self::Eip4844(TxEip4844 { chain_id: ref mut c, .. }) |
247            Self::Eip7702(TxEip7702 { chain_id: ref mut c, .. }) => *c = chain_id,
248            #[cfg(feature = "optimism")]
249            Self::Deposit(_) => { /* noop */ }
250        }
251    }
252
253    /// Get the transaction's type
254    pub const fn tx_type(&self) -> TxType {
255        match self {
256            Self::Legacy(_) => TxType::Legacy,
257            Self::Eip2930(_) => TxType::Eip2930,
258            Self::Eip1559(_) => TxType::Eip1559,
259            Self::Eip4844(_) => TxType::Eip4844,
260            Self::Eip7702(_) => TxType::Eip7702,
261            Self::Seismic(_) => TxType::Seismic,
262            #[cfg(feature = "optimism")]
263            Self::Deposit(_) => TxType::Deposit,
264        }
265    }
266
267    /// Returns the blob gas used for all blobs of the EIP-4844 transaction if it is an EIP-4844
268    /// transaction.
269    ///
270    /// This is the number of blobs times the
271    /// [`DATA_GAS_PER_BLOB`](alloy_eips::eip4844::DATA_GAS_PER_BLOB) a single blob consumes.
272    pub fn blob_gas_used(&self) -> Option<u64> {
273        self.as_eip4844().map(TxEip4844::blob_gas)
274    }
275
276    /// Returns the effective miner gas tip cap (`gasTipCap`) for the given base fee:
277    /// `min(maxFeePerGas - baseFee, maxPriorityFeePerGas)`
278    ///
279    /// If the base fee is `None`, the `max_priority_fee_per_gas`, or gas price for non-EIP1559
280    /// transactions is returned.
281    ///
282    /// Returns `None` if the basefee is higher than the [`Transaction::max_fee_per_gas`].
283    pub fn effective_tip_per_gas(&self, base_fee: Option<u64>) -> Option<u128> {
284        let base_fee = match base_fee {
285            Some(base_fee) => base_fee as u128,
286            None => return Some(self.priority_fee_or_price()),
287        };
288
289        let max_fee_per_gas = self.max_fee_per_gas();
290
291        // Check if max_fee_per_gas is less than base_fee
292        if max_fee_per_gas < base_fee {
293            return None
294        }
295
296        // Calculate the difference between max_fee_per_gas and base_fee
297        let fee = max_fee_per_gas - base_fee;
298
299        // Compare the fee with max_priority_fee_per_gas (or gas price for non-EIP1559 transactions)
300        if let Some(priority_fee) = self.max_priority_fee_per_gas() {
301            Some(fee.min(priority_fee))
302        } else {
303            Some(fee)
304        }
305    }
306
307    /// This encodes the transaction _without_ the signature, and is only suitable for creating a
308    /// hash intended for signing.
309    pub fn encode_for_signing(&self, out: &mut dyn bytes::BufMut) {
310        match self {
311            Self::Legacy(tx) => tx.encode_for_signing(out),
312            Self::Eip2930(tx) => tx.encode_for_signing(out),
313            Self::Eip1559(tx) => tx.encode_for_signing(out),
314            Self::Eip4844(tx) => tx.encode_for_signing(out),
315            Self::Eip7702(tx) => tx.encode_for_signing(out),
316            Self::Seismic(tx) => tx.encode_for_signing(out),
317            #[cfg(feature = "optimism")]
318            Self::Deposit(_) => {}
319        }
320    }
321
322    /// Produces EIP-2718 encoding of the transaction
323    pub fn eip2718_encode(&self, signature: &Signature, out: &mut dyn bytes::BufMut) {
324        match self {
325            Self::Legacy(legacy_tx) => {
326                // do nothing w/ with_header
327                legacy_tx.eip2718_encode(signature, out);
328            }
329            Self::Eip2930(access_list_tx) => {
330                access_list_tx.eip2718_encode(signature, out);
331            }
332            Self::Eip1559(dynamic_fee_tx) => {
333                dynamic_fee_tx.eip2718_encode(signature, out);
334            }
335            Self::Eip4844(blob_tx) => blob_tx.eip2718_encode(signature, out),
336            Self::Eip7702(set_code_tx) => {
337                set_code_tx.eip2718_encode(signature, out);
338            }
339            Self::Seismic(seismic_tx) => {
340                seismic_tx.eip2718_encode(signature, out);
341            }
342            #[cfg(feature = "optimism")]
343            Self::Deposit(deposit_tx) => deposit_tx.encode_2718(out),
344        }
345    }
346
347    /// This sets the transaction's gas limit.
348    pub fn set_gas_limit(&mut self, gas_limit: u64) {
349        match self {
350            Self::Legacy(tx) => tx.gas_limit = gas_limit,
351            Self::Seismic(tx) => tx.gas_limit = gas_limit,
352            Self::Eip2930(tx) => tx.gas_limit = gas_limit,
353            Self::Eip1559(tx) => tx.gas_limit = gas_limit,
354            Self::Eip4844(tx) => tx.gas_limit = gas_limit,
355            Self::Eip7702(tx) => tx.gas_limit = gas_limit,
356            #[cfg(feature = "optimism")]
357            Self::Deposit(tx) => tx.gas_limit = gas_limit,
358        }
359    }
360
361    /// This sets the transaction's nonce.
362    pub fn set_nonce(&mut self, nonce: u64) {
363        match self {
364            Self::Legacy(tx) => tx.nonce = nonce,
365            Self::Seismic(tx) => tx.nonce = nonce,
366            Self::Eip2930(tx) => tx.nonce = nonce,
367            Self::Eip1559(tx) => tx.nonce = nonce,
368            Self::Eip4844(tx) => tx.nonce = nonce,
369            Self::Eip7702(tx) => tx.nonce = nonce,
370            #[cfg(feature = "optimism")]
371            Self::Deposit(_) => { /* noop */ }
372        }
373    }
374
375    /// This sets the transaction's value.
376    pub fn set_value(&mut self, value: U256) {
377        match self {
378            Self::Legacy(tx) => tx.value = value,
379            Self::Seismic(tx) => tx.value = value,
380            Self::Eip2930(tx) => tx.value = value,
381            Self::Eip1559(tx) => tx.value = value,
382            Self::Eip4844(tx) => tx.value = value,
383            Self::Eip7702(tx) => tx.value = value,
384            #[cfg(feature = "optimism")]
385            Self::Deposit(tx) => tx.value = value,
386        }
387    }
388
389    /// This sets the transaction's input field.
390    pub fn set_input(&mut self, input: Bytes) {
391        match self {
392            Self::Legacy(tx) => tx.input = input,
393            Self::Seismic(tx) => tx.input = input,
394            Self::Eip2930(tx) => tx.input = input,
395            Self::Eip1559(tx) => tx.input = input,
396            Self::Eip4844(tx) => tx.input = input,
397            Self::Eip7702(tx) => tx.input = input,
398            #[cfg(feature = "optimism")]
399            Self::Deposit(tx) => tx.input = input,
400        }
401    }
402
403    /// Returns true if the transaction is a legacy transaction.
404    #[inline]
405    pub const fn is_legacy(&self) -> bool {
406        matches!(self, Self::Legacy(_))
407    }
408
409    /// Returns true if the transaction is an EIP-2930 transaction.
410    #[inline]
411    pub const fn is_eip2930(&self) -> bool {
412        matches!(self, Self::Eip2930(_))
413    }
414
415    /// Returns true if the transaction is an EIP-1559 transaction.
416    #[inline]
417    pub const fn is_eip1559(&self) -> bool {
418        matches!(self, Self::Eip1559(_))
419    }
420
421    /// Returns true if the transaction is an EIP-4844 transaction.
422    #[inline]
423    pub const fn is_eip4844(&self) -> bool {
424        matches!(self, Self::Eip4844(_))
425    }
426
427    /// Returns true if the transaction is an EIP-7702 transaction.
428    #[inline]
429    pub const fn is_eip7702(&self) -> bool {
430        matches!(self, Self::Eip7702(_))
431    }
432
433    /// Returns the [`TxLegacy`] variant if the transaction is a legacy transaction.
434    pub const fn as_legacy(&self) -> Option<&TxLegacy> {
435        match self {
436            Self::Legacy(tx) => Some(tx),
437            _ => None,
438        }
439    }
440
441    /// Returns the [`TxEip2930`] variant if the transaction is an EIP-2930 transaction.
442    pub const fn as_eip2930(&self) -> Option<&TxEip2930> {
443        match self {
444            Self::Eip2930(tx) => Some(tx),
445            _ => None,
446        }
447    }
448
449    /// Returns the [`TxEip1559`] variant if the transaction is an EIP-1559 transaction.
450    pub const fn as_eip1559(&self) -> Option<&TxEip1559> {
451        match self {
452            Self::Eip1559(tx) => Some(tx),
453            _ => None,
454        }
455    }
456
457    /// Returns the [`TxEip4844`] variant if the transaction is an EIP-4844 transaction.
458    pub const fn as_eip4844(&self) -> Option<&TxEip4844> {
459        match self {
460            Self::Eip4844(tx) => Some(tx),
461            _ => None,
462        }
463    }
464
465    /// Returns the [`TxEip7702`] variant if the transaction is an EIP-7702 transaction.
466    pub const fn as_eip7702(&self) -> Option<&TxEip7702> {
467        match self {
468            Self::Eip7702(tx) => Some(tx),
469            _ => None,
470        }
471    }
472}
473
474impl InMemorySize for Transaction {
475    /// Calculates a heuristic for the in-memory size of the [Transaction].
476    #[inline]
477    fn size(&self) -> usize {
478        match self {
479            Self::Legacy(tx) => tx.size(),
480            Self::Seismic(tx) => tx.size(),
481            Self::Eip2930(tx) => tx.size(),
482            Self::Eip1559(tx) => tx.size(),
483            Self::Eip4844(tx) => tx.size(),
484            Self::Eip7702(tx) => tx.size(),
485            #[cfg(feature = "optimism")]
486            Self::Deposit(tx) => tx.size(),
487        }
488    }
489}
490
491#[cfg(any(test, feature = "reth-codec"))]
492impl reth_codecs::Compact for Transaction {
493    // Serializes the TxType to the buffer if necessary, returning 2 bits of the type as an
494    // identifier instead of the length.
495    fn to_compact<B>(&self, buf: &mut B) -> usize
496    where
497        B: bytes::BufMut + AsMut<[u8]>,
498    {
499        let identifier = self.tx_type().to_compact(buf);
500        match self {
501            Self::Legacy(tx) => {
502                tx.to_compact(buf);
503            }
504            Self::Eip2930(tx) => {
505                tx.to_compact(buf);
506            }
507            Self::Eip1559(tx) => {
508                tx.to_compact(buf);
509            }
510            Self::Eip4844(tx) => {
511                tx.to_compact(buf);
512            }
513            Self::Eip7702(tx) => {
514                tx.to_compact(buf);
515            }
516            Self::Seismic(tx) => {
517                tx.to_compact(buf);
518            }
519            #[cfg(feature = "optimism")]
520            Self::Deposit(tx) => {
521                tx.to_compact(buf);
522            }
523        }
524        identifier
525    }
526
527    // For backwards compatibility purposes, only 2 bits of the type are encoded in the identifier
528    // parameter. In the case of a [`COMPACT_EXTENDED_IDENTIFIER_FLAG`], the full transaction type
529    // is read from the buffer as a single byte.
530    //
531    // # Panics
532    //
533    // A panic will be triggered if an identifier larger than 3 is passed from the database. For
534    // optimism a identifier with value [`DEPOSIT_TX_TYPE_ID`] is allowed.
535    fn from_compact(mut buf: &[u8], identifier: usize) -> (Self, &[u8]) {
536        use bytes::Buf;
537
538        match identifier {
539            reth_codecs::txtype::COMPACT_IDENTIFIER_LEGACY => {
540                let (tx, buf) = TxLegacy::from_compact(buf, buf.len());
541                (Self::Legacy(tx), buf)
542            }
543            reth_codecs::txtype::COMPACT_IDENTIFIER_EIP2930 => {
544                let (tx, buf) = TxEip2930::from_compact(buf, buf.len());
545                (Self::Eip2930(tx), buf)
546            }
547            reth_codecs::txtype::COMPACT_IDENTIFIER_EIP1559 => {
548                let (tx, buf) = TxEip1559::from_compact(buf, buf.len());
549                (Self::Eip1559(tx), buf)
550            }
551            reth_codecs::txtype::COMPACT_EXTENDED_IDENTIFIER_FLAG => {
552                // An identifier of 3 indicates that the transaction type did not fit into
553                // the backwards compatible 2 bit identifier, their transaction types are
554                // larger than 2 bits (eg. 4844 and Deposit Transactions). In this case,
555                // we need to read the concrete transaction type from the buffer by
556                // reading the full 8 bits (single byte) and match on this transaction type.
557                let identifier = buf.get_u8();
558                match identifier {
559                    alloy_consensus::constants::EIP4844_TX_TYPE_ID => {
560                        let (tx, buf) = TxEip4844::from_compact(buf, buf.len());
561                        (Self::Eip4844(tx), buf)
562                    }
563                    alloy_consensus::constants::EIP7702_TX_TYPE_ID => {
564                        let (tx, buf) = TxEip7702::from_compact(buf, buf.len());
565                        (Self::Eip7702(tx), buf)
566                    }
567                    alloy_consensus::constants::SEISMIC_TX_TYPE_ID => {
568                        let (tx, buf) = TxSeismic::from_compact(buf, buf.len());
569                        (Self::Seismic(tx), buf)
570                    }
571                    #[cfg(feature = "optimism")]
572                    op_alloy_consensus::DEPOSIT_TX_TYPE_ID => {
573                        let (tx, buf) = TxDeposit::from_compact(buf, buf.len());
574                        (Self::Deposit(tx), buf)
575                    }
576                    _ => unreachable!(
577                        "Junk data in database: unknown Transaction variant: {identifier}"
578                    ),
579                }
580            }
581            _ => unreachable!("Junk data in database: unknown Transaction variant: {identifier}"),
582        }
583    }
584}
585
586impl Default for Transaction {
587    fn default() -> Self {
588        Self::Legacy(TxLegacy::default())
589    }
590}
591
592impl alloy_consensus::transaction::ShieldableTransaction for Transaction {
593    fn shield_input(&mut self) {
594        match self {
595            Self::Seismic(tx) => {
596                tx.shield_input();
597            }
598            _ => {}
599        }
600    }
601}
602impl alloy_consensus::Transaction for Transaction {
603    fn chain_id(&self) -> Option<ChainId> {
604        match self {
605            Self::Legacy(tx) => tx.chain_id(),
606            Self::Seismic(tx) => tx.chain_id(),
607            Self::Eip2930(tx) => tx.chain_id(),
608            Self::Eip1559(tx) => tx.chain_id(),
609            Self::Eip4844(tx) => tx.chain_id(),
610            Self::Eip7702(tx) => tx.chain_id(),
611            #[cfg(feature = "optimism")]
612            Self::Deposit(tx) => tx.chain_id(),
613        }
614    }
615
616    fn nonce(&self) -> u64 {
617        match self {
618            Self::Legacy(tx) => tx.nonce(),
619            Self::Seismic(tx) => tx.nonce(),
620            Self::Eip2930(tx) => tx.nonce(),
621            Self::Eip1559(tx) => tx.nonce(),
622            Self::Eip4844(tx) => tx.nonce(),
623            Self::Eip7702(tx) => tx.nonce(),
624            #[cfg(feature = "optimism")]
625            Self::Deposit(tx) => tx.nonce(),
626        }
627    }
628
629    fn gas_limit(&self) -> u64 {
630        match self {
631            Self::Legacy(tx) => tx.gas_limit(),
632            Self::Seismic(tx) => tx.gas_limit(),
633            Self::Eip2930(tx) => tx.gas_limit(),
634            Self::Eip1559(tx) => tx.gas_limit(),
635            Self::Eip4844(tx) => tx.gas_limit(),
636            Self::Eip7702(tx) => tx.gas_limit(),
637            #[cfg(feature = "optimism")]
638            Self::Deposit(tx) => tx.gas_limit(),
639        }
640    }
641
642    fn gas_price(&self) -> Option<u128> {
643        match self {
644            Self::Legacy(tx) => tx.gas_price(),
645            Self::Seismic(tx) => tx.gas_price(),
646            Self::Eip2930(tx) => tx.gas_price(),
647            Self::Eip1559(tx) => tx.gas_price(),
648            Self::Eip4844(tx) => tx.gas_price(),
649            Self::Eip7702(tx) => tx.gas_price(),
650            #[cfg(feature = "optimism")]
651            Self::Deposit(tx) => tx.gas_price(),
652        }
653    }
654
655    fn max_fee_per_gas(&self) -> u128 {
656        match self {
657            Self::Legacy(tx) => tx.max_fee_per_gas(),
658            Self::Seismic(tx) => tx.max_fee_per_gas(),
659            Self::Eip2930(tx) => tx.max_fee_per_gas(),
660            Self::Eip1559(tx) => tx.max_fee_per_gas(),
661            Self::Eip4844(tx) => tx.max_fee_per_gas(),
662            Self::Eip7702(tx) => tx.max_fee_per_gas(),
663            #[cfg(feature = "optimism")]
664            Self::Deposit(tx) => tx.max_fee_per_gas(),
665        }
666    }
667
668    fn max_priority_fee_per_gas(&self) -> Option<u128> {
669        match self {
670            Self::Legacy(tx) => tx.max_priority_fee_per_gas(),
671            Self::Seismic(tx) => tx.max_priority_fee_per_gas(),
672            Self::Eip2930(tx) => tx.max_priority_fee_per_gas(),
673            Self::Eip1559(tx) => tx.max_priority_fee_per_gas(),
674            Self::Eip4844(tx) => tx.max_priority_fee_per_gas(),
675            Self::Eip7702(tx) => tx.max_priority_fee_per_gas(),
676            #[cfg(feature = "optimism")]
677            Self::Deposit(tx) => tx.max_priority_fee_per_gas(),
678        }
679    }
680
681    fn max_fee_per_blob_gas(&self) -> Option<u128> {
682        match self {
683            Self::Legacy(tx) => tx.max_fee_per_blob_gas(),
684            Self::Seismic(tx) => tx.max_fee_per_blob_gas(),
685            Self::Eip2930(tx) => tx.max_fee_per_blob_gas(),
686            Self::Eip1559(tx) => tx.max_fee_per_blob_gas(),
687            Self::Eip4844(tx) => tx.max_fee_per_blob_gas(),
688            Self::Eip7702(tx) => tx.max_fee_per_blob_gas(),
689            #[cfg(feature = "optimism")]
690            Self::Deposit(tx) => tx.max_fee_per_blob_gas(),
691        }
692    }
693
694    fn priority_fee_or_price(&self) -> u128 {
695        match self {
696            Self::Legacy(tx) => tx.priority_fee_or_price(),
697            Self::Seismic(tx) => tx.priority_fee_or_price(),
698            Self::Eip2930(tx) => tx.priority_fee_or_price(),
699            Self::Eip1559(tx) => tx.priority_fee_or_price(),
700            Self::Eip4844(tx) => tx.priority_fee_or_price(),
701            Self::Eip7702(tx) => tx.priority_fee_or_price(),
702            #[cfg(feature = "optimism")]
703            Self::Deposit(tx) => tx.priority_fee_or_price(),
704        }
705    }
706
707    fn effective_gas_price(&self, base_fee: Option<u64>) -> u128 {
708        match self {
709            Self::Legacy(tx) => tx.effective_gas_price(base_fee),
710            Self::Seismic(tx) => tx.effective_gas_price(base_fee),
711            Self::Eip2930(tx) => tx.effective_gas_price(base_fee),
712            Self::Eip1559(tx) => tx.effective_gas_price(base_fee),
713            Self::Eip4844(tx) => tx.effective_gas_price(base_fee),
714            Self::Eip7702(tx) => tx.effective_gas_price(base_fee),
715            #[cfg(feature = "optimism")]
716            Self::Deposit(tx) => tx.effective_gas_price(base_fee),
717        }
718    }
719
720    fn is_dynamic_fee(&self) -> bool {
721        match self {
722            Self::Legacy(_) | Self::Eip2930(_) | Self::Seismic(_) => false,
723            Self::Eip1559(_) | Self::Eip4844(_) | Self::Eip7702(_) => true,
724            #[cfg(feature = "optimism")]
725            Self::Deposit(_) => false,
726        }
727    }
728
729    fn kind(&self) -> TxKind {
730        match self {
731            Self::Legacy(tx) => tx.kind(),
732            Self::Seismic(tx) => tx.kind(),
733            Self::Eip2930(tx) => tx.kind(),
734            Self::Eip1559(tx) => tx.kind(),
735            Self::Eip4844(tx) => tx.kind(),
736            Self::Eip7702(tx) => tx.kind(),
737            #[cfg(feature = "optimism")]
738            Self::Deposit(tx) => tx.kind(),
739        }
740    }
741
742    fn is_create(&self) -> bool {
743        match self {
744            Self::Legacy(tx) => tx.is_create(),
745            Self::Seismic(tx) => tx.is_create(),
746            Self::Eip2930(tx) => tx.is_create(),
747            Self::Eip1559(tx) => tx.is_create(),
748            Self::Eip4844(tx) => tx.is_create(),
749            Self::Eip7702(tx) => tx.is_create(),
750            #[cfg(feature = "optimism")]
751            Self::Deposit(tx) => tx.is_create(),
752        }
753    }
754
755    fn value(&self) -> U256 {
756        match self {
757            Self::Legacy(tx) => tx.value(),
758            Self::Seismic(tx) => tx.value(),
759            Self::Eip2930(tx) => tx.value(),
760            Self::Eip1559(tx) => tx.value(),
761            Self::Eip4844(tx) => tx.value(),
762            Self::Eip7702(tx) => tx.value(),
763            #[cfg(feature = "optimism")]
764            Self::Deposit(tx) => tx.value(),
765        }
766    }
767
768    fn input(&self) -> &Bytes {
769        match self {
770            Self::Legacy(tx) => tx.input(),
771            Self::Seismic(tx) => tx.input(),
772            Self::Eip2930(tx) => tx.input(),
773            Self::Eip1559(tx) => tx.input(),
774            Self::Eip4844(tx) => tx.input(),
775            Self::Eip7702(tx) => tx.input(),
776            #[cfg(feature = "optimism")]
777            Self::Deposit(tx) => tx.input(),
778        }
779    }
780
781    fn access_list(&self) -> Option<&AccessList> {
782        match self {
783            Self::Legacy(tx) => tx.access_list(),
784            Self::Seismic(tx) => tx.access_list(),
785            Self::Eip2930(tx) => tx.access_list(),
786            Self::Eip1559(tx) => tx.access_list(),
787            Self::Eip4844(tx) => tx.access_list(),
788            Self::Eip7702(tx) => tx.access_list(),
789            #[cfg(feature = "optimism")]
790            Self::Deposit(tx) => tx.access_list(),
791        }
792    }
793
794    fn blob_versioned_hashes(&self) -> Option<&[B256]> {
795        match self {
796            Self::Legacy(tx) => tx.blob_versioned_hashes(),
797            Self::Seismic(tx) => tx.blob_versioned_hashes(),
798            Self::Eip2930(tx) => tx.blob_versioned_hashes(),
799            Self::Eip1559(tx) => tx.blob_versioned_hashes(),
800            Self::Eip4844(tx) => tx.blob_versioned_hashes(),
801            Self::Eip7702(tx) => tx.blob_versioned_hashes(),
802            #[cfg(feature = "optimism")]
803            Self::Deposit(tx) => tx.blob_versioned_hashes(),
804        }
805    }
806
807    fn authorization_list(&self) -> Option<&[SignedAuthorization]> {
808        match self {
809            Self::Legacy(tx) => tx.authorization_list(),
810            Self::Seismic(tx) => tx.authorization_list(),
811            Self::Eip2930(tx) => tx.authorization_list(),
812            Self::Eip1559(tx) => tx.authorization_list(),
813            Self::Eip4844(tx) => tx.authorization_list(),
814            Self::Eip7702(tx) => tx.authorization_list(),
815            #[cfg(feature = "optimism")]
816            Self::Deposit(tx) => tx.authorization_list(),
817        }
818    }
819
820    fn seismic_elements(&self) -> Option<&TxSeismicElements> {
821        match self {
822            Self::Seismic(tx) => tx.seismic_elements(),
823            _ => None,
824        }
825    }
826}
827
828impl From<TxEip4844Variant> for Transaction {
829    fn from(value: TxEip4844Variant) -> Self {
830        match value {
831            TxEip4844Variant::TxEip4844(tx) => tx.into(),
832            TxEip4844Variant::TxEip4844WithSidecar(tx) => tx.tx.into(),
833        }
834    }
835}
836
837impl From<TypedTransaction> for Transaction {
838    fn from(value: TypedTransaction) -> Self {
839        match value {
840            TypedTransaction::Legacy(tx) => tx.into(),
841            TypedTransaction::Seismic(tx) => tx.into(),
842            TypedTransaction::Eip2930(tx) => tx.into(),
843            TypedTransaction::Eip1559(tx) => tx.into(),
844            TypedTransaction::Eip4844(tx) => tx.into(),
845            TypedTransaction::Eip7702(tx) => tx.into(),
846        }
847    }
848}
849
850/// Signed transaction.
851#[cfg_attr(any(test, feature = "reth-codec"), reth_codecs::add_arbitrary_tests(rlp))]
852#[derive(Debug, Clone, Eq, AsRef, Deref, Serialize, Deserialize)]
853pub struct TransactionSigned {
854    /// Transaction hash
855    #[serde(skip)]
856    pub hash: OnceLock<TxHash>,
857    /// The transaction signature values
858    pub signature: Signature,
859    /// Raw transaction info
860    #[deref]
861    #[as_ref]
862    pub transaction: Transaction,
863}
864
865impl Default for TransactionSigned {
866    fn default() -> Self {
867        Self {
868            hash: Default::default(),
869            signature: Signature::test_signature(),
870            transaction: Default::default(),
871        }
872    }
873}
874
875impl AsRef<Self> for TransactionSigned {
876    fn as_ref(&self) -> &Self {
877        self
878    }
879}
880
881impl Hash for TransactionSigned {
882    fn hash<H: Hasher>(&self, state: &mut H) {
883        self.signature.hash(state);
884        self.transaction.hash(state);
885    }
886}
887
888impl PartialEq for TransactionSigned {
889    fn eq(&self, other: &Self) -> bool {
890        self.signature == other.signature &&
891            self.transaction == other.transaction &&
892            self.tx_hash() == other.tx_hash()
893    }
894}
895
896impl Typed2718 for TransactionSigned {
897    fn ty(&self) -> u8 {
898        self.deref().ty()
899    }
900}
901
902// === impl TransactionSigned ===
903
904impl TransactionSigned {
905    /// Creates a new signed transaction from the given parts.
906    pub fn new(transaction: Transaction, signature: Signature, hash: B256) -> Self {
907        Self { hash: hash.into(), signature, transaction }
908    }
909
910    /// Creates a new signed transaction from the given transaction and signature without the hash.
911    ///
912    /// Note: this only calculates the hash on the first [`TransactionSigned::hash`] call.
913    pub fn new_unhashed(transaction: Transaction, signature: Signature) -> Self {
914        Self { hash: Default::default(), signature, transaction }
915    }
916
917    /// Transaction
918    pub const fn transaction(&self) -> &Transaction {
919        &self.transaction
920    }
921
922    /// Tries to convert a [`TransactionSigned`] into a [`PooledTransactionsElement`].
923    ///
924    /// This function used as a helper to convert from a decoded p2p broadcast message to
925    /// [`PooledTransactionsElement`]. Since [`BlobTransaction`] is disallowed to be broadcasted on
926    /// p2p, return an err if `tx` is [`Transaction::Eip4844`].
927    pub fn try_into_pooled(self) -> Result<PooledTransactionsElement, Self> {
928        let hash = self.hash();
929        match self {
930            Self { transaction: Transaction::Legacy(tx), signature, .. } => {
931                Ok(PooledTransactionsElement::Legacy(Signed::new_unchecked(tx, signature, hash)))
932            }
933            Self { transaction: Transaction::Eip2930(tx), signature, .. } => {
934                Ok(PooledTransactionsElement::Eip2930(Signed::new_unchecked(tx, signature, hash)))
935            }
936            Self { transaction: Transaction::Eip1559(tx), signature, .. } => {
937                Ok(PooledTransactionsElement::Eip1559(Signed::new_unchecked(tx, signature, hash)))
938            }
939            Self { transaction: Transaction::Eip7702(tx), signature, .. } => {
940                Ok(PooledTransactionsElement::Eip7702(Signed::new_unchecked(tx, signature, hash)))
941            }
942            Self { transaction: Transaction::Seismic(tx), signature, .. } => {
943                Ok(PooledTransactionsElement::Seismic(Signed::new_unchecked(tx, signature, hash)))
944            }
945            // Not supported because missing blob sidecar
946            tx @ Self { transaction: Transaction::Eip4844(_), .. } => Err(tx),
947            #[cfg(feature = "optimism")]
948            // Not supported because deposit transactions are never pooled
949            tx @ Self { transaction: Transaction::Deposit(_), .. } => Err(tx),
950        }
951    }
952
953    /// Transaction hash. Used to identify transaction.
954    pub fn hash(&self) -> TxHash {
955        *self.tx_hash()
956    }
957
958    /// Recovers a list of signers from a transaction list iterator.
959    ///
960    /// Returns `None`, if some transaction's signature is invalid, see also
961    /// [`Self::recover_signer`].
962    pub fn recover_signers<'a, T>(txes: T, num_txes: usize) -> Option<Vec<Address>>
963    where
964        T: IntoParallelIterator<Item = &'a Self> + IntoIterator<Item = &'a Self> + Send,
965    {
966        if num_txes < *PARALLEL_SENDER_RECOVERY_THRESHOLD {
967            txes.into_iter().map(|tx| tx.recover_signer()).collect()
968        } else {
969            txes.into_par_iter().map(|tx| tx.recover_signer()).collect()
970        }
971    }
972
973    /// Recovers a list of signers from a transaction list iterator _without ensuring that the
974    /// signature has a low `s` value_.
975    ///
976    /// Returns `None`, if some transaction's signature is invalid, see also
977    /// [`Self::recover_signer_unchecked`].
978    pub fn recover_signers_unchecked<'a, T>(txes: T, num_txes: usize) -> Option<Vec<Address>>
979    where
980        T: IntoParallelIterator<Item = &'a Self> + IntoIterator<Item = &'a Self>,
981    {
982        if num_txes < *PARALLEL_SENDER_RECOVERY_THRESHOLD {
983            txes.into_iter().map(|tx| tx.recover_signer_unchecked()).collect()
984        } else {
985            txes.into_par_iter().map(|tx| tx.recover_signer_unchecked()).collect()
986        }
987    }
988
989    /// Returns the [`RecoveredTx`] transaction with the given sender.
990    #[inline]
991    pub const fn with_signer(self, signer: Address) -> RecoveredTx {
992        RecoveredTx::from_signed_transaction(self, signer)
993    }
994
995    /// Consumes the type, recover signer and return [`RecoveredTx`]
996    ///
997    /// Returns `None` if the transaction's signature is invalid, see also [`Self::recover_signer`].
998    pub fn into_ecrecovered(self) -> Option<RecoveredTx> {
999        let signer = self.recover_signer()?;
1000        Some(RecoveredTx { signed_transaction: self, signer })
1001    }
1002
1003    /// Consumes the type, recover signer and return [`RecoveredTx`] _without
1004    /// ensuring that the signature has a low `s` value_ (EIP-2).
1005    ///
1006    /// Returns `None` if the transaction's signature is invalid, see also
1007    /// [`Self::recover_signer_unchecked`].
1008    pub fn into_ecrecovered_unchecked(self) -> Option<RecoveredTx> {
1009        let signer = self.recover_signer_unchecked()?;
1010        Some(RecoveredTx { signed_transaction: self, signer })
1011    }
1012
1013    /// Tries to recover signer and return [`RecoveredTx`]. _without ensuring that
1014    /// the signature has a low `s` value_ (EIP-2).
1015    ///
1016    /// Returns `Err(Self)` if the transaction's signature is invalid, see also
1017    /// [`Self::recover_signer_unchecked`].
1018    pub fn try_into_ecrecovered_unchecked(self) -> Result<RecoveredTx, Self> {
1019        match self.recover_signer_unchecked() {
1020            None => Err(self),
1021            Some(signer) => Ok(RecoveredTx { signed_transaction: self, signer }),
1022        }
1023    }
1024
1025    /// Calculate transaction hash, eip2728 transaction does not contain rlp header and start with
1026    /// tx type.
1027    pub fn recalculate_hash(&self) -> B256 {
1028        match &self.transaction {
1029            Transaction::Legacy(tx) => {
1030                *SignableTransaction::<Signature>::into_signed(tx.clone(), self.signature).hash()
1031            }
1032            Transaction::Eip2930(tx) => {
1033                *SignableTransaction::<Signature>::into_signed(tx.clone(), self.signature).hash()
1034            }
1035            Transaction::Eip1559(tx) => {
1036                *SignableTransaction::<Signature>::into_signed(tx.clone(), self.signature).hash()
1037            }
1038            Transaction::Eip4844(tx) => {
1039                *SignableTransaction::<Signature>::into_signed(tx.clone(), self.signature).hash()
1040            }
1041            Transaction::Eip7702(tx) => {
1042                *SignableTransaction::<Signature>::into_signed(tx.clone(), self.signature).hash()
1043            }
1044            Transaction::Seismic(tx) => {
1045                *SignableTransaction::<Signature>::into_signed(tx.clone(), self.signature).hash()
1046            }
1047            #[cfg(feature = "optimism")]
1048            Transaction::Deposit(tx) => {
1049                *SignableTransaction::<Signature>::into_signed(tx.clone(), self.signature).hash()
1050            }
1051        }
1052    }
1053
1054    /// Splits the transaction into parts.
1055    pub fn into_parts(self) -> (Transaction, Signature, B256) {
1056        let hash = self.hash();
1057        (self.transaction, self.signature, hash)
1058    }
1059
1060    /// Decodes legacy transaction from the data buffer into a tuple.
1061    ///
1062    /// This expects `rlp(legacy_tx)`
1063    ///
1064    /// Refer to the docs for [`Self::decode_rlp_legacy_transaction`] for details on the exact
1065    /// format expected.
1066    pub fn decode_rlp_legacy_transaction_tuple(
1067        data: &mut &[u8],
1068    ) -> alloy_rlp::Result<(TxLegacy, TxHash, Signature)> {
1069        // keep this around, so we can use it to calculate the hash
1070        let original_encoding = *data;
1071
1072        let header = Header::decode(data)?;
1073        let remaining_len = data.len();
1074
1075        let transaction_payload_len = header.payload_length;
1076
1077        if transaction_payload_len > remaining_len {
1078            return Err(RlpError::InputTooShort)
1079        }
1080
1081        let mut transaction = TxLegacy {
1082            nonce: Decodable::decode(data)?,
1083            gas_price: Decodable::decode(data)?,
1084            gas_limit: Decodable::decode(data)?,
1085            to: Decodable::decode(data)?,
1086            value: Decodable::decode(data)?,
1087            input: Decodable::decode(data)?,
1088            chain_id: None,
1089        };
1090        let (signature, extracted_id) = decode_with_eip155_chain_id(data)?;
1091        transaction.chain_id = extracted_id;
1092
1093        // check the new length, compared to the original length and the header length
1094        let decoded = remaining_len - data.len();
1095        if decoded != transaction_payload_len {
1096            return Err(RlpError::UnexpectedLength)
1097        }
1098
1099        let tx_length = header.payload_length + header.length();
1100        let hash = keccak256(&original_encoding[..tx_length]);
1101        Ok((transaction, hash, signature))
1102    }
1103
1104    /// Decodes legacy transaction from the data buffer.
1105    ///
1106    /// This should be used _only_ be used in general transaction decoding methods, which have
1107    /// already ensured that the input is a legacy transaction with the following format:
1108    /// `rlp(legacy_tx)`
1109    ///
1110    /// Legacy transactions are encoded as lists, so the input should start with a RLP list header.
1111    ///
1112    /// This expects `rlp(legacy_tx)`
1113    // TODO: make buf advancement semantics consistent with `decode_enveloped_typed_transaction`,
1114    // so decoding methods do not need to manually advance the buffer
1115    pub fn decode_rlp_legacy_transaction(data: &mut &[u8]) -> alloy_rlp::Result<Self> {
1116        let (transaction, hash, signature) = Self::decode_rlp_legacy_transaction_tuple(data)?;
1117        let signed =
1118            Self { transaction: Transaction::Legacy(transaction), hash: hash.into(), signature };
1119        Ok(signed)
1120    }
1121
1122    /// Shields the input of the transaction.
1123    pub fn shield_input(mut self) -> Self {
1124        self.transaction.set_input(Bytes::new());
1125        self
1126    }
1127}
1128
1129impl SignedTransaction for TransactionSigned {
1130    fn tx_hash(&self) -> &TxHash {
1131        self.hash.get_or_init(|| self.recalculate_hash())
1132    }
1133
1134    fn signature(&self) -> &Signature {
1135        &self.signature
1136    }
1137
1138    fn recover_signer(&self) -> Option<Address> {
1139        // Optimism's Deposit transaction does not have a signature. Directly return the
1140        // `from` address.
1141        #[cfg(feature = "optimism")]
1142        if let Transaction::Deposit(TxDeposit { from, .. }) = self.transaction {
1143            return Some(from)
1144        }
1145        let signature_hash = self.signature_hash();
1146        recover_signer(&self.signature, signature_hash)
1147    }
1148
1149    fn recover_signer_unchecked_with_buf(&self, buf: &mut Vec<u8>) -> Option<Address> {
1150        // Optimism's Deposit transaction does not have a signature. Directly return the
1151        // `from` address.
1152        #[cfg(feature = "optimism")]
1153        if let Transaction::Deposit(TxDeposit { from, .. }) = self.transaction {
1154            return Some(from)
1155        }
1156        self.encode_for_signing(buf);
1157        let signature_hash = keccak256(buf);
1158        recover_signer_unchecked(&self.signature, signature_hash)
1159    }
1160}
1161
1162impl reth_primitives_traits::FillTxEnv for TransactionSigned {
1163    fn fill_tx_env(&self, tx_env: &mut TxEnv, sender: Address) {
1164        tx_env.caller = sender;
1165        match self.as_ref() {
1166            Transaction::Legacy(tx) => {
1167                tx_env.gas_limit = tx.gas_limit;
1168                tx_env.gas_price = U256::from(tx.gas_price);
1169                tx_env.gas_priority_fee = None;
1170                tx_env.transact_to = tx.to;
1171                tx_env.value = tx.value;
1172                tx_env.data = tx.input.clone();
1173                tx_env.chain_id = tx.chain_id;
1174                tx_env.nonce = Some(tx.nonce);
1175                tx_env.access_list.clear();
1176                tx_env.blob_hashes.clear();
1177                tx_env.max_fee_per_blob_gas.take();
1178                tx_env.authorization_list = None;
1179            }
1180            Transaction::Eip2930(tx) => {
1181                tx_env.gas_limit = tx.gas_limit;
1182                tx_env.gas_price = U256::from(tx.gas_price);
1183                tx_env.gas_priority_fee = None;
1184                tx_env.transact_to = tx.to;
1185                tx_env.value = tx.value;
1186                tx_env.data = tx.input.clone();
1187                tx_env.chain_id = Some(tx.chain_id);
1188                tx_env.nonce = Some(tx.nonce);
1189                tx_env.access_list.clone_from(&tx.access_list.0);
1190                tx_env.blob_hashes.clear();
1191                tx_env.max_fee_per_blob_gas.take();
1192                tx_env.authorization_list = None;
1193            }
1194            Transaction::Eip1559(tx) => {
1195                tx_env.gas_limit = tx.gas_limit;
1196                tx_env.gas_price = U256::from(tx.max_fee_per_gas);
1197                tx_env.gas_priority_fee = Some(U256::from(tx.max_priority_fee_per_gas));
1198                tx_env.transact_to = tx.to;
1199                tx_env.value = tx.value;
1200                tx_env.data = tx.input.clone();
1201                tx_env.chain_id = Some(tx.chain_id);
1202                tx_env.nonce = Some(tx.nonce);
1203                tx_env.access_list.clone_from(&tx.access_list.0);
1204                tx_env.blob_hashes.clear();
1205                tx_env.max_fee_per_blob_gas.take();
1206                tx_env.authorization_list = None;
1207            }
1208            Transaction::Eip4844(tx) => {
1209                tx_env.gas_limit = tx.gas_limit;
1210                tx_env.gas_price = U256::from(tx.max_fee_per_gas);
1211                tx_env.gas_priority_fee = Some(U256::from(tx.max_priority_fee_per_gas));
1212                tx_env.transact_to = TxKind::Call(tx.to);
1213                tx_env.value = tx.value;
1214                tx_env.data = tx.input.clone();
1215                tx_env.chain_id = Some(tx.chain_id);
1216                tx_env.nonce = Some(tx.nonce);
1217                tx_env.access_list.clone_from(&tx.access_list.0);
1218                tx_env.blob_hashes.clone_from(&tx.blob_versioned_hashes);
1219                tx_env.max_fee_per_blob_gas = Some(U256::from(tx.max_fee_per_blob_gas));
1220                tx_env.authorization_list = None;
1221            }
1222            Transaction::Eip7702(tx) => {
1223                tx_env.gas_limit = tx.gas_limit;
1224                tx_env.gas_price = U256::from(tx.max_fee_per_gas);
1225                tx_env.gas_priority_fee = Some(U256::from(tx.max_priority_fee_per_gas));
1226                tx_env.transact_to = tx.to.into();
1227                tx_env.value = tx.value;
1228                tx_env.data = tx.input.clone();
1229                tx_env.chain_id = Some(tx.chain_id);
1230                tx_env.nonce = Some(tx.nonce);
1231                tx_env.access_list.clone_from(&tx.access_list.0);
1232                tx_env.blob_hashes.clear();
1233                tx_env.max_fee_per_blob_gas.take();
1234                tx_env.authorization_list =
1235                    Some(AuthorizationList::Signed(tx.authorization_list.clone()));
1236            }
1237            Transaction::Seismic(_tx) => {
1238                // implementation is in EthEvmConfig to avoid changing FillTxEnv trait
1239                error!(target: "reth::fill_tx_env", "Seismic transaction not filled");
1240                return
1241            }
1242            #[cfg(feature = "optimism")]
1243            Transaction::Deposit(_) => {}
1244        }
1245    }
1246}
1247
1248impl InMemorySize for TransactionSigned {
1249    /// Calculate a heuristic for the in-memory size of the [`TransactionSigned`].
1250    #[inline]
1251    fn size(&self) -> usize {
1252        self.hash().size() + self.transaction.size() + self.signature().size()
1253    }
1254}
1255
1256impl alloy_consensus::transaction::ShieldableTransaction for TransactionSigned {
1257    fn shield_input(&mut self) {
1258        match &mut self.transaction {
1259            Transaction::Seismic(tx) => {
1260                tx.shield_input();
1261            }
1262            _ => {}
1263        }
1264    }
1265}
1266
1267impl alloy_consensus::Transaction for TransactionSigned {
1268    fn chain_id(&self) -> Option<ChainId> {
1269        self.deref().chain_id()
1270    }
1271
1272    fn nonce(&self) -> u64 {
1273        self.deref().nonce()
1274    }
1275
1276    fn gas_limit(&self) -> u64 {
1277        self.deref().gas_limit()
1278    }
1279
1280    fn gas_price(&self) -> Option<u128> {
1281        self.deref().gas_price()
1282    }
1283
1284    fn max_fee_per_gas(&self) -> u128 {
1285        self.deref().max_fee_per_gas()
1286    }
1287
1288    fn max_priority_fee_per_gas(&self) -> Option<u128> {
1289        self.deref().max_priority_fee_per_gas()
1290    }
1291
1292    fn max_fee_per_blob_gas(&self) -> Option<u128> {
1293        self.deref().max_fee_per_blob_gas()
1294    }
1295
1296    fn priority_fee_or_price(&self) -> u128 {
1297        self.deref().priority_fee_or_price()
1298    }
1299
1300    fn effective_gas_price(&self, base_fee: Option<u64>) -> u128 {
1301        self.deref().effective_gas_price(base_fee)
1302    }
1303
1304    fn is_dynamic_fee(&self) -> bool {
1305        self.deref().is_dynamic_fee()
1306    }
1307
1308    fn kind(&self) -> TxKind {
1309        self.deref().kind()
1310    }
1311
1312    fn is_create(&self) -> bool {
1313        self.deref().is_create()
1314    }
1315
1316    fn value(&self) -> U256 {
1317        self.deref().value()
1318    }
1319
1320    fn input(&self) -> &Bytes {
1321        self.deref().input()
1322    }
1323
1324    fn access_list(&self) -> Option<&AccessList> {
1325        self.deref().access_list()
1326    }
1327
1328    fn blob_versioned_hashes(&self) -> Option<&[B256]> {
1329        alloy_consensus::Transaction::blob_versioned_hashes(self.deref())
1330    }
1331
1332    fn authorization_list(&self) -> Option<&[SignedAuthorization]> {
1333        self.deref().authorization_list()
1334    }
1335
1336    fn seismic_elements(&self) -> Option<&TxSeismicElements> {
1337        match &self.transaction {
1338            Transaction::Seismic(tx) => tx.seismic_elements(),
1339            _ => None,
1340        }
1341    }
1342}
1343
1344impl From<RecoveredTx> for TransactionSigned {
1345    fn from(recovered: RecoveredTx) -> Self {
1346        recovered.signed_transaction
1347    }
1348}
1349
1350impl Encodable for TransactionSigned {
1351    /// This encodes the transaction _with_ the signature, and an rlp header.
1352    ///
1353    /// For legacy transactions, it encodes the transaction data:
1354    /// `rlp(tx-data)`
1355    ///
1356    /// For EIP-2718 typed transactions, it encodes the transaction type followed by the rlp of the
1357    /// transaction:
1358    /// `rlp(tx-type || rlp(tx-data))`
1359    fn encode(&self, out: &mut dyn bytes::BufMut) {
1360        self.network_encode(out);
1361    }
1362
1363    fn length(&self) -> usize {
1364        let mut payload_length = self.encode_2718_len();
1365        if !Encodable2718::is_legacy(self) {
1366            payload_length += Header { list: false, payload_length }.length();
1367        }
1368
1369        payload_length
1370    }
1371}
1372
1373impl Decodable for TransactionSigned {
1374    /// This `Decodable` implementation only supports decoding rlp encoded transactions as it's used
1375    /// by p2p.
1376    ///
1377    /// The p2p encoding format always includes an RLP header, although the type RLP header depends
1378    /// on whether or not the transaction is a legacy transaction.
1379    ///
1380    /// If the transaction is a legacy transaction, it is just encoded as a RLP list:
1381    /// `rlp(tx-data)`.
1382    ///
1383    /// If the transaction is a typed transaction, it is encoded as a RLP string:
1384    /// `rlp(tx-type || rlp(tx-data))`
1385    ///
1386    /// This can be used for decoding all signed transactions in p2p `BlockBodies` responses.
1387    ///
1388    /// This cannot be used for decoding EIP-4844 transactions in p2p `PooledTransactions`, since
1389    /// the EIP-4844 variant of [`TransactionSigned`] does not include the blob sidecar.
1390    ///
1391    /// For a method suitable for decoding pooled transactions, see [`PooledTransactionsElement`].
1392    ///
1393    /// CAUTION: Due to a quirk in [`Header::decode`], this method will succeed even if a typed
1394    /// transaction is encoded in this format, and does not start with a RLP header:
1395    /// `tx-type || rlp(tx-data)`.
1396    ///
1397    /// This is because [`Header::decode`] does not advance the buffer, and returns a length-1
1398    /// string header if the first byte is less than `0xf7`.
1399    fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
1400        Self::network_decode(buf).map_err(Into::into)
1401    }
1402}
1403
1404impl Encodable2718 for TransactionSigned {
1405    fn type_flag(&self) -> Option<u8> {
1406        match self.transaction.tx_type() {
1407            TxType::Legacy => None,
1408            tx_type => Some(tx_type as u8),
1409        }
1410    }
1411
1412    fn encode_2718_len(&self) -> usize {
1413        match &self.transaction {
1414            Transaction::Legacy(legacy_tx) => legacy_tx.eip2718_encoded_length(&self.signature),
1415            Transaction::Eip2930(access_list_tx) => {
1416                access_list_tx.eip2718_encoded_length(&self.signature)
1417            }
1418            Transaction::Eip1559(dynamic_fee_tx) => {
1419                dynamic_fee_tx.eip2718_encoded_length(&self.signature)
1420            }
1421            Transaction::Eip4844(blob_tx) => blob_tx.eip2718_encoded_length(&self.signature),
1422            Transaction::Eip7702(set_code_tx) => {
1423                set_code_tx.eip2718_encoded_length(&self.signature)
1424            }
1425            Transaction::Seismic(seismic_tx) => seismic_tx.eip2718_encoded_length(&self.signature),
1426            #[cfg(feature = "optimism")]
1427            Transaction::Deposit(deposit_tx) => deposit_tx.eip2718_encoded_length(),
1428        }
1429    }
1430
1431    fn encode_2718(&self, out: &mut dyn alloy_rlp::BufMut) {
1432        self.transaction.eip2718_encode(&self.signature, out)
1433    }
1434
1435    fn trie_hash(&self) -> B256 {
1436        self.hash()
1437    }
1438}
1439
1440impl Decodable2718 for TransactionSigned {
1441    fn typed_decode(ty: u8, buf: &mut &[u8]) -> Eip2718Result<Self> {
1442        match ty.try_into().map_err(|_| Eip2718Error::UnexpectedType(ty))? {
1443            TxType::Legacy => Err(Eip2718Error::UnexpectedType(0)),
1444            TxType::Eip2930 => {
1445                let (tx, signature, hash) = TxEip2930::rlp_decode_signed(buf)?.into_parts();
1446                Ok(Self { transaction: Transaction::Eip2930(tx), signature, hash: hash.into() })
1447            }
1448            TxType::Eip1559 => {
1449                let (tx, signature, hash) = TxEip1559::rlp_decode_signed(buf)?.into_parts();
1450                Ok(Self { transaction: Transaction::Eip1559(tx), signature, hash: hash.into() })
1451            }
1452            TxType::Eip7702 => {
1453                let (tx, signature, hash) = TxEip7702::rlp_decode_signed(buf)?.into_parts();
1454                Ok(Self { transaction: Transaction::Eip7702(tx), signature, hash: hash.into() })
1455            }
1456            TxType::Eip4844 => {
1457                let (tx, signature, hash) = TxEip4844::rlp_decode_signed(buf)?.into_parts();
1458                Ok(Self { transaction: Transaction::Eip4844(tx), signature, hash: hash.into() })
1459            }
1460            TxType::Seismic => {
1461                let (tx, signature, hash) = TxSeismic::rlp_decode_signed(buf)?.into_parts();
1462                Ok(Self { transaction: Transaction::Seismic(tx), signature, hash: hash.into() })
1463            }
1464            #[cfg(feature = "optimism")]
1465            TxType::Deposit => Ok(Self::new_unhashed(
1466                Transaction::Deposit(TxDeposit::rlp_decode(buf)?),
1467                TxDeposit::signature(),
1468            )),
1469        }
1470    }
1471
1472    fn fallback_decode(buf: &mut &[u8]) -> Eip2718Result<Self> {
1473        Ok(Self::decode_rlp_legacy_transaction(buf)?)
1474    }
1475}
1476
1477impl Decodable712 for TransactionSigned {
1478    fn decode_712(typed_data: &TypedDataRequest) -> Eip712Result<Self> {
1479        let (tx, signature, hash) = TxSeismic::eip712_decode(&typed_data.data)?
1480            .into_signed(typed_data.signature)
1481            .into_parts();
1482        Ok(Self { transaction: Transaction::Seismic(tx), signature, hash: hash.into() })
1483    }
1484}
1485
1486#[cfg(any(test, feature = "reth-codec"))]
1487impl reth_codecs::Compact for TransactionSigned {
1488    fn to_compact<B>(&self, buf: &mut B) -> usize
1489    where
1490        B: bytes::BufMut + AsMut<[u8]>,
1491    {
1492        let start = buf.as_mut().len();
1493
1494        // Placeholder for bitflags.
1495        // The first byte uses 4 bits as flags: IsCompressed[1bit], TxType[2bits], Signature[1bit]
1496        buf.put_u8(0);
1497
1498        let sig_bit = self.signature.to_compact(buf) as u8;
1499        let zstd_bit = self.transaction.input().len() >= 32;
1500
1501        let tx_bits = if zstd_bit {
1502            let mut tmp = Vec::with_capacity(256);
1503            if cfg!(feature = "std") {
1504                reth_zstd_compressors::TRANSACTION_COMPRESSOR.with(|compressor| {
1505                    let mut compressor = compressor.borrow_mut();
1506                    let tx_bits = self.transaction.to_compact(&mut tmp);
1507                    buf.put_slice(&compressor.compress(&tmp).expect("Failed to compress"));
1508                    tx_bits as u8
1509                })
1510            } else {
1511                let mut compressor = reth_zstd_compressors::create_tx_compressor();
1512                let tx_bits = self.transaction.to_compact(&mut tmp);
1513                buf.put_slice(&compressor.compress(&tmp).expect("Failed to compress"));
1514                tx_bits as u8
1515            }
1516        } else {
1517            self.transaction.to_compact(buf) as u8
1518        };
1519
1520        // Replace bitflags with the actual values
1521        buf.as_mut()[start] = sig_bit | (tx_bits << 1) | ((zstd_bit as u8) << 3);
1522
1523        buf.as_mut().len() - start
1524    }
1525
1526    fn from_compact(mut buf: &[u8], _len: usize) -> (Self, &[u8]) {
1527        use bytes::Buf;
1528
1529        // The first byte uses 4 bits as flags: IsCompressed[1], TxType[2], Signature[1]
1530        let bitflags = buf.get_u8() as usize;
1531
1532        let sig_bit = bitflags & 1;
1533        let (signature, buf) = Signature::from_compact(buf, sig_bit);
1534
1535        let zstd_bit = bitflags >> 3;
1536        let (transaction, buf) = if zstd_bit != 0 {
1537            if cfg!(feature = "std") {
1538                reth_zstd_compressors::TRANSACTION_DECOMPRESSOR.with(|decompressor| {
1539                    let mut decompressor = decompressor.borrow_mut();
1540
1541                    // TODO: enforce that zstd is only present at a "top" level type
1542
1543                    let transaction_type = (bitflags & 0b110) >> 1;
1544                    let (transaction, _) =
1545                        Transaction::from_compact(decompressor.decompress(buf), transaction_type);
1546
1547                    (transaction, buf)
1548                })
1549            } else {
1550                let mut decompressor = reth_zstd_compressors::create_tx_decompressor();
1551                let transaction_type = (bitflags & 0b110) >> 1;
1552                let (transaction, _) =
1553                    Transaction::from_compact(decompressor.decompress(buf), transaction_type);
1554
1555                (transaction, buf)
1556            }
1557        } else {
1558            let transaction_type = bitflags >> 1;
1559            Transaction::from_compact(buf, transaction_type)
1560        };
1561
1562        (Self { signature, transaction, hash: Default::default() }, buf)
1563    }
1564}
1565
1566macro_rules! impl_from_signed {
1567    ($($tx:ident),*) => {
1568        $(
1569            impl From<Signed<$tx>> for TransactionSigned {
1570                fn from(value: Signed<$tx>) -> Self {
1571                    let(tx,sig,hash) = value.into_parts();
1572                    Self::new(tx.into(), sig, hash)
1573                }
1574            }
1575        )*
1576    };
1577}
1578
1579impl_from_signed!(
1580    TxLegacy,
1581    TxEip2930,
1582    TxEip1559,
1583    TxEip7702,
1584    TxEip4844,
1585    TypedTransaction,
1586    TxSeismic
1587);
1588
1589impl From<Signed<Transaction>> for TransactionSigned {
1590    fn from(value: Signed<Transaction>) -> Self {
1591        let (tx, sig, hash) = value.into_parts();
1592        Self::new(tx, sig, hash)
1593    }
1594}
1595
1596impl From<TransactionSigned> for Signed<Transaction> {
1597    fn from(value: TransactionSigned) -> Self {
1598        let (tx, sig, hash) = value.into_parts();
1599        Self::new_unchecked(tx, sig, hash)
1600    }
1601}
1602
1603#[cfg(any(test, feature = "arbitrary"))]
1604impl<'a> arbitrary::Arbitrary<'a> for TransactionSigned {
1605    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
1606        #[allow(unused_mut)]
1607        let mut transaction = Transaction::arbitrary(u)?;
1608
1609        let secp = secp256k1::Secp256k1::new();
1610        let key_pair = secp256k1::Keypair::new(&secp, &mut rand::thread_rng());
1611        let signature = crate::sign_message(
1612            B256::from_slice(&key_pair.secret_bytes()[..]),
1613            transaction.signature_hash(),
1614        )
1615        .unwrap();
1616
1617        #[cfg(feature = "optimism")]
1618        // Both `Some(0)` and `None` values are encoded as empty string byte. This introduces
1619        // ambiguity in roundtrip tests. Patch the mint value of deposit transaction here, so that
1620        // it's `None` if zero.
1621        if let Transaction::Deposit(ref mut tx_deposit) = transaction {
1622            if tx_deposit.mint == Some(0) {
1623                tx_deposit.mint = None;
1624            }
1625        }
1626
1627        #[cfg(feature = "optimism")]
1628        let signature = if transaction.is_deposit() { TxDeposit::signature() } else { signature };
1629        let tx_signed = Self::new_unhashed(transaction, signature);
1630        let hash = tx_signed.hash();
1631        Ok(Self::new(tx_signed.transaction, tx_signed.signature, hash))
1632    }
1633}
1634
1635impl From<TransactionSigned> for TransactionRequest {
1636    fn from(tx: TransactionSigned) -> Self {
1637        match tx.transaction {
1638            Transaction::Legacy(tx) => tx.into(),
1639            Transaction::Eip2930(tx) => tx.into(),
1640            Transaction::Eip1559(tx) => tx.into(),
1641            Transaction::Eip4844(tx) => tx.into(),
1642            Transaction::Eip7702(tx) => tx.into(),
1643            Transaction::Seismic(tx) => tx.into(),
1644            #[cfg(feature = "optimism")]
1645            Transaction::Deposit(tx) => tx.into(),
1646        }
1647    }
1648}
1649
1650/// Type alias kept for backward compatibility.
1651pub type TransactionSignedEcRecovered<T = TransactionSigned> = RecoveredTx<T>;
1652
1653/// Signed transaction with recovered signer.
1654#[derive(Debug, Clone, PartialEq, Hash, Eq, AsRef, Deref)]
1655pub struct RecoveredTx<T = TransactionSigned> {
1656    /// Signer of the transaction
1657    signer: Address,
1658    /// Signed transaction
1659    #[deref]
1660    #[as_ref]
1661    signed_transaction: T,
1662}
1663
1664// === impl RecoveredTx ===
1665
1666impl<T> RecoveredTx<T> {
1667    /// Signer of transaction recovered from signature
1668    pub const fn signer(&self) -> Address {
1669        self.signer
1670    }
1671
1672    /// Reference to the signer of transaction recovered from signature
1673    pub const fn signer_ref(&self) -> &Address {
1674        &self.signer
1675    }
1676
1677    /// Returns a reference to [`TransactionSigned`]
1678    pub const fn as_signed(&self) -> &T {
1679        &self.signed_transaction
1680    }
1681
1682    /// Transform back to [`TransactionSigned`]
1683    pub fn into_signed(self) -> T {
1684        self.signed_transaction
1685    }
1686
1687    /// Dissolve Self to its component
1688    pub fn to_components(self) -> (T, Address) {
1689        (self.signed_transaction, self.signer)
1690    }
1691
1692    /// Create [`RecoveredTx`] from [`TransactionSigned`] and [`Address`] of the
1693    /// signer.
1694    #[inline]
1695    pub const fn from_signed_transaction(signed_transaction: T, signer: Address) -> Self {
1696        Self { signed_transaction, signer }
1697    }
1698
1699    /// Applies the given closure to the inner transactions.
1700    pub fn map_transaction<Tx>(self, f: impl FnOnce(T) -> Tx) -> RecoveredTx<Tx> {
1701        RecoveredTx::from_signed_transaction(f(self.signed_transaction), self.signer)
1702    }
1703}
1704
1705impl RecoveredTx<TransactionSigned> {
1706    /// Shields the input of the transaction.
1707    pub fn shield_input(mut self) -> Self {
1708        self.signed_transaction = self.signed_transaction.shield_input();
1709        self
1710    }
1711}
1712
1713impl<T: Encodable> Encodable for RecoveredTx<T> {
1714    /// This encodes the transaction _with_ the signature, and an rlp header.
1715    ///
1716    /// Refer to docs for [`TransactionSigned::encode`] for details on the exact format.
1717    fn encode(&self, out: &mut dyn bytes::BufMut) {
1718        self.signed_transaction.encode(out)
1719    }
1720
1721    fn length(&self) -> usize {
1722        self.signed_transaction.length()
1723    }
1724}
1725
1726impl<T: SignedTransaction> Decodable for RecoveredTx<T> {
1727    fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
1728        let signed_transaction = T::decode(buf)?;
1729        let signer = signed_transaction
1730            .recover_signer()
1731            .ok_or(RlpError::Custom("Unable to recover decoded transaction signer."))?;
1732        Ok(Self { signer, signed_transaction })
1733    }
1734}
1735
1736impl<T: Encodable2718> Encodable2718 for RecoveredTx<T> {
1737    fn type_flag(&self) -> Option<u8> {
1738        self.signed_transaction.type_flag()
1739    }
1740
1741    fn encode_2718_len(&self) -> usize {
1742        self.signed_transaction.encode_2718_len()
1743    }
1744
1745    fn encode_2718(&self, out: &mut dyn alloy_rlp::BufMut) {
1746        self.signed_transaction.encode_2718(out)
1747    }
1748
1749    fn trie_hash(&self) -> B256 {
1750        self.signed_transaction.trie_hash()
1751    }
1752}
1753
1754/// Extension trait for [`SignedTransaction`] to convert it into [`RecoveredTx`].
1755pub trait SignedTransactionIntoRecoveredExt: SignedTransaction {
1756    /// Tries to recover signer and return [`RecoveredTx`] by cloning the type.
1757    fn try_ecrecovered(&self) -> Option<RecoveredTx<Self>> {
1758        let signer = self.recover_signer()?;
1759        Some(RecoveredTx { signed_transaction: self.clone(), signer })
1760    }
1761
1762    /// Tries to recover signer and return [`RecoveredTx`].
1763    ///
1764    /// Returns `Err(Self)` if the transaction's signature is invalid, see also
1765    /// [`SignedTransaction::recover_signer`].
1766    fn try_into_ecrecovered(self) -> Result<RecoveredTx<Self>, Self> {
1767        match self.recover_signer() {
1768            None => Err(self),
1769            Some(signer) => Ok(RecoveredTx { signed_transaction: self, signer }),
1770        }
1771    }
1772
1773    /// Consumes the type, recover signer and return [`RecoveredTx`] _without
1774    /// ensuring that the signature has a low `s` value_ (EIP-2).
1775    ///
1776    /// Returns `None` if the transaction's signature is invalid.
1777    fn into_ecrecovered_unchecked(self) -> Option<RecoveredTx<Self>> {
1778        let signer = self.recover_signer_unchecked()?;
1779        Some(RecoveredTx::from_signed_transaction(self, signer))
1780    }
1781
1782    /// Returns the [`RecoveredTx`] transaction with the given sender.
1783    fn with_signer(self, signer: Address) -> RecoveredTx<Self> {
1784        RecoveredTx::from_signed_transaction(self, signer)
1785    }
1786}
1787
1788impl<T> SignedTransactionIntoRecoveredExt for T where T: SignedTransaction {}
1789
1790/// Bincode-compatible transaction type serde implementations.
1791#[cfg(feature = "serde-bincode-compat")]
1792pub mod serde_bincode_compat {
1793    use alloc::borrow::Cow;
1794    use alloy_consensus::{
1795        transaction::serde_bincode_compat::{TxEip1559, TxEip2930, TxEip7702, TxLegacy, TxSeismic},
1796        TxEip4844,
1797    };
1798    use alloy_primitives::{PrimitiveSignature as Signature, TxHash};
1799    use serde::{Deserialize, Deserializer, Serialize, Serializer};
1800    use serde_with::{DeserializeAs, SerializeAs};
1801
1802    /// Bincode-compatible [`super::Transaction`] serde implementation.
1803    ///
1804    /// Intended to use with the [`serde_with::serde_as`] macro in the following way:
1805    /// ```rust
1806    /// use reth_primitives::{serde_bincode_compat, Transaction};
1807    /// use serde::{Deserialize, Serialize};
1808    /// use serde_with::serde_as;
1809    ///
1810    /// #[serde_as]
1811    /// #[derive(Serialize, Deserialize)]
1812    /// struct Data {
1813    ///     #[serde_as(as = "serde_bincode_compat::transaction::Transaction")]
1814    ///     transaction: Transaction,
1815    /// }
1816    /// ```
1817    #[derive(Debug, Serialize, Deserialize)]
1818    #[allow(missing_docs)]
1819    pub enum Transaction<'a> {
1820        Legacy(TxLegacy<'a>),
1821        Seismic(TxSeismic<'a>),
1822        Eip2930(TxEip2930<'a>),
1823        Eip1559(TxEip1559<'a>),
1824        Eip4844(Cow<'a, TxEip4844>),
1825        Eip7702(TxEip7702<'a>),
1826        #[cfg(feature = "optimism")]
1827        Deposit(op_alloy_consensus::serde_bincode_compat::TxDeposit<'a>),
1828    }
1829
1830    impl<'a> From<&'a super::Transaction> for Transaction<'a> {
1831        fn from(value: &'a super::Transaction) -> Self {
1832            match value {
1833                super::Transaction::Legacy(tx) => Self::Legacy(TxLegacy::from(tx)),
1834                super::Transaction::Seismic(tx) => Self::Seismic(TxSeismic::from(tx)),
1835                super::Transaction::Eip2930(tx) => Self::Eip2930(TxEip2930::from(tx)),
1836                super::Transaction::Eip1559(tx) => Self::Eip1559(TxEip1559::from(tx)),
1837                super::Transaction::Eip4844(tx) => Self::Eip4844(Cow::Borrowed(tx)),
1838                super::Transaction::Eip7702(tx) => Self::Eip7702(TxEip7702::from(tx)),
1839                #[cfg(feature = "optimism")]
1840                super::Transaction::Deposit(tx) => {
1841                    Self::Deposit(op_alloy_consensus::serde_bincode_compat::TxDeposit::from(tx))
1842                }
1843            }
1844        }
1845    }
1846
1847    impl<'a> From<Transaction<'a>> for super::Transaction {
1848        fn from(value: Transaction<'a>) -> Self {
1849            match value {
1850                Transaction::Legacy(tx) => Self::Legacy(tx.into()),
1851                Transaction::Eip2930(tx) => Self::Eip2930(tx.into()),
1852                Transaction::Eip1559(tx) => Self::Eip1559(tx.into()),
1853                Transaction::Eip4844(tx) => Self::Eip4844(tx.into_owned()),
1854                Transaction::Eip7702(tx) => Self::Eip7702(tx.into()),
1855                Transaction::Seismic(tx) => Self::Seismic(tx.into()),
1856                #[cfg(feature = "optimism")]
1857                Transaction::Deposit(tx) => Self::Deposit(tx.into()),
1858            }
1859        }
1860    }
1861
1862    impl SerializeAs<super::Transaction> for Transaction<'_> {
1863        fn serialize_as<S>(source: &super::Transaction, serializer: S) -> Result<S::Ok, S::Error>
1864        where
1865            S: Serializer,
1866        {
1867            Transaction::from(source).serialize(serializer)
1868        }
1869    }
1870
1871    impl<'de> DeserializeAs<'de, super::Transaction> for Transaction<'de> {
1872        fn deserialize_as<D>(deserializer: D) -> Result<super::Transaction, D::Error>
1873        where
1874            D: Deserializer<'de>,
1875        {
1876            Transaction::deserialize(deserializer).map(Into::into)
1877        }
1878    }
1879
1880    /// Bincode-compatible [`super::TransactionSigned`] serde implementation.
1881    ///
1882    /// Intended to use with the [`serde_with::serde_as`] macro in the following way:
1883    /// ```rust
1884    /// use reth_primitives::{serde_bincode_compat, TransactionSigned};
1885    /// use serde::{Deserialize, Serialize};
1886    /// use serde_with::serde_as;
1887    ///
1888    /// #[serde_as]
1889    /// #[derive(Serialize, Deserialize)]
1890    /// struct Data {
1891    ///     #[serde_as(as = "serde_bincode_compat::transaction::TransactionSigned")]
1892    ///     transaction: TransactionSigned,
1893    /// }
1894    /// ```
1895    #[derive(Debug, Serialize, Deserialize)]
1896    pub struct TransactionSigned<'a> {
1897        hash: TxHash,
1898        signature: Signature,
1899        transaction: Transaction<'a>,
1900    }
1901
1902    impl<'a> From<&'a super::TransactionSigned> for TransactionSigned<'a> {
1903        fn from(value: &'a super::TransactionSigned) -> Self {
1904            Self {
1905                hash: value.hash(),
1906                signature: value.signature,
1907                transaction: Transaction::from(&value.transaction),
1908            }
1909        }
1910    }
1911
1912    impl<'a> From<TransactionSigned<'a>> for super::TransactionSigned {
1913        fn from(value: TransactionSigned<'a>) -> Self {
1914            Self {
1915                hash: value.hash.into(),
1916                signature: value.signature,
1917                transaction: value.transaction.into(),
1918            }
1919        }
1920    }
1921
1922    impl SerializeAs<super::TransactionSigned> for TransactionSigned<'_> {
1923        fn serialize_as<S>(
1924            source: &super::TransactionSigned,
1925            serializer: S,
1926        ) -> Result<S::Ok, S::Error>
1927        where
1928            S: Serializer,
1929        {
1930            TransactionSigned::from(source).serialize(serializer)
1931        }
1932    }
1933
1934    impl<'de> DeserializeAs<'de, super::TransactionSigned> for TransactionSigned<'de> {
1935        fn deserialize_as<D>(deserializer: D) -> Result<super::TransactionSigned, D::Error>
1936        where
1937            D: Deserializer<'de>,
1938        {
1939            TransactionSigned::deserialize(deserializer).map(Into::into)
1940        }
1941    }
1942
1943    #[cfg(test)]
1944    mod tests {
1945        use super::super::{serde_bincode_compat, Transaction, TransactionSigned};
1946        use arbitrary::Arbitrary;
1947        use rand::Rng;
1948        use reth_testing_utils::generators;
1949        use serde::{Deserialize, Serialize};
1950        use serde_with::serde_as;
1951
1952        #[test]
1953        fn test_transaction_bincode_roundtrip() {
1954            #[serde_as]
1955            #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
1956            struct Data {
1957                #[serde_as(as = "serde_bincode_compat::Transaction")]
1958                transaction: Transaction,
1959            }
1960
1961            let mut bytes = [0u8; 1024];
1962            generators::rng().fill(bytes.as_mut_slice());
1963            let data = Data {
1964                transaction: Transaction::arbitrary(&mut arbitrary::Unstructured::new(&bytes))
1965                    .unwrap(),
1966            };
1967
1968            let encoded = bincode::serialize(&data).unwrap();
1969            let decoded: Data = bincode::deserialize(&encoded).unwrap();
1970            assert_eq!(decoded, data);
1971        }
1972
1973        #[test]
1974        fn test_transaction_signed_bincode_roundtrip() {
1975            #[serde_as]
1976            #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
1977            struct Data {
1978                #[serde_as(as = "serde_bincode_compat::TransactionSigned")]
1979                transaction: TransactionSigned,
1980            }
1981
1982            let mut bytes = [0u8; 1024];
1983            generators::rng().fill(bytes.as_mut_slice());
1984            let data = Data {
1985                transaction: TransactionSigned::arbitrary(&mut arbitrary::Unstructured::new(
1986                    &bytes,
1987                ))
1988                .unwrap(),
1989            };
1990
1991            let encoded = bincode::serialize(&data).unwrap();
1992            let decoded: Data = bincode::deserialize(&encoded).unwrap();
1993            assert_eq!(decoded, data);
1994        }
1995    }
1996}
1997
1998/// Recovers a list of signers from a transaction list iterator.
1999///
2000/// Returns `None`, if some transaction's signature is invalid
2001pub fn recover_signers<'a, I, T>(txes: I, num_txes: usize) -> Option<Vec<Address>>
2002where
2003    T: SignedTransaction,
2004    I: IntoParallelIterator<Item = &'a T> + IntoIterator<Item = &'a T> + Send,
2005{
2006    if num_txes < *PARALLEL_SENDER_RECOVERY_THRESHOLD {
2007        txes.into_iter().map(|tx| tx.recover_signer()).collect()
2008    } else {
2009        txes.into_par_iter().map(|tx| tx.recover_signer()).collect()
2010    }
2011}
2012
2013/// Recovers a list of signers from a transaction list iterator _without ensuring that the
2014/// signature has a low `s` value_.
2015///
2016/// Returns `None`, if some transaction's signature is invalid.
2017pub fn recover_signers_unchecked<'a, I, T>(txes: I, num_txes: usize) -> Option<Vec<Address>>
2018where
2019    T: SignedTransaction,
2020    I: IntoParallelIterator<Item = &'a T> + IntoIterator<Item = &'a T> + Send,
2021{
2022    if num_txes < *PARALLEL_SENDER_RECOVERY_THRESHOLD {
2023        txes.into_iter().map(|tx| tx.recover_signer_unchecked()).collect()
2024    } else {
2025        txes.into_par_iter().map(|tx| tx.recover_signer_unchecked()).collect()
2026    }
2027}
2028
2029#[cfg(test)]
2030mod tests {
2031    use crate::{
2032        transaction::{TxEip1559, TxKind, TxLegacy},
2033        RecoveredTx, Transaction, TransactionSigned,
2034    };
2035    use alloy_consensus::Transaction as _;
2036    use alloy_eips::eip2718::{Decodable2718, Encodable2718};
2037    use alloy_primitives::{
2038        address, b256, bytes, hex, Address, Bytes, PrimitiveSignature as Signature, B256, U256,
2039    };
2040    use alloy_rlp::{Decodable, Encodable, Error as RlpError};
2041    use reth_chainspec::MIN_TRANSACTION_GAS;
2042    use reth_codecs::Compact;
2043    use reth_primitives_traits::SignedTransaction;
2044    use std::str::FromStr;
2045
2046    #[test]
2047    fn test_decode_empty_typed_tx() {
2048        let input = [0x80u8];
2049        let res = TransactionSigned::decode(&mut &input[..]).unwrap_err();
2050        assert_eq!(RlpError::InputTooShort, res);
2051    }
2052
2053    #[test]
2054    fn raw_kind_encoding_sanity() {
2055        // check the 0x80 encoding for Create
2056        let mut buf = Vec::new();
2057        TxKind::Create.encode(&mut buf);
2058        assert_eq!(buf, vec![0x80]);
2059
2060        // check decoding
2061        let buf = [0x80];
2062        let decoded = TxKind::decode(&mut &buf[..]).unwrap();
2063        assert_eq!(decoded, TxKind::Create);
2064    }
2065
2066    #[test]
2067    fn test_decode_create_goerli() {
2068        // test that an example create tx from goerli decodes properly
2069        let tx_bytes = hex!("b901f202f901ee05228459682f008459682f11830209bf8080b90195608060405234801561001057600080fd5b50610175806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c80630c49c36c14610030575b600080fd5b61003861004e565b604051610045919061011d565b60405180910390f35b60606020600052600f6020527f68656c6c6f2073746174656d696e64000000000000000000000000000000000060405260406000f35b600081519050919050565b600082825260208201905092915050565b60005b838110156100be5780820151818401526020810190506100a3565b838111156100cd576000848401525b50505050565b6000601f19601f8301169050919050565b60006100ef82610084565b6100f9818561008f565b93506101098185602086016100a0565b610112816100d3565b840191505092915050565b6000602082019050818103600083015261013781846100e4565b90509291505056fea264697066735822122051449585839a4ea5ac23cae4552ef8a96b64ff59d0668f76bfac3796b2bdbb3664736f6c63430008090033c080a0136ebffaa8fc8b9fda9124de9ccb0b1f64e90fbd44251b4c4ac2501e60b104f9a07eb2999eec6d185ef57e91ed099afb0a926c5b536f0155dd67e537c7476e1471");
2070
2071        let decoded = TransactionSigned::decode(&mut &tx_bytes[..]).unwrap();
2072        assert_eq!(tx_bytes.len(), decoded.length());
2073        assert_eq!(tx_bytes, &alloy_rlp::encode(decoded)[..]);
2074    }
2075
2076    #[test]
2077    fn test_decode_recover_mainnet_tx() {
2078        // random mainnet tx <https://etherscan.io/tx/0x86718885c4b4218c6af87d3d0b0d83e3cc465df2a05c048aa4db9f1a6f9de91f>
2079        let tx_bytes = hex!("02f872018307910d808507204d2cb1827d0094388c818ca8b9251b393131c08a736a67ccb19297880320d04823e2701c80c001a0cf024f4815304df2867a1a74e9d2707b6abda0337d2d54a4438d453f4160f190a07ac0e6b3bc9395b5b9c8b9e6d77204a236577a5b18467b9175c01de4faa208d9");
2080
2081        let decoded = TransactionSigned::decode_2718(&mut &tx_bytes[..]).unwrap();
2082        assert_eq!(
2083            decoded.recover_signer(),
2084            Some(Address::from_str("0x95222290DD7278Aa3Ddd389Cc1E1d165CC4BAfe5").unwrap())
2085        );
2086    }
2087
2088    #[test]
2089    // Test vector from https://sepolia.etherscan.io/tx/0x9a22ccb0029bc8b0ddd073be1a1d923b7ae2b2ea52100bae0db4424f9107e9c0
2090    // Blobscan: https://sepolia.blobscan.com/tx/0x9a22ccb0029bc8b0ddd073be1a1d923b7ae2b2ea52100bae0db4424f9107e9c0
2091    fn test_decode_recover_sepolia_4844_tx() {
2092        use alloy_primitives::{address, b256};
2093
2094        // https://sepolia.etherscan.io/getRawTx?tx=0x9a22ccb0029bc8b0ddd073be1a1d923b7ae2b2ea52100bae0db4424f9107e9c0
2095        let raw_tx = alloy_primitives::hex::decode("0x03f9011d83aa36a7820fa28477359400852e90edd0008252089411e9ca82a3a762b4b5bd264d4173a242e7a770648080c08504a817c800f8a5a0012ec3d6f66766bedb002a190126b3549fce0047de0d4c25cffce0dc1c57921aa00152d8e24762ff22b1cfd9f8c0683786a7ca63ba49973818b3d1e9512cd2cec4a0013b98c6c83e066d5b14af2b85199e3d4fc7d1e778dd53130d180f5077e2d1c7a001148b495d6e859114e670ca54fb6e2657f0cbae5b08063605093a4b3dc9f8f1a0011ac212f13c5dff2b2c6b600a79635103d6f580a4221079951181b25c7e654901a0c8de4cced43169f9aa3d36506363b2d2c44f6c49fc1fd91ea114c86f3757077ea01e11fdd0d1934eda0492606ee0bb80a7bf8f35cc5f86ec60fe5031ba48bfd544").unwrap();
2096        let decoded = TransactionSigned::decode_2718(&mut raw_tx.as_slice()).unwrap();
2097        assert!(decoded.is_eip4844());
2098
2099        let from = decoded.recover_signer();
2100        assert_eq!(from, Some(address!("A83C816D4f9b2783761a22BA6FADB0eB0606D7B2")));
2101
2102        let tx = decoded.transaction;
2103
2104        assert_eq!(tx.to(), Some(address!("11E9CA82A3a762b4B5bd264d4173a242e7a77064")));
2105
2106        assert_eq!(
2107            tx.blob_versioned_hashes(),
2108            Some(
2109                &[
2110                    b256!("012ec3d6f66766bedb002a190126b3549fce0047de0d4c25cffce0dc1c57921a"),
2111                    b256!("0152d8e24762ff22b1cfd9f8c0683786a7ca63ba49973818b3d1e9512cd2cec4"),
2112                    b256!("013b98c6c83e066d5b14af2b85199e3d4fc7d1e778dd53130d180f5077e2d1c7"),
2113                    b256!("01148b495d6e859114e670ca54fb6e2657f0cbae5b08063605093a4b3dc9f8f1"),
2114                    b256!("011ac212f13c5dff2b2c6b600a79635103d6f580a4221079951181b25c7e6549"),
2115                ][..]
2116            )
2117        );
2118    }
2119
2120    #[test]
2121    fn decode_transaction_consumes_buffer() {
2122        let bytes = &mut &hex!("b87502f872041a8459682f008459682f0d8252089461815774383099e24810ab832a5b2a5425c154d58829a2241af62c000080c001a059e6b67f48fb32e7e570dfb11e042b5ad2e55e3ce3ce9cd989c7e06e07feeafda0016b83f4f980694ed2eee4d10667242b1f40dc406901b34125b008d334d47469")[..];
2123        let _transaction_res = TransactionSigned::decode(bytes).unwrap();
2124        assert_eq!(
2125            bytes.len(),
2126            0,
2127            "did not consume all bytes in the buffer, {:?} remaining",
2128            bytes.len()
2129        );
2130    }
2131
2132    #[test]
2133    fn decode_multiple_network_txs() {
2134        let bytes = hex!("f86b02843b9aca00830186a094d3e8763675e4c425df46cc3b5c0f6cbdac39604687038d7ea4c68000802ba00eb96ca19e8a77102767a41fc85a36afd5c61ccb09911cec5d3e86e193d9c5aea03a456401896b1b6055311536bf00a718568c744d8c1f9df59879e8350220ca18");
2135        let transaction = Transaction::Legacy(TxLegacy {
2136            chain_id: Some(4u64),
2137            nonce: 2,
2138            gas_price: 1000000000,
2139            gas_limit: 100000,
2140            to: Address::from_str("d3e8763675e4c425df46cc3b5c0f6cbdac396046").unwrap().into(),
2141            value: U256::from(1000000000000000u64),
2142            input: Bytes::default(),
2143        });
2144        let signature = Signature::new(
2145            U256::from_str("0xeb96ca19e8a77102767a41fc85a36afd5c61ccb09911cec5d3e86e193d9c5ae")
2146                .unwrap(),
2147            U256::from_str("0x3a456401896b1b6055311536bf00a718568c744d8c1f9df59879e8350220ca18")
2148                .unwrap(),
2149            false,
2150        );
2151        let hash = b256!("a517b206d2223278f860ea017d3626cacad4f52ff51030dc9a96b432f17f8d34");
2152        test_decode_and_encode(&bytes, transaction, signature, Some(hash));
2153
2154        let bytes = hex!("f86b01843b9aca00830186a094d3e8763675e4c425df46cc3b5c0f6cbdac3960468702769bb01b2a00802ba0e24d8bd32ad906d6f8b8d7741e08d1959df021698b19ee232feba15361587d0aa05406ad177223213df262cb66ccbb2f46bfdccfdfbbb5ffdda9e2c02d977631da");
2155        let transaction = Transaction::Legacy(TxLegacy {
2156            chain_id: Some(4),
2157            nonce: 1u64,
2158            gas_price: 1000000000,
2159            gas_limit: 100000,
2160            to: Address::from_slice(&hex!("d3e8763675e4c425df46cc3b5c0f6cbdac396046")[..]).into(),
2161            value: U256::from(693361000000000u64),
2162            input: Default::default(),
2163        });
2164        let signature = Signature::new(
2165            U256::from_str("0xe24d8bd32ad906d6f8b8d7741e08d1959df021698b19ee232feba15361587d0a")
2166                .unwrap(),
2167            U256::from_str("0x5406ad177223213df262cb66ccbb2f46bfdccfdfbbb5ffdda9e2c02d977631da")
2168                .unwrap(),
2169            false,
2170        );
2171        test_decode_and_encode(&bytes, transaction, signature, None);
2172
2173        let bytes = hex!("f86b0384773594008398968094d3e8763675e4c425df46cc3b5c0f6cbdac39604687038d7ea4c68000802ba0ce6834447c0a4193c40382e6c57ae33b241379c5418caac9cdc18d786fd12071a03ca3ae86580e94550d7c071e3a02eadb5a77830947c9225165cf9100901bee88");
2174        let transaction = Transaction::Legacy(TxLegacy {
2175            chain_id: Some(4),
2176            nonce: 3,
2177            gas_price: 2000000000,
2178            gas_limit: 10000000,
2179            to: Address::from_slice(&hex!("d3e8763675e4c425df46cc3b5c0f6cbdac396046")[..]).into(),
2180            value: U256::from(1000000000000000u64),
2181            input: Bytes::default(),
2182        });
2183        let signature = Signature::new(
2184            U256::from_str("0xce6834447c0a4193c40382e6c57ae33b241379c5418caac9cdc18d786fd12071")
2185                .unwrap(),
2186            U256::from_str("0x3ca3ae86580e94550d7c071e3a02eadb5a77830947c9225165cf9100901bee88")
2187                .unwrap(),
2188            false,
2189        );
2190        test_decode_and_encode(&bytes, transaction, signature, None);
2191
2192        let bytes = hex!("b87502f872041a8459682f008459682f0d8252089461815774383099e24810ab832a5b2a5425c154d58829a2241af62c000080c001a059e6b67f48fb32e7e570dfb11e042b5ad2e55e3ce3ce9cd989c7e06e07feeafda0016b83f4f980694ed2eee4d10667242b1f40dc406901b34125b008d334d47469");
2193        let transaction = Transaction::Eip1559(TxEip1559 {
2194            chain_id: 4,
2195            nonce: 26,
2196            max_priority_fee_per_gas: 1500000000,
2197            max_fee_per_gas: 1500000013,
2198            gas_limit: MIN_TRANSACTION_GAS,
2199            to: Address::from_slice(&hex!("61815774383099e24810ab832a5b2a5425c154d5")[..]).into(),
2200            value: U256::from(3000000000000000000u64),
2201            input: Default::default(),
2202            access_list: Default::default(),
2203        });
2204        let signature = Signature::new(
2205            U256::from_str("0x59e6b67f48fb32e7e570dfb11e042b5ad2e55e3ce3ce9cd989c7e06e07feeafd")
2206                .unwrap(),
2207            U256::from_str("0x016b83f4f980694ed2eee4d10667242b1f40dc406901b34125b008d334d47469")
2208                .unwrap(),
2209            true,
2210        );
2211        test_decode_and_encode(&bytes, transaction, signature, None);
2212
2213        let bytes = hex!("f8650f84832156008287fb94cf7f9e66af820a19257a2108375b180b0ec491678204d2802ca035b7bfeb9ad9ece2cbafaaf8e202e706b4cfaeb233f46198f00b44d4a566a981a0612638fb29427ca33b9a3be2a0a561beecfe0269655be160d35e72d366a6a860");
2214        let transaction = Transaction::Legacy(TxLegacy {
2215            chain_id: Some(4),
2216            nonce: 15,
2217            gas_price: 2200000000,
2218            gas_limit: 34811,
2219            to: Address::from_slice(&hex!("cf7f9e66af820a19257a2108375b180b0ec49167")[..]).into(),
2220            value: U256::from(1234),
2221            input: Bytes::default(),
2222        });
2223        let signature = Signature::new(
2224            U256::from_str("0x35b7bfeb9ad9ece2cbafaaf8e202e706b4cfaeb233f46198f00b44d4a566a981")
2225                .unwrap(),
2226            U256::from_str("0x612638fb29427ca33b9a3be2a0a561beecfe0269655be160d35e72d366a6a860")
2227                .unwrap(),
2228            true,
2229        );
2230        test_decode_and_encode(&bytes, transaction, signature, None);
2231    }
2232
2233    fn test_decode_and_encode(
2234        bytes: &[u8],
2235        transaction: Transaction,
2236        signature: Signature,
2237        hash: Option<B256>,
2238    ) {
2239        let expected = TransactionSigned::new_unhashed(transaction, signature);
2240        if let Some(hash) = hash {
2241            assert_eq!(hash, expected.hash());
2242        }
2243        assert_eq!(bytes.len(), expected.length());
2244
2245        let decoded = TransactionSigned::decode(&mut &bytes[..]).unwrap();
2246        assert_eq!(expected, decoded);
2247        assert_eq!(bytes, &alloy_rlp::encode(expected));
2248    }
2249
2250    #[test]
2251    fn decode_raw_tx_and_recover_signer() {
2252        use alloy_primitives::hex_literal::hex;
2253        // transaction is from ropsten
2254
2255        let hash: B256 =
2256            hex!("559fb34c4a7f115db26cbf8505389475caaab3df45f5c7a0faa4abfa3835306c").into();
2257        let signer: Address = hex!("641c5d790f862a58ec7abcfd644c0442e9c201b3").into();
2258        let raw = hex!("f88b8212b085028fa6ae00830f424094aad593da0c8116ef7d2d594dd6a63241bccfc26c80a48318b64b000000000000000000000000641c5d790f862a58ec7abcfd644c0442e9c201b32aa0a6ef9e170bca5ffb7ac05433b13b7043de667fbb0b4a5e45d3b54fb2d6efcc63a0037ec2c05c3d60c5f5f78244ce0a3859e3a18a36c61efb061b383507d3ce19d2");
2259
2260        let mut pointer = raw.as_ref();
2261        let tx = TransactionSigned::decode(&mut pointer).unwrap();
2262        assert_eq!(tx.hash(), hash, "Expected same hash");
2263        assert_eq!(tx.recover_signer(), Some(signer), "Recovering signer should pass.");
2264    }
2265
2266    #[test]
2267    fn test_envelop_encode() {
2268        // random tx: <https://etherscan.io/getRawTx?tx=0x9448608d36e721ef403c53b00546068a6474d6cbab6816c3926de449898e7bce>
2269        let input = hex!("02f871018302a90f808504890aef60826b6c94ddf4c5025d1a5742cf12f74eec246d4432c295e487e09c3bbcc12b2b80c080a0f21a4eacd0bf8fea9c5105c543be5a1d8c796516875710fafafdf16d16d8ee23a001280915021bb446d1973501a67f93d2b38894a514b976e7b46dc2fe54598d76");
2270        let decoded = TransactionSigned::decode(&mut &input[..]).unwrap();
2271
2272        let encoded = decoded.encoded_2718();
2273        assert_eq!(encoded[..], input);
2274    }
2275
2276    #[test]
2277    fn test_envelop_decode() {
2278        // random tx: <https://etherscan.io/getRawTx?tx=0x9448608d36e721ef403c53b00546068a6474d6cbab6816c3926de449898e7bce>
2279        let input = bytes!("02f871018302a90f808504890aef60826b6c94ddf4c5025d1a5742cf12f74eec246d4432c295e487e09c3bbcc12b2b80c080a0f21a4eacd0bf8fea9c5105c543be5a1d8c796516875710fafafdf16d16d8ee23a001280915021bb446d1973501a67f93d2b38894a514b976e7b46dc2fe54598d76");
2280        let decoded = TransactionSigned::decode_2718(&mut input.as_ref()).unwrap();
2281
2282        let encoded = decoded.encoded_2718();
2283        assert_eq!(encoded, input);
2284    }
2285
2286    #[test]
2287    fn test_decode_signed_ec_recovered_transaction() {
2288        // random tx: <https://etherscan.io/getRawTx?tx=0x9448608d36e721ef403c53b00546068a6474d6cbab6816c3926de449898e7bce>
2289        let input = hex!("02f871018302a90f808504890aef60826b6c94ddf4c5025d1a5742cf12f74eec246d4432c295e487e09c3bbcc12b2b80c080a0f21a4eacd0bf8fea9c5105c543be5a1d8c796516875710fafafdf16d16d8ee23a001280915021bb446d1973501a67f93d2b38894a514b976e7b46dc2fe54598d76");
2290        let tx = TransactionSigned::decode(&mut &input[..]).unwrap();
2291        let recovered = tx.into_ecrecovered().unwrap();
2292
2293        let decoded = RecoveredTx::decode(&mut &alloy_rlp::encode(&recovered)[..]).unwrap();
2294        assert_eq!(recovered, decoded)
2295    }
2296
2297    #[test]
2298    fn test_decode_tx() {
2299        // some random transactions pulled from hive tests
2300        let data = hex!("b86f02f86c0705843b9aca008506fc23ac00830124f89400000000000000000000000000000000000003160180c001a00293c713e2f1eab91c366621ff2f867e05ad7e99d4aa5d069aafeb9e1e8c9b6aa05ec6c0605ff20b57c90a6484ec3b0509e5923733d06f9b69bee9a2dabe4f1352");
2301        let tx = TransactionSigned::decode(&mut data.as_slice()).unwrap();
2302        let mut b = Vec::with_capacity(data.len());
2303        tx.encode(&mut b);
2304        assert_eq!(data.as_slice(), b.as_slice());
2305
2306        let data = hex!("f865048506fc23ac00830124f8940000000000000000000000000000000000000316018032a06b8fdfdcb84790816b7af85b19305f493665fe8b4e7c51ffdd7cc144cd776a60a028a09ab55def7b8d6602ba1c97a0ebbafe64ffc9c8e89520cec97a8edfb2ebe9");
2307        let tx = TransactionSigned::decode(&mut data.as_slice()).unwrap();
2308        let mut b = Vec::with_capacity(data.len());
2309        tx.encode(&mut b);
2310        assert_eq!(data.as_slice(), b.as_slice());
2311    }
2312
2313    #[cfg(feature = "secp256k1")]
2314    proptest::proptest! {
2315        #![proptest_config(proptest::prelude::ProptestConfig::with_cases(1))]
2316
2317        #[test]
2318        fn test_parallel_recovery_order(txes in proptest::collection::vec(
2319            proptest_arbitrary_interop::arb::<Transaction>(),
2320            *crate::transaction::PARALLEL_SENDER_RECOVERY_THRESHOLD * 5
2321        )) {
2322            let mut rng =rand::thread_rng();
2323            let secp = secp256k1::Secp256k1::new();
2324            let txes: Vec<TransactionSigned> = txes.into_iter().map(|mut tx| {
2325                 if let Some(chain_id) = tx.chain_id() {
2326                    // Otherwise we might overflow when calculating `v` on `recalculate_hash`
2327                    tx.set_chain_id(chain_id % (u64::MAX / 2 - 36));
2328                }
2329
2330                let key_pair = secp256k1::Keypair::new(&secp, &mut rng);
2331
2332                let signature =
2333                    crate::sign_message(B256::from_slice(&key_pair.secret_bytes()[..]), tx.signature_hash()).unwrap();
2334
2335                TransactionSigned::new_unhashed(tx, signature)
2336            }).collect();
2337
2338            let parallel_senders = TransactionSigned::recover_signers(&txes, txes.len()).unwrap();
2339            let seq_senders = txes.iter().map(|tx| tx.recover_signer()).collect::<Option<Vec<_>>>().unwrap();
2340
2341            assert_eq!(parallel_senders, seq_senders);
2342        }
2343    }
2344
2345    // <https://etherscan.io/tx/0x280cde7cdefe4b188750e76c888f13bd05ce9a4d7767730feefe8a0e50ca6fc4>
2346    #[test]
2347    fn recover_legacy_singer() {
2348        let data = hex!("f9015482078b8505d21dba0083022ef1947a250d5630b4cf539739df2c5dacb4c659f2488d880c46549a521b13d8b8e47ff36ab50000000000000000000000000000000000000000000066ab5a608bd00a23f2fe000000000000000000000000000000000000000000000000000000000000008000000000000000000000000048c04ed5691981c42154c6167398f95e8f38a7ff00000000000000000000000000000000000000000000000000000000632ceac70000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006c6ee5e31d828de241282b9606c8e98ea48526e225a0c9077369501641a92ef7399ff81c21639ed4fd8fc69cb793cfa1dbfab342e10aa0615facb2f1bcf3274a354cfe384a38d0cc008a11c2dd23a69111bc6930ba27a8");
2349        let tx = TransactionSigned::decode_rlp_legacy_transaction(&mut data.as_slice()).unwrap();
2350        assert!(tx.is_legacy());
2351        let sender = tx.recover_signer().unwrap();
2352        assert_eq!(sender, address!("a12e1462d0ceD572f396F58B6E2D03894cD7C8a4"));
2353    }
2354
2355    // <https://github.com/alloy-rs/alloy/issues/141>
2356    // <https://etherscan.io/tx/0xce4dc6d7a7549a98ee3b071b67e970879ff51b5b95d1c340bacd80fa1e1aab31>
2357    #[test]
2358    fn recover_enveloped() {
2359        let data = hex!("02f86f0102843b9aca0085029e7822d68298f094d9e1459a7a482635700cbc20bbaf52d495ab9c9680841b55ba3ac080a0c199674fcb29f353693dd779c017823b954b3c69dffa3cd6b2a6ff7888798039a028ca912de909e7e6cdef9cdcaf24c54dd8c1032946dfa1d85c206b32a9064fe8");
2360        let tx = TransactionSigned::decode_2718(&mut data.as_slice()).unwrap();
2361        let sender = tx.recover_signer().unwrap();
2362        assert_eq!(sender, address!("001e2b7dE757bA469a57bF6b23d982458a07eFcE"));
2363        assert_eq!(tx.to(), Some(address!("D9e1459A7A482635700cBc20BBAF52D495Ab9C96")));
2364        assert_eq!(tx.input().as_ref(), hex!("1b55ba3a"));
2365        let encoded = tx.encoded_2718();
2366        assert_eq!(encoded.as_ref(), data.to_vec());
2367    }
2368
2369    // <https://github.com/paradigmxyz/reth/issues/7750>
2370    // <https://etherscan.io/tx/0x2084b8144eea4031c2fa7dfe343498c5e665ca85ed17825f2925f0b5b01c36ac>
2371    #[test]
2372    fn recover_pre_eip2() {
2373        let data = hex!("f8ea0c850ba43b7400832dc6c0942935aa0a2d2fbb791622c29eb1c117b65b7a908580b884590528a9000000000000000000000001878ace42092b7f1ae1f28d16c1272b1aa80ca4670000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000d02ab486cedc0000000000000000000000000000000000000000000000000000557fe293cabc08cf1ca05bfaf3fda0a56b49cc78b22125feb5ae6a99d2b4781f00507d8b02c173771c85a0b5da0dbe6c5bc53740d0071fc83eb17ba0f709e49e9ae7df60dee625ef51afc5");
2374        let tx = TransactionSigned::decode_2718(&mut data.as_slice()).unwrap();
2375        let sender = tx.recover_signer();
2376        assert!(sender.is_none());
2377        let sender = tx.recover_signer_unchecked().unwrap();
2378
2379        assert_eq!(sender, address!("7e9e359edf0dbacf96a9952fa63092d919b0842b"));
2380    }
2381
2382    #[test]
2383    fn transaction_signed_no_hash_zstd_codec() {
2384        // will use same signature everywhere.
2385        // We don't need signature to match tx, just decoded to the same signature
2386        let signature = Signature::new(
2387            U256::from_str("0xeb96ca19e8a77102767a41fc85a36afd5c61ccb09911cec5d3e86e193d9c5ae")
2388                .unwrap(),
2389            U256::from_str("0x3a456401896b1b6055311536bf00a718568c744d8c1f9df59879e8350220ca18")
2390                .unwrap(),
2391            false,
2392        );
2393
2394        let inputs: Vec<Vec<u8>> = vec![
2395            vec![],
2396            vec![0],
2397            vec![255],
2398            vec![1u8; 31],
2399            vec![255u8; 31],
2400            vec![1u8; 32],
2401            vec![255u8; 32],
2402            vec![1u8; 64],
2403            vec![255u8; 64],
2404        ];
2405
2406        for input in inputs {
2407            let transaction = Transaction::Legacy(TxLegacy {
2408                chain_id: Some(4u64),
2409                nonce: 2,
2410                gas_price: 1000000000,
2411                gas_limit: 100000,
2412                to: Address::from_str("d3e8763675e4c425df46cc3b5c0f6cbdac396046").unwrap().into(),
2413                value: U256::from(1000000000000000u64),
2414                input: Bytes::from(input),
2415            });
2416
2417            let tx = TransactionSigned::new_unhashed(transaction, signature);
2418            test_transaction_signed_to_from_compact(tx);
2419        }
2420    }
2421
2422    fn test_transaction_signed_to_from_compact(tx: TransactionSigned) {
2423        // zstd aware `to_compact`
2424        let mut buff: Vec<u8> = Vec::new();
2425        let written_bytes = tx.to_compact(&mut buff);
2426        let (decoded, _) = TransactionSigned::from_compact(&buff, written_bytes);
2427        assert_eq!(tx, decoded);
2428    }
2429
2430    #[test]
2431    fn create_txs_disallowed_for_eip4844() {
2432        let data =
2433            [3, 208, 128, 128, 123, 128, 120, 128, 129, 129, 128, 192, 129, 129, 192, 128, 128, 9];
2434        let res = TransactionSigned::decode_2718(&mut &data[..]);
2435
2436        assert!(res.is_err());
2437    }
2438}