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    // TODO(EOF)
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            // TODO(EOF)
110            //initcodes: Vec::new(),
111        }
112    }
113}
114
115/// Error type for deriving transaction type used as error in [`TxEnv::derive_tx_type`] function.
116#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
117#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
118pub enum DeriveTxTypeError {
119    /// Missing target for EIP-4844
120    MissingTargetForEip4844,
121    /// Missing target for EIP-7702
122    MissingTargetForEip7702,
123    /// Missing target for EIP-7873
124    MissingTargetForEip7873,
125}
126
127impl TxEnv {
128    /// Derives tx type from transaction fields and sets it to `tx_type`.
129    /// Returns error in case some fields were not set correctly.
130    pub fn derive_tx_type(&mut self) -> Result<(), DeriveTxTypeError> {
131        if !self.access_list.0.is_empty() {
132            self.tx_type = TransactionType::Eip2930 as u8;
133        }
134
135        if self.gas_priority_fee.is_some() {
136            self.tx_type = TransactionType::Eip1559 as u8;
137        }
138
139        if !self.blob_hashes.is_empty() || self.max_fee_per_blob_gas > 0 {
140            if let TxKind::Call(_) = self.kind {
141                self.tx_type = TransactionType::Eip4844 as u8;
142                return Ok(());
143            } else {
144                return Err(DeriveTxTypeError::MissingTargetForEip4844);
145            }
146        }
147
148        if !self.authorization_list.is_empty() {
149            if let TxKind::Call(_) = self.kind {
150                self.tx_type = TransactionType::Eip7702 as u8;
151                return Ok(());
152            } else {
153                return Err(DeriveTxTypeError::MissingTargetForEip7702);
154            }
155        }
156
157        // TODO(EOF)
158        // if !self.initcodes.is_empty() {
159        //     if let TxKind::Call(_) = self.kind {
160        //         self.tx_type = TransactionType::Eip7873 as u8;
161        //         return Ok(());
162        //     } else {
163        //         return Err(DeriveTxTypeError::MissingTargetForEip7873);
164        //     }
165        // }
166
167        Ok(())
168    }
169
170    /// Insert a list of signed authorizations into the authorization list.
171    pub fn set_signed_authorization(&mut self, auth: Vec<SignedAuthorization>) {
172        self.authorization_list = auth.into_iter().map(Either::Left).collect();
173    }
174
175    /// Insert a list of recovered authorizations into the authorization list.
176    pub fn set_recovered_authorization(&mut self, auth: Vec<RecoveredAuthorization>) {
177        self.authorization_list = auth.into_iter().map(Either::Right).collect();
178    }
179}
180
181impl Transaction for TxEnv {
182    type AccessListItem<'a> = &'a AccessListItem;
183    type Authorization<'a> = &'a Either<SignedAuthorization, RecoveredAuthorization>;
184
185    fn tx_type(&self) -> u8 {
186        self.tx_type
187    }
188
189    fn kind(&self) -> TxKind {
190        self.kind
191    }
192
193    fn caller(&self) -> Address {
194        self.caller
195    }
196
197    fn gas_limit(&self) -> u64 {
198        self.gas_limit
199    }
200
201    fn gas_price(&self) -> u128 {
202        self.gas_price
203    }
204
205    fn value(&self) -> U256 {
206        self.value
207    }
208
209    fn nonce(&self) -> u64 {
210        self.nonce
211    }
212
213    fn chain_id(&self) -> Option<u64> {
214        self.chain_id
215    }
216
217    fn access_list(&self) -> Option<impl Iterator<Item = Self::AccessListItem<'_>>> {
218        Some(self.access_list.0.iter())
219    }
220
221    fn max_fee_per_gas(&self) -> u128 {
222        self.gas_price
223    }
224
225    fn max_fee_per_blob_gas(&self) -> u128 {
226        self.max_fee_per_blob_gas
227    }
228
229    fn authorization_list_len(&self) -> usize {
230        self.authorization_list.len()
231    }
232
233    fn authorization_list(&self) -> impl Iterator<Item = Self::Authorization<'_>> {
234        self.authorization_list.iter()
235    }
236
237    fn input(&self) -> &Bytes {
238        &self.data
239    }
240
241    fn blob_versioned_hashes(&self) -> &[B256] {
242        &self.blob_hashes
243    }
244
245    fn max_priority_fee_per_gas(&self) -> Option<u128> {
246        self.gas_priority_fee
247    }
248
249    // TODO(EOF)
250    // fn initcodes(&self) -> &[Bytes] {
251    //     &self.initcodes
252    // }
253}
254
255#[cfg(test)]
256mod tests {
257    use super::*;
258
259    fn effective_gas_setup(
260        tx_type: TransactionType,
261        gas_price: u128,
262        gas_priority_fee: Option<u128>,
263    ) -> u128 {
264        let tx = TxEnv {
265            tx_type: tx_type as u8,
266            gas_price,
267            gas_priority_fee,
268            ..Default::default()
269        };
270        let base_fee = 100;
271        tx.effective_gas_price(base_fee)
272    }
273
274    #[test]
275    fn test_effective_gas_price() {
276        assert_eq!(90, effective_gas_setup(TransactionType::Legacy, 90, None));
277        assert_eq!(
278            90,
279            effective_gas_setup(TransactionType::Legacy, 90, Some(0))
280        );
281        assert_eq!(
282            90,
283            effective_gas_setup(TransactionType::Legacy, 90, Some(10))
284        );
285        assert_eq!(
286            120,
287            effective_gas_setup(TransactionType::Legacy, 120, Some(10))
288        );
289        assert_eq!(90, effective_gas_setup(TransactionType::Eip2930, 90, None));
290        assert_eq!(
291            90,
292            effective_gas_setup(TransactionType::Eip2930, 90, Some(0))
293        );
294        assert_eq!(
295            90,
296            effective_gas_setup(TransactionType::Eip2930, 90, Some(10))
297        );
298        assert_eq!(
299            120,
300            effective_gas_setup(TransactionType::Eip2930, 120, Some(10))
301        );
302        assert_eq!(90, effective_gas_setup(TransactionType::Eip1559, 90, None));
303        assert_eq!(
304            90,
305            effective_gas_setup(TransactionType::Eip1559, 90, Some(0))
306        );
307        assert_eq!(
308            90,
309            effective_gas_setup(TransactionType::Eip1559, 90, Some(10))
310        );
311        assert_eq!(
312            110,
313            effective_gas_setup(TransactionType::Eip1559, 120, Some(10))
314        );
315        assert_eq!(90, effective_gas_setup(TransactionType::Eip4844, 90, None));
316        assert_eq!(
317            90,
318            effective_gas_setup(TransactionType::Eip4844, 90, Some(0))
319        );
320        assert_eq!(
321            90,
322            effective_gas_setup(TransactionType::Eip4844, 90, Some(10))
323        );
324        assert_eq!(
325            110,
326            effective_gas_setup(TransactionType::Eip4844, 120, Some(10))
327        );
328        assert_eq!(90, effective_gas_setup(TransactionType::Eip7702, 90, None));
329        assert_eq!(
330            90,
331            effective_gas_setup(TransactionType::Eip7702, 90, Some(0))
332        );
333        assert_eq!(
334            90,
335            effective_gas_setup(TransactionType::Eip7702, 90, Some(10))
336        );
337        assert_eq!(
338            110,
339            effective_gas_setup(TransactionType::Eip7702, 120, Some(10))
340        );
341    }
342}