op_revm/transaction/
abstraction.rs

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