alloy_consensus/transaction/
mod.rs

1//! Transaction types.
2
3use crate::Signed;
4use alloc::vec::Vec;
5use alloy_eips::{eip2930::AccessList, eip7702::SignedAuthorization};
6use alloy_primitives::{keccak256, Address, Bytes, ChainId, Selector, TxKind, B256, U256};
7use core::{any, fmt};
8
9mod eip1559;
10pub use eip1559::TxEip1559;
11
12mod eip2930;
13pub use eip2930::TxEip2930;
14
15mod eip7702;
16pub use eip7702::TxEip7702;
17
18mod envelope;
19#[cfg(all(feature = "serde", feature = "serde-bincode-compat"))]
20pub use envelope::serde_bincode_compat as envelope_serde_bincode_compat;
21
22/// [EIP-4844] constants, helpers, and types.
23pub mod eip4844;
24pub mod pooled;
25pub use pooled::PooledTransaction;
26
27use alloy_eips::eip4844::DATA_GAS_PER_BLOB;
28pub use alloy_eips::eip4844::{
29    builder::{SidecarBuilder, SidecarCoder, SimpleCoder},
30    utils as eip4844_utils, Blob, BlobTransactionSidecar, Bytes48,
31};
32#[cfg(feature = "kzg")]
33pub use eip4844::BlobTransactionValidationError;
34pub use eip4844::{TxEip4844, TxEip4844Variant, TxEip4844WithSidecar};
35
36/// Re-export for convenience
37pub use either::Either;
38
39pub use envelope::{EthereumTxEnvelope, TxEnvelope, TxType};
40
41mod legacy;
42pub use legacy::{from_eip155_value, to_eip155_value, TxLegacy};
43
44mod rlp;
45#[doc(hidden)]
46pub use rlp::{RlpEcdsaDecodableTx, RlpEcdsaEncodableTx, RlpEcdsaTx};
47
48mod typed;
49pub use typed::{EthereumTypedTransaction, TypedTransaction};
50
51mod meta;
52pub use meta::{TransactionInfo, TransactionMeta};
53
54mod recovered;
55pub use recovered::{Recovered, SignerRecoverable};
56
57#[cfg(feature = "serde")]
58pub use legacy::signed_legacy_serde;
59
60/// Bincode-compatible serde implementations for transaction types.
61#[cfg(all(feature = "serde", feature = "serde-bincode-compat"))]
62pub mod serde_bincode_compat {
63    pub use super::{
64        eip1559::serde_bincode_compat::*, eip2930::serde_bincode_compat::*,
65        eip7702::serde_bincode_compat::*, envelope::serde_bincode_compat::*,
66        legacy::serde_bincode_compat::*, typed::serde_bincode_compat::*,
67    };
68}
69
70use alloy_eips::Typed2718;
71
72/// Represents a minimal EVM transaction.
73/// Currently, EIP-1559, EIP-4844, and EIP-7702 support dynamic fees.
74/// We call these transactions "dynamic fee transactions".
75/// We call non dynamic fee transactions(EIP-155, EIP-2930) "legacy fee transactions".
76#[doc(alias = "Tx")]
77#[auto_impl::auto_impl(&, Arc)]
78pub trait Transaction: Typed2718 + fmt::Debug + any::Any + Send + Sync + 'static {
79    /// Get `chain_id`.
80    fn chain_id(&self) -> Option<ChainId>;
81
82    /// Get `nonce`.
83    fn nonce(&self) -> u64;
84
85    /// Get `gas_limit`.
86    fn gas_limit(&self) -> u64;
87
88    /// Get `gas_price`.
89    fn gas_price(&self) -> Option<u128>;
90
91    /// For dynamic fee transactions returns the maximum fee per gas the caller is willing to pay.
92    ///
93    /// For legacy fee transactions this is `gas_price`.
94    ///
95    /// This is also commonly referred to as the "Gas Fee Cap".
96    fn max_fee_per_gas(&self) -> u128;
97
98    /// For dynamic fee transactions returns the Priority fee the caller is paying to the block
99    /// author.
100    ///
101    /// This will return `None` for legacy fee transactions
102    fn max_priority_fee_per_gas(&self) -> Option<u128>;
103
104    /// Max fee per blob gas for EIP-4844 transaction.
105    ///
106    /// Returns `None` for non-eip4844 transactions.
107    ///
108    /// This is also commonly referred to as the "Blob Gas Fee Cap".
109    fn max_fee_per_blob_gas(&self) -> Option<u128>;
110
111    /// Return the max priority fee per gas if the transaction is an dynamic fee transaction, and
112    /// otherwise return the gas price.
113    ///
114    /// # Warning
115    ///
116    /// This is different than the `max_priority_fee_per_gas` method, which returns `None` for
117    /// legacy fee transactions.
118    fn priority_fee_or_price(&self) -> u128;
119
120    /// Returns the effective gas price for the given base fee.
121    ///
122    /// If the transaction is a legacy fee transaction, the gas price is returned.
123    fn effective_gas_price(&self, base_fee: Option<u64>) -> u128;
124
125    /// Returns the effective tip for this transaction.
126    ///
127    /// For dynamic fee transactions: `min(max_fee_per_gas - base_fee, max_priority_fee_per_gas)`.
128    /// For legacy fee transactions: `gas_price - base_fee`.
129    fn effective_tip_per_gas(&self, base_fee: u64) -> Option<u128> {
130        let base_fee = base_fee as u128;
131
132        let max_fee_per_gas = self.max_fee_per_gas();
133
134        // Check if max_fee_per_gas is less than base_fee
135        if max_fee_per_gas < base_fee {
136            return None;
137        }
138
139        // Calculate the difference between max_fee_per_gas and base_fee
140        let fee = max_fee_per_gas - base_fee;
141
142        // Compare the fee with max_priority_fee_per_gas (or gas price for legacy fee transactions)
143        self.max_priority_fee_per_gas()
144            .map_or(Some(fee), |priority_fee| Some(fee.min(priority_fee)))
145    }
146
147    /// Returns `true` if the transaction supports dynamic fees.
148    fn is_dynamic_fee(&self) -> bool;
149
150    /// Returns the transaction kind.
151    fn kind(&self) -> TxKind;
152
153    /// Returns true if the transaction is a contract creation.
154    /// We don't provide a default implementation via `kind` as it copies the 21-byte
155    /// [`TxKind`] for this simple check. A proper implementation shouldn't allocate.
156    fn is_create(&self) -> bool;
157
158    /// Get the transaction's address of the contract that will be called, or the address that will
159    /// receive the transfer.
160    ///
161    /// Returns `None` if this is a `CREATE` transaction.
162    fn to(&self) -> Option<Address> {
163        self.kind().to().copied()
164    }
165
166    /// Get `value`.
167    fn value(&self) -> U256;
168
169    /// Get `data`.
170    fn input(&self) -> &Bytes;
171
172    /// Returns the first 4bytes of the calldata for a function call.
173    ///
174    /// The selector specifies the function to be called.
175    fn function_selector(&self) -> Option<&Selector> {
176        if self.kind().is_call() {
177            self.input().get(..4).and_then(|s| TryFrom::try_from(s).ok())
178        } else {
179            None
180        }
181    }
182
183    /// Returns the EIP-2930 `access_list` for the particular transaction type. Returns `None` for
184    /// older transaction types.
185    fn access_list(&self) -> Option<&AccessList>;
186
187    /// Blob versioned hashes for eip4844 transaction. For previous transaction types this is
188    /// `None`.
189    fn blob_versioned_hashes(&self) -> Option<&[B256]>;
190
191    /// Returns the number of blobs of this transaction.
192    ///
193    /// This is convenience function for `len(blob_versioned_hashes)`.
194    ///
195    /// Returns `None` for non-eip4844 transactions.
196    fn blob_count(&self) -> Option<u64> {
197        self.blob_versioned_hashes().map(|h| h.len() as u64)
198    }
199
200    /// Returns the total gas for all blobs in this transaction.
201    ///
202    /// Returns `None` for non-eip4844 transactions.
203    #[inline]
204    fn blob_gas_used(&self) -> Option<u64> {
205        // SAFETY: we don't expect u64::MAX / DATA_GAS_PER_BLOB hashes in a single transaction
206        self.blob_count().map(|blobs| blobs * DATA_GAS_PER_BLOB)
207    }
208
209    /// Returns the [`SignedAuthorization`] list of the transaction.
210    ///
211    /// Returns `None` if this transaction is not EIP-7702.
212    fn authorization_list(&self) -> Option<&[SignedAuthorization]>;
213
214    /// Returns the number of blobs of [`SignedAuthorization`] in this transactions
215    ///
216    /// This is convenience function for `len(authorization_list)`.
217    ///
218    /// Returns `None` for non-eip7702 transactions.
219    fn authorization_count(&self) -> Option<u64> {
220        self.authorization_list().map(|auths| auths.len() as u64)
221    }
222}
223
224/// A signable transaction.
225///
226/// A transaction can have multiple signature types. This is usually
227/// [`alloy_primitives::PrimitiveSignature`], however, it may be different for future EIP-2718
228/// transaction types, or in other networks. For example, in Optimism, the deposit transaction
229/// signature is the unit type `()`.
230#[doc(alias = "SignableTx", alias = "TxSignable")]
231pub trait SignableTransaction<Signature>: Transaction {
232    /// Sets `chain_id`.
233    ///
234    /// Prefer [`set_chain_id_checked`](Self::set_chain_id_checked).
235    fn set_chain_id(&mut self, chain_id: ChainId);
236
237    /// Set `chain_id` if it is not already set. Checks that the provided `chain_id` matches the
238    /// existing `chain_id` if it is already set, returning `false` if they do not match.
239    fn set_chain_id_checked(&mut self, chain_id: ChainId) -> bool {
240        match self.chain_id() {
241            Some(tx_chain_id) => {
242                if tx_chain_id != chain_id {
243                    return false;
244                }
245                self.set_chain_id(chain_id);
246            }
247            None => {
248                self.set_chain_id(chain_id);
249            }
250        }
251        true
252    }
253
254    /// RLP-encodes the transaction for signing.
255    fn encode_for_signing(&self, out: &mut dyn alloy_rlp::BufMut);
256
257    /// Outputs the length of the signature RLP encoding for the transaction.
258    fn payload_len_for_signature(&self) -> usize;
259
260    /// RLP-encodes the transaction for signing it. Used to calculate `signature_hash`.
261    ///
262    /// See [`SignableTransaction::encode_for_signing`].
263    fn encoded_for_signing(&self) -> Vec<u8> {
264        let mut buf = Vec::with_capacity(self.payload_len_for_signature());
265        self.encode_for_signing(&mut buf);
266        buf
267    }
268
269    /// Calculate the signing hash for the transaction.
270    fn signature_hash(&self) -> B256 {
271        keccak256(self.encoded_for_signing())
272    }
273
274    /// Convert to a [`Signed`] object.
275    fn into_signed(self, signature: Signature) -> Signed<Self, Signature>
276    where
277        Self: Sized,
278    {
279        Signed::new_unhashed(self, signature)
280    }
281}
282
283// TODO: Remove in favor of dyn trait upcasting (TBD, see https://github.com/rust-lang/rust/issues/65991#issuecomment-1903120162)
284#[doc(hidden)]
285impl<S: 'static> dyn SignableTransaction<S> {
286    pub fn __downcast_ref<T: any::Any>(&self) -> Option<&T> {
287        if any::Any::type_id(self) == any::TypeId::of::<T>() {
288            unsafe { Some(&*(self as *const _ as *const T)) }
289        } else {
290            None
291        }
292    }
293}
294
295#[cfg(feature = "serde")]
296impl<T: Transaction> Transaction for alloy_serde::WithOtherFields<T> {
297    #[inline]
298    fn chain_id(&self) -> Option<ChainId> {
299        self.inner.chain_id()
300    }
301
302    #[inline]
303    fn nonce(&self) -> u64 {
304        self.inner.nonce()
305    }
306
307    #[inline]
308    fn gas_limit(&self) -> u64 {
309        self.inner.gas_limit()
310    }
311
312    #[inline]
313    fn gas_price(&self) -> Option<u128> {
314        self.inner.gas_price()
315    }
316
317    #[inline]
318    fn max_fee_per_gas(&self) -> u128 {
319        self.inner.max_fee_per_gas()
320    }
321
322    #[inline]
323    fn max_priority_fee_per_gas(&self) -> Option<u128> {
324        self.inner.max_priority_fee_per_gas()
325    }
326
327    #[inline]
328    fn max_fee_per_blob_gas(&self) -> Option<u128> {
329        self.inner.max_fee_per_blob_gas()
330    }
331
332    #[inline]
333    fn priority_fee_or_price(&self) -> u128 {
334        self.inner.priority_fee_or_price()
335    }
336
337    fn effective_gas_price(&self, base_fee: Option<u64>) -> u128 {
338        self.inner.effective_gas_price(base_fee)
339    }
340
341    #[inline]
342    fn is_dynamic_fee(&self) -> bool {
343        self.inner.is_dynamic_fee()
344    }
345
346    #[inline]
347    fn kind(&self) -> TxKind {
348        self.inner.kind()
349    }
350
351    #[inline]
352    fn is_create(&self) -> bool {
353        self.inner.is_create()
354    }
355
356    #[inline]
357    fn value(&self) -> U256 {
358        self.inner.value()
359    }
360
361    #[inline]
362    fn input(&self) -> &Bytes {
363        self.inner.input()
364    }
365
366    #[inline]
367    fn access_list(&self) -> Option<&AccessList> {
368        self.inner.access_list()
369    }
370
371    #[inline]
372    fn blob_versioned_hashes(&self) -> Option<&[B256]> {
373        self.inner.blob_versioned_hashes()
374    }
375
376    #[inline]
377    fn authorization_list(&self) -> Option<&[SignedAuthorization]> {
378        self.inner.authorization_list()
379    }
380}
381
382impl<L, R> Transaction for either::Either<L, R>
383where
384    L: Transaction,
385    R: Transaction,
386{
387    fn chain_id(&self) -> Option<ChainId> {
388        match self {
389            Self::Left(tx) => tx.chain_id(),
390            Self::Right(tx) => tx.chain_id(),
391        }
392    }
393
394    fn nonce(&self) -> u64 {
395        match self {
396            Self::Left(tx) => tx.nonce(),
397            Self::Right(tx) => tx.nonce(),
398        }
399    }
400
401    fn gas_limit(&self) -> u64 {
402        match self {
403            Self::Left(tx) => tx.gas_limit(),
404            Self::Right(tx) => tx.gas_limit(),
405        }
406    }
407
408    fn gas_price(&self) -> Option<u128> {
409        match self {
410            Self::Left(tx) => tx.gas_price(),
411            Self::Right(tx) => tx.gas_price(),
412        }
413    }
414
415    fn max_fee_per_gas(&self) -> u128 {
416        match self {
417            Self::Left(tx) => tx.max_fee_per_gas(),
418            Self::Right(tx) => tx.max_fee_per_gas(),
419        }
420    }
421
422    fn max_priority_fee_per_gas(&self) -> Option<u128> {
423        match self {
424            Self::Left(tx) => tx.max_priority_fee_per_gas(),
425            Self::Right(tx) => tx.max_priority_fee_per_gas(),
426        }
427    }
428
429    fn max_fee_per_blob_gas(&self) -> Option<u128> {
430        match self {
431            Self::Left(tx) => tx.max_fee_per_blob_gas(),
432            Self::Right(tx) => tx.max_fee_per_blob_gas(),
433        }
434    }
435
436    fn priority_fee_or_price(&self) -> u128 {
437        match self {
438            Self::Left(tx) => tx.priority_fee_or_price(),
439            Self::Right(tx) => tx.priority_fee_or_price(),
440        }
441    }
442
443    fn effective_gas_price(&self, base_fee: Option<u64>) -> u128 {
444        match self {
445            Self::Left(tx) => tx.effective_gas_price(base_fee),
446            Self::Right(tx) => tx.effective_gas_price(base_fee),
447        }
448    }
449
450    fn effective_tip_per_gas(&self, base_fee: u64) -> Option<u128> {
451        match self {
452            Self::Left(tx) => tx.effective_tip_per_gas(base_fee),
453            Self::Right(tx) => tx.effective_tip_per_gas(base_fee),
454        }
455    }
456
457    fn is_dynamic_fee(&self) -> bool {
458        match self {
459            Self::Left(tx) => tx.is_dynamic_fee(),
460            Self::Right(tx) => tx.is_dynamic_fee(),
461        }
462    }
463
464    fn kind(&self) -> TxKind {
465        match self {
466            Self::Left(tx) => tx.kind(),
467            Self::Right(tx) => tx.kind(),
468        }
469    }
470
471    fn is_create(&self) -> bool {
472        match self {
473            Self::Left(tx) => tx.is_create(),
474            Self::Right(tx) => tx.is_create(),
475        }
476    }
477
478    fn to(&self) -> Option<Address> {
479        match self {
480            Self::Left(tx) => tx.to(),
481            Self::Right(tx) => tx.to(),
482        }
483    }
484
485    fn value(&self) -> U256 {
486        match self {
487            Self::Left(tx) => tx.value(),
488            Self::Right(tx) => tx.value(),
489        }
490    }
491
492    fn input(&self) -> &Bytes {
493        match self {
494            Self::Left(tx) => tx.input(),
495            Self::Right(tx) => tx.input(),
496        }
497    }
498
499    fn function_selector(&self) -> Option<&Selector> {
500        match self {
501            Self::Left(tx) => tx.function_selector(),
502            Self::Right(tx) => tx.function_selector(),
503        }
504    }
505
506    fn access_list(&self) -> Option<&AccessList> {
507        match self {
508            Self::Left(tx) => tx.access_list(),
509            Self::Right(tx) => tx.access_list(),
510        }
511    }
512
513    fn blob_versioned_hashes(&self) -> Option<&[B256]> {
514        match self {
515            Self::Left(tx) => tx.blob_versioned_hashes(),
516            Self::Right(tx) => tx.blob_versioned_hashes(),
517        }
518    }
519
520    fn blob_count(&self) -> Option<u64> {
521        match self {
522            Self::Left(tx) => tx.blob_count(),
523            Self::Right(tx) => tx.blob_count(),
524        }
525    }
526
527    fn blob_gas_used(&self) -> Option<u64> {
528        match self {
529            Self::Left(tx) => tx.blob_gas_used(),
530            Self::Right(tx) => tx.blob_gas_used(),
531        }
532    }
533
534    fn authorization_list(&self) -> Option<&[SignedAuthorization]> {
535        match self {
536            Self::Left(tx) => tx.authorization_list(),
537            Self::Right(tx) => tx.authorization_list(),
538        }
539    }
540
541    fn authorization_count(&self) -> Option<u64> {
542        match self {
543            Self::Left(tx) => tx.authorization_count(),
544            Self::Right(tx) => tx.authorization_count(),
545        }
546    }
547}