op_revm/transaction/
abstraction.rs

1use super::deposit::{DepositTransactionParts, DEPOSIT_TRANSACTION_TYPE};
2use auto_impl::auto_impl;
3use revm::{
4    context::TxEnv,
5    context_interface::transaction::Transaction,
6    handler::SystemCallTx,
7    primitives::{Address, Bytes, TxKind, B256, U256},
8};
9use std::vec;
10
11#[auto_impl(&, &mut, Box, Arc)]
12pub trait OpTxTr: Transaction {
13    fn enveloped_tx(&self) -> Option<&Bytes>;
14
15    /// Source hash of the deposit transaction
16    fn source_hash(&self) -> Option<B256>;
17
18    /// Mint of the deposit transaction
19    fn mint(&self) -> Option<u128>;
20
21    /// Whether the transaction is a system transaction
22    fn is_system_transaction(&self) -> bool;
23
24    /// Returns `true` if transaction is of type [`DEPOSIT_TRANSACTION_TYPE`].
25    fn is_deposit(&self) -> bool {
26        self.tx_type() == DEPOSIT_TRANSACTION_TYPE
27    }
28}
29
30#[derive(Clone, Debug, PartialEq, Eq)]
31#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
32pub struct OpTransaction<T: Transaction> {
33    pub base: T,
34    /// An enveloped EIP-2718 typed transaction
35    ///
36    /// This is used to compute the L1 tx cost using the L1 block info, as
37    /// opposed to requiring downstream apps to compute the cost
38    /// externally.
39    pub enveloped_tx: Option<Bytes>,
40    pub deposit: DepositTransactionParts,
41}
42
43impl<T: Transaction> OpTransaction<T> {
44    pub fn new(base: T) -> Self {
45        Self {
46            base,
47            enveloped_tx: None,
48            deposit: DepositTransactionParts::default(),
49        }
50    }
51}
52
53impl Default for OpTransaction<TxEnv> {
54    fn default() -> Self {
55        Self {
56            base: TxEnv::default(),
57            enveloped_tx: Some(vec![0x00].into()),
58            deposit: DepositTransactionParts::default(),
59        }
60    }
61}
62
63impl<TX: Transaction + SystemCallTx> SystemCallTx for OpTransaction<TX> {
64    fn new_system_tx(data: Bytes, system_contract_address: Address) -> Self {
65        OpTransaction::new(TX::new_system_tx(data, system_contract_address))
66    }
67}
68
69impl<T: Transaction> Transaction for OpTransaction<T> {
70    type AccessListItem<'a>
71        = T::AccessListItem<'a>
72    where
73        T: 'a;
74    type Authorization<'a>
75        = T::Authorization<'a>
76    where
77        T: 'a;
78
79    fn tx_type(&self) -> u8 {
80        self.base.tx_type()
81    }
82
83    fn caller(&self) -> Address {
84        self.base.caller()
85    }
86
87    fn gas_limit(&self) -> u64 {
88        self.base.gas_limit()
89    }
90
91    fn value(&self) -> U256 {
92        self.base.value()
93    }
94
95    fn input(&self) -> &Bytes {
96        self.base.input()
97    }
98
99    fn nonce(&self) -> u64 {
100        self.base.nonce()
101    }
102
103    fn kind(&self) -> TxKind {
104        self.base.kind()
105    }
106
107    fn chain_id(&self) -> Option<u64> {
108        self.base.chain_id()
109    }
110
111    fn access_list(&self) -> Option<impl Iterator<Item = Self::AccessListItem<'_>>> {
112        self.base.access_list()
113    }
114
115    fn max_priority_fee_per_gas(&self) -> Option<u128> {
116        self.base.max_priority_fee_per_gas()
117    }
118
119    fn max_fee_per_gas(&self) -> u128 {
120        self.base.max_fee_per_gas()
121    }
122
123    fn gas_price(&self) -> u128 {
124        self.base.gas_price()
125    }
126
127    fn blob_versioned_hashes(&self) -> &[B256] {
128        self.base.blob_versioned_hashes()
129    }
130
131    fn max_fee_per_blob_gas(&self) -> u128 {
132        self.base.max_fee_per_blob_gas()
133    }
134
135    fn effective_gas_price(&self, base_fee: u128) -> u128 {
136        self.base.effective_gas_price(base_fee)
137    }
138
139    fn authorization_list_len(&self) -> usize {
140        self.base.authorization_list_len()
141    }
142
143    fn authorization_list(&self) -> impl Iterator<Item = Self::Authorization<'_>> {
144        self.base.authorization_list()
145    }
146
147    fn initcodes(&self) -> &[Bytes] {
148        self.base.initcodes()
149    }
150}
151
152impl<T: Transaction> OpTxTr for OpTransaction<T> {
153    fn enveloped_tx(&self) -> Option<&Bytes> {
154        self.enveloped_tx.as_ref()
155    }
156
157    fn source_hash(&self) -> Option<B256> {
158        if self.tx_type() != DEPOSIT_TRANSACTION_TYPE {
159            return None;
160        }
161        Some(self.deposit.source_hash)
162    }
163
164    fn mint(&self) -> Option<u128> {
165        self.deposit.mint
166    }
167
168    fn is_system_transaction(&self) -> bool {
169        self.deposit.is_system_transaction
170    }
171}
172
173#[cfg(test)]
174mod tests {
175    use crate::transaction::deposit::DEPOSIT_TRANSACTION_TYPE;
176
177    use super::*;
178    use revm::primitives::{Address, B256};
179
180    #[test]
181    fn test_deposit_transaction_fields() {
182        let op_tx = OpTransaction {
183            base: TxEnv {
184                tx_type: DEPOSIT_TRANSACTION_TYPE,
185                gas_limit: 10,
186                gas_price: 100,
187                gas_priority_fee: Some(5),
188                ..Default::default()
189            },
190            enveloped_tx: None,
191            deposit: DepositTransactionParts {
192                is_system_transaction: false,
193                mint: Some(0u128),
194                source_hash: B256::default(),
195            },
196        };
197        // Verify transaction type
198        assert_eq!(op_tx.tx_type(), DEPOSIT_TRANSACTION_TYPE);
199        // Verify common fields access
200        assert_eq!(op_tx.gas_limit(), 10);
201        assert_eq!(op_tx.kind(), revm::primitives::TxKind::Call(Address::ZERO));
202        // Verify gas related calculations
203        assert_eq!(op_tx.effective_gas_price(90), 95);
204        assert_eq!(op_tx.max_fee_per_gas(), 100);
205    }
206}