revm_context_interface/
transaction.rs

1//! Transaction trait [`Transaction`] and associated types.
2mod alloy_types;
3pub mod eip2930;
4pub mod eip7702;
5pub mod transaction_type;
6
7pub use alloy_types::{
8    AccessList, AccessListItem, Authorization, RecoveredAuthority, RecoveredAuthorization,
9    SignedAuthorization,
10};
11pub use eip2930::AccessListItemTr;
12pub use eip7702::AuthorizationTr;
13pub use transaction_type::TransactionType;
14
15use crate::result::InvalidTransaction;
16use auto_impl::auto_impl;
17use core::cmp::min;
18use core::fmt::Debug;
19use primitives::{eip4844::GAS_PER_BLOB, Address, Bytes, TxKind, B256, U256};
20
21/// Transaction validity error types.
22pub trait TransactionError: Debug + core::error::Error {}
23
24/// Main Transaction trait that abstracts and specifies all transaction currently supported by Ethereum
25///
26/// Access to any associated type is gaited behind [`tx_type`][Transaction::tx_type] function.
27///
28/// It can be extended to support new transaction types and only transaction types can be
29/// deprecated by not returning tx_type.
30#[auto_impl(&, Box, Arc, Rc)]
31pub trait Transaction {
32    /// EIP-2930 Access list item type.
33    type AccessListItem<'a>: AccessListItemTr
34    where
35        Self: 'a;
36
37    /// EIP-7702 Authorization type.
38    type Authorization<'a>: AuthorizationTr
39    where
40        Self: 'a;
41
42    /// Returns the transaction type.
43    ///
44    /// Depending on this field other functions should be called.
45    fn tx_type(&self) -> u8;
46
47    /// Caller aka Author aka transaction signer.
48    ///
49    /// Note : Common field for all transactions.
50    fn caller(&self) -> Address;
51
52    /// The maximum amount of gas the transaction can use.
53    ///
54    /// Note : Common field for all transactions.
55    fn gas_limit(&self) -> u64;
56
57    /// The value sent to the receiver of [`TxKind::Call`][primitives::TxKind::Call].
58    ///
59    /// Note : Common field for all transactions.
60    fn value(&self) -> U256;
61
62    /// Returns the input data of the transaction.
63    ///
64    /// Note : Common field for all transactions.
65    fn input(&self) -> &Bytes;
66
67    /// The nonce of the transaction.
68    ///
69    /// Note : Common field for all transactions.
70    fn nonce(&self) -> u64;
71
72    /// Transaction kind. It can be Call or Create.
73    ///
74    /// Kind is applicable for: Legacy, EIP-2930, EIP-1559
75    /// And is Call for EIP-4844 and EIP-7702 transactions.
76    fn kind(&self) -> TxKind;
77
78    /// Chain Id is optional for legacy transactions.
79    ///
80    /// As it was introduced in EIP-155.
81    fn chain_id(&self) -> Option<u64>;
82
83    /// Gas price for the transaction.
84    /// It is only applicable for Legacy and EIP-2930 transactions.
85    /// For Eip1559 it is max_fee_per_gas.
86    fn gas_price(&self) -> u128;
87
88    /// Access list for the transaction.
89    ///
90    /// Introduced in EIP-2930.
91    fn access_list(&self) -> Option<impl Iterator<Item = Self::AccessListItem<'_>>>;
92
93    /// Returns vector of fixed size hash(32 bytes)
94    ///
95    /// Note : EIP-4844 transaction field.
96    fn blob_versioned_hashes(&self) -> &[B256];
97
98    /// Max fee per data gas
99    ///
100    /// Note : EIP-4844 transaction field.
101    fn max_fee_per_blob_gas(&self) -> u128;
102
103    /// Total gas for all blobs. Max number of blocks is already checked
104    /// so we dont need to check for overflow.
105    fn total_blob_gas(&self) -> u64 {
106        GAS_PER_BLOB * self.blob_versioned_hashes().len() as u64
107    }
108
109    /// Calculates the maximum [EIP-4844] `data_fee` of the transaction.
110    ///
111    /// This is used for ensuring that the user has at least enough funds to pay the
112    /// `max_fee_per_blob_gas * total_blob_gas`, on top of regular gas costs.
113    ///
114    /// See EIP-4844:
115    /// <https://github.com/ethereum/EIPs/blob/master/EIPS/eip-4844.md#execution-layer-validation>
116    fn calc_max_data_fee(&self) -> U256 {
117        U256::from((self.total_blob_gas() as u128).saturating_mul(self.max_fee_per_blob_gas()))
118    }
119
120    /// Returns length of the authorization list.
121    ///
122    /// # Note
123    ///
124    /// Transaction is considered invalid if list is empty.
125    fn authorization_list_len(&self) -> usize;
126
127    /// List of authorizations, that contains the signature that authorizes this
128    /// caller to place the code to signer account.
129    ///
130    /// Set EOA account code for one transaction
131    ///
132    /// [EIP-Set EOA account code for one transaction](https://eips.ethereum.org/EIPS/eip-7702)
133    fn authorization_list(&self) -> impl Iterator<Item = Self::Authorization<'_>>;
134
135    // TODO(EOF)
136    // /// List of initcodes found in Initcode transaction. Initcodes can only be accessed
137    // /// by TXCREATE opcode to create a new EOF contract.
138    // ///
139    // /// Each transaction can contain up to [`primitives::eof::MAX_INITCODE_COUNT`] initcodes,
140    // /// with each initcode not exceeding [`primitives::MAX_INITCODE_SIZE`] bytes in size.
141    // ///
142    // /// EIP link: <https://eips.ethereum.org/EIPS/eip-7873>
143    // fn initcodes(&self) -> &[Bytes];
144
145    /// Returns maximum fee that can be paid for the transaction.
146    fn max_fee_per_gas(&self) -> u128 {
147        self.gas_price()
148    }
149
150    /// Maximum priority fee per gas.
151    fn max_priority_fee_per_gas(&self) -> Option<u128>;
152
153    /// Returns effective gas price is gas price field for Legacy and Eip2930 transaction.
154    ///
155    /// While for transactions after Eip1559 it is minimum of max_fee and `base + max_priority_fee`.
156    fn effective_gas_price(&self, base_fee: u128) -> u128 {
157        if self.tx_type() == TransactionType::Legacy as u8
158            || self.tx_type() == TransactionType::Eip2930 as u8
159        {
160            return self.gas_price();
161        }
162
163        // for EIP-1559 tx and onwards gas_price represents maximum price.
164        let max_price = self.gas_price();
165        let Some(max_priority_fee) = self.max_priority_fee_per_gas() else {
166            return max_price;
167        };
168        min(max_price, base_fee.saturating_add(max_priority_fee))
169    }
170
171    /// Returns the maximum balance that can be spent by the transaction.
172    ///
173    /// Return U256 or error if all values overflow U256 number.
174    fn max_balance_spending(&self) -> Result<U256, InvalidTransaction> {
175        // gas_limit * max_fee + value + additional_gas_cost
176        let mut max_balance_spending = (self.gas_limit() as u128)
177            .checked_mul(self.max_fee_per_gas())
178            .and_then(|gas_cost| U256::from(gas_cost).checked_add(self.value()))
179            .ok_or(InvalidTransaction::OverflowPaymentInTransaction)?;
180
181        // add blob fee
182        if self.tx_type() == TransactionType::Eip4844 {
183            let data_fee = self.calc_max_data_fee();
184            max_balance_spending = max_balance_spending
185                .checked_add(data_fee)
186                .ok_or(InvalidTransaction::OverflowPaymentInTransaction)?;
187        }
188        Ok(max_balance_spending)
189    }
190
191    /// Returns the effective balance that is going to be spent that depends on base_fee
192    /// Multiplication for gas are done in u128 type (saturated) and value is added as U256 type.
193    ///
194    /// # Reason
195    ///
196    /// This is done for performance reasons and it is known to be safe as there is no more that u128::MAX value of eth in existence.
197    ///
198    /// This is always strictly less than [`Self::max_balance_spending`].
199    ///
200    /// Return U256 or error if all values overflow U256 number.
201    fn effective_balance_spending(
202        &self,
203        base_fee: u128,
204        blob_price: u128,
205    ) -> Result<U256, InvalidTransaction> {
206        // gas_limit * max_fee + value + additional_gas_cost
207        let mut effective_balance_spending = (self.gas_limit() as u128)
208            .checked_mul(self.effective_gas_price(base_fee))
209            .and_then(|gas_cost| U256::from(gas_cost).checked_add(self.value()))
210            .ok_or(InvalidTransaction::OverflowPaymentInTransaction)?;
211
212        // add blob fee
213        if self.tx_type() == TransactionType::Eip4844 {
214            let blob_gas = self.total_blob_gas() as u128;
215            effective_balance_spending = effective_balance_spending
216                .checked_add(U256::from(blob_price.saturating_mul(blob_gas)))
217                .ok_or(InvalidTransaction::OverflowPaymentInTransaction)?;
218        }
219
220        Ok(effective_balance_spending)
221    }
222}