revm_context/
tx.rs

1//! This module contains [`TxEnv`] struct and implements [`Transaction`] trait for it.
2use crate::TransactionType;
3use context_interface::{
4    either::Either,
5    transaction::{
6        AccessList, AccessListItem, RecoveredAuthorization, SignedAuthorization, Transaction,
7    },
8};
9use core::fmt::Debug;
10use primitives::{Address, Bytes, TxKind, B256, U256};
11use std::vec::Vec;
12
13/// The transaction environment
14#[derive(Clone, Debug, PartialEq, Eq)]
15#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
16pub struct TxEnv {
17    /// Transaction type
18    pub tx_type: u8,
19    /// Caller aka Author aka transaction signer
20    pub caller: Address,
21    /// The gas limit of the transaction.
22    pub gas_limit: u64,
23    /// The gas price of the transaction.
24    ///
25    /// For EIP-1559 transaction this represent max_gas_fee.
26    pub gas_price: u128,
27    /// The destination of the transaction
28    pub kind: TxKind,
29    /// The value sent to `transact_to`
30    pub value: U256,
31    /// The data of the transaction
32    pub data: Bytes,
33
34    /// The nonce of the transaction
35    pub nonce: u64,
36
37    /// The chain ID of the transaction
38    ///
39    /// If set to [`None`], no checks are performed.
40    ///
41    /// Incorporated as part of the Spurious Dragon upgrade via [EIP-155].
42    ///
43    /// [EIP-155]: https://eips.ethereum.org/EIPS/eip-155
44    pub chain_id: Option<u64>,
45
46    /// A list of addresses and storage keys that the transaction plans to access
47    ///
48    /// Added in [EIP-2930].
49    ///
50    /// [EIP-2930]: https://eips.ethereum.org/EIPS/eip-2930
51    pub access_list: AccessList,
52
53    /// The priority fee per gas
54    ///
55    /// Incorporated as part of the London upgrade via [EIP-1559].
56    ///
57    /// [EIP-1559]: https://eips.ethereum.org/EIPS/eip-1559
58    pub gas_priority_fee: Option<u128>,
59
60    /// The list of blob versioned hashes
61    ///
62    /// Per EIP there should be at least one blob present if [`max_fee_per_blob_gas`][Self::max_fee_per_blob_gas] is [`Some`].
63    ///
64    /// Incorporated as part of the Cancun upgrade via [EIP-4844].
65    ///
66    /// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844
67    pub blob_hashes: Vec<B256>,
68
69    /// The max fee per blob gas
70    ///
71    /// Incorporated as part of the Cancun upgrade via [EIP-4844].
72    ///
73    /// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844
74    pub max_fee_per_blob_gas: u128,
75
76    /// List of authorizations
77    ///
78    /// `authorization_list` contains the signature that authorizes this
79    /// caller to place the code to signer account.
80    ///
81    /// Set EOA account code for one transaction via [EIP-7702].
82    ///
83    /// [EIP-7702]: https://eips.ethereum.org/EIPS/eip-7702
84    pub authorization_list: Vec<Either<SignedAuthorization, RecoveredAuthorization>>,
85
86    /// List of initcodes that is part of Initcode transaction.
87    ///
88    /// [EIP-7873](https://eips.ethereum.org/EIPS/eip-7873)
89    pub initcodes: Vec<Bytes>,
90}
91
92impl Default for TxEnv {
93    fn default() -> Self {
94        Self {
95            tx_type: 0,
96            caller: Address::default(),
97            gas_limit: 30_000_000,
98            gas_price: 0,
99            kind: TxKind::Call(Address::default()),
100            value: U256::ZERO,
101            data: Bytes::default(),
102            nonce: 0,
103            chain_id: Some(1), // Mainnet chain ID is 1
104            access_list: Default::default(),
105            gas_priority_fee: None,
106            blob_hashes: Vec::new(),
107            max_fee_per_blob_gas: 0,
108            authorization_list: Vec::new(),
109            initcodes: Vec::new(),
110        }
111    }
112}
113
114/// Error type for deriving transaction type used as error in [`TxEnv::derive_tx_type`] function.
115#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
116#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
117pub enum DeriveTxTypeError {
118    /// Missing target for EIP-4844
119    MissingTargetForEip4844,
120    /// Missing target for EIP-7702
121    MissingTargetForEip7702,
122    /// Missing target for EIP-7873
123    MissingTargetForEip7873,
124}
125
126impl TxEnv {
127    /// Derives tx type from transaction fields and sets it to `tx_type`.
128    /// Returns error in case some fields were not set correctly.
129    pub fn derive_tx_type(&mut self) -> Result<(), DeriveTxTypeError> {
130        if !self.access_list.0.is_empty() {
131            self.tx_type = TransactionType::Eip2930 as u8;
132        }
133
134        if self.gas_priority_fee.is_some() {
135            self.tx_type = TransactionType::Eip1559 as u8;
136        }
137
138        if !self.blob_hashes.is_empty() || self.max_fee_per_blob_gas > 0 {
139            if let TxKind::Call(_) = self.kind {
140                self.tx_type = TransactionType::Eip4844 as u8;
141                return Ok(());
142            } else {
143                return Err(DeriveTxTypeError::MissingTargetForEip4844);
144            }
145        }
146
147        if !self.authorization_list.is_empty() {
148            if let TxKind::Call(_) = self.kind {
149                self.tx_type = TransactionType::Eip7702 as u8;
150                return Ok(());
151            } else {
152                return Err(DeriveTxTypeError::MissingTargetForEip7702);
153            }
154        }
155
156        if !self.initcodes.is_empty() {
157            if let TxKind::Call(_) = self.kind {
158                self.tx_type = TransactionType::Eip7873 as u8;
159                return Ok(());
160            } else {
161                return Err(DeriveTxTypeError::MissingTargetForEip7873);
162            }
163        }
164        Ok(())
165    }
166
167    /// Insert a list of signed authorizations into the authorization list.
168    pub fn set_signed_authorization(&mut self, auth: Vec<SignedAuthorization>) {
169        self.authorization_list = auth.into_iter().map(Either::Left).collect();
170    }
171
172    /// Insert a list of recovered authorizations into the authorization list.
173    pub fn set_recovered_authorization(&mut self, auth: Vec<RecoveredAuthorization>) {
174        self.authorization_list = auth.into_iter().map(Either::Right).collect();
175    }
176}
177
178impl Transaction for TxEnv {
179    type AccessListItem<'a> = &'a AccessListItem;
180    type Authorization<'a> = &'a Either<SignedAuthorization, RecoveredAuthorization>;
181
182    fn tx_type(&self) -> u8 {
183        self.tx_type
184    }
185
186    fn kind(&self) -> TxKind {
187        self.kind
188    }
189
190    fn caller(&self) -> Address {
191        self.caller
192    }
193
194    fn gas_limit(&self) -> u64 {
195        self.gas_limit
196    }
197
198    fn gas_price(&self) -> u128 {
199        self.gas_price
200    }
201
202    fn value(&self) -> U256 {
203        self.value
204    }
205
206    fn nonce(&self) -> u64 {
207        self.nonce
208    }
209
210    fn chain_id(&self) -> Option<u64> {
211        self.chain_id
212    }
213
214    fn access_list(&self) -> Option<impl Iterator<Item = Self::AccessListItem<'_>>> {
215        Some(self.access_list.0.iter())
216    }
217
218    fn max_fee_per_gas(&self) -> u128 {
219        self.gas_price
220    }
221
222    fn max_fee_per_blob_gas(&self) -> u128 {
223        self.max_fee_per_blob_gas
224    }
225
226    fn authorization_list_len(&self) -> usize {
227        self.authorization_list.len()
228    }
229
230    fn authorization_list(&self) -> impl Iterator<Item = Self::Authorization<'_>> {
231        self.authorization_list.iter()
232    }
233
234    fn input(&self) -> &Bytes {
235        &self.data
236    }
237
238    fn blob_versioned_hashes(&self) -> &[B256] {
239        &self.blob_hashes
240    }
241
242    fn max_priority_fee_per_gas(&self) -> Option<u128> {
243        self.gas_priority_fee
244    }
245
246    fn initcodes(&self) -> &[Bytes] {
247        &self.initcodes
248    }
249}
250
251#[cfg(test)]
252mod tests {
253    use super::*;
254
255    fn effective_gas_setup(
256        tx_type: TransactionType,
257        gas_price: u128,
258        gas_priority_fee: Option<u128>,
259    ) -> u128 {
260        let tx = TxEnv {
261            tx_type: tx_type as u8,
262            gas_price,
263            gas_priority_fee,
264            ..Default::default()
265        };
266        let base_fee = 100;
267        tx.effective_gas_price(base_fee)
268    }
269
270    #[test]
271    fn test_effective_gas_price() {
272        assert_eq!(90, effective_gas_setup(TransactionType::Legacy, 90, None));
273        assert_eq!(
274            90,
275            effective_gas_setup(TransactionType::Legacy, 90, Some(0))
276        );
277        assert_eq!(
278            90,
279            effective_gas_setup(TransactionType::Legacy, 90, Some(10))
280        );
281        assert_eq!(
282            120,
283            effective_gas_setup(TransactionType::Legacy, 120, Some(10))
284        );
285        assert_eq!(90, effective_gas_setup(TransactionType::Eip2930, 90, None));
286        assert_eq!(
287            90,
288            effective_gas_setup(TransactionType::Eip2930, 90, Some(0))
289        );
290        assert_eq!(
291            90,
292            effective_gas_setup(TransactionType::Eip2930, 90, Some(10))
293        );
294        assert_eq!(
295            120,
296            effective_gas_setup(TransactionType::Eip2930, 120, Some(10))
297        );
298        assert_eq!(90, effective_gas_setup(TransactionType::Eip1559, 90, None));
299        assert_eq!(
300            90,
301            effective_gas_setup(TransactionType::Eip1559, 90, Some(0))
302        );
303        assert_eq!(
304            90,
305            effective_gas_setup(TransactionType::Eip1559, 90, Some(10))
306        );
307        assert_eq!(
308            110,
309            effective_gas_setup(TransactionType::Eip1559, 120, Some(10))
310        );
311        assert_eq!(90, effective_gas_setup(TransactionType::Eip4844, 90, None));
312        assert_eq!(
313            90,
314            effective_gas_setup(TransactionType::Eip4844, 90, Some(0))
315        );
316        assert_eq!(
317            90,
318            effective_gas_setup(TransactionType::Eip4844, 90, Some(10))
319        );
320        assert_eq!(
321            110,
322            effective_gas_setup(TransactionType::Eip4844, 120, Some(10))
323        );
324        assert_eq!(90, effective_gas_setup(TransactionType::Eip7702, 90, None));
325        assert_eq!(
326            90,
327            effective_gas_setup(TransactionType::Eip7702, 90, Some(0))
328        );
329        assert_eq!(
330            90,
331            effective_gas_setup(TransactionType::Eip7702, 90, Some(10))
332        );
333        assert_eq!(
334            110,
335            effective_gas_setup(TransactionType::Eip7702, 120, Some(10))
336        );
337    }
338}