op_revm/transaction/
abstraction.rs1use super::deposit::{DepositTransactionParts, DEPOSIT_TRANSACTION_TYPE};
3use auto_impl::auto_impl;
4use revm::{
5 context::{
6 tx::{TxEnvBuildError, TxEnvBuilder},
7 TxEnv,
8 },
9 context_interface::transaction::Transaction,
10 handler::SystemCallTx,
11 primitives::{Address, Bytes, TxKind, B256, U256},
12};
13use std::vec;
14
15#[auto_impl(&, &mut, Box, Arc)]
17pub trait OpTxTr: Transaction {
18 fn enveloped_tx(&self) -> Option<&Bytes>;
20
21 fn source_hash(&self) -> Option<B256>;
23
24 fn mint(&self) -> Option<u128>;
26
27 fn is_system_transaction(&self) -> bool;
29
30 fn is_deposit(&self) -> bool {
32 self.tx_type() == DEPOSIT_TRANSACTION_TYPE
33 }
34}
35
36#[derive(Clone, Debug, PartialEq, Eq)]
38#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
39pub struct OpTransaction<T: Transaction> {
40 pub base: T,
42 pub enveloped_tx: Option<Bytes>,
48 pub deposit: DepositTransactionParts,
50}
51
52impl<T: Transaction> AsRef<T> for OpTransaction<T> {
53 fn as_ref(&self) -> &T {
54 &self.base
55 }
56}
57
58impl<T: Transaction> OpTransaction<T> {
59 pub fn new(base: T) -> Self {
61 Self {
62 base,
63 enveloped_tx: None,
64 deposit: DepositTransactionParts::default(),
65 }
66 }
67}
68
69impl OpTransaction<TxEnv> {
70 pub fn builder() -> OpTransactionBuilder {
72 OpTransactionBuilder::new()
73 }
74}
75
76impl Default for OpTransaction<TxEnv> {
77 fn default() -> Self {
78 Self {
79 base: TxEnv::default(),
80 enveloped_tx: Some(vec![0x00].into()),
81 deposit: DepositTransactionParts::default(),
82 }
83 }
84}
85
86impl<TX: Transaction + SystemCallTx> SystemCallTx for OpTransaction<TX> {
87 fn new_system_tx_with_caller(
88 caller: Address,
89 system_contract_address: Address,
90 data: Bytes,
91 ) -> Self {
92 let mut tx = OpTransaction::new(TX::new_system_tx_with_caller(
93 caller,
94 system_contract_address,
95 data,
96 ));
97
98 tx.enveloped_tx = Some(Bytes::default());
99
100 tx
101 }
102}
103
104impl<T: Transaction> Transaction for OpTransaction<T> {
105 type AccessListItem<'a>
106 = T::AccessListItem<'a>
107 where
108 T: 'a;
109 type Authorization<'a>
110 = T::Authorization<'a>
111 where
112 T: 'a;
113
114 fn tx_type(&self) -> u8 {
115 if self.deposit.source_hash != B256::ZERO {
117 DEPOSIT_TRANSACTION_TYPE
118 } else {
119 self.base.tx_type()
120 }
121 }
122
123 fn caller(&self) -> Address {
124 self.base.caller()
125 }
126
127 fn gas_limit(&self) -> u64 {
128 self.base.gas_limit()
129 }
130
131 fn value(&self) -> U256 {
132 self.base.value()
133 }
134
135 fn input(&self) -> &Bytes {
136 self.base.input()
137 }
138
139 fn nonce(&self) -> u64 {
140 self.base.nonce()
141 }
142
143 fn kind(&self) -> TxKind {
144 self.base.kind()
145 }
146
147 fn chain_id(&self) -> Option<u64> {
148 self.base.chain_id()
149 }
150
151 fn access_list(&self) -> Option<impl Iterator<Item = Self::AccessListItem<'_>>> {
152 self.base.access_list()
153 }
154
155 fn max_priority_fee_per_gas(&self) -> Option<u128> {
156 self.base.max_priority_fee_per_gas()
157 }
158
159 fn max_fee_per_gas(&self) -> u128 {
160 self.base.max_fee_per_gas()
161 }
162
163 fn gas_price(&self) -> u128 {
164 self.base.gas_price()
165 }
166
167 fn blob_versioned_hashes(&self) -> &[B256] {
168 self.base.blob_versioned_hashes()
169 }
170
171 fn max_fee_per_blob_gas(&self) -> u128 {
172 self.base.max_fee_per_blob_gas()
173 }
174
175 fn effective_gas_price(&self, base_fee: u128) -> u128 {
176 if self.tx_type() == DEPOSIT_TRANSACTION_TYPE {
178 return self.gas_price();
179 }
180 self.base.effective_gas_price(base_fee)
181 }
182
183 fn authorization_list_len(&self) -> usize {
184 self.base.authorization_list_len()
185 }
186
187 fn authorization_list(&self) -> impl Iterator<Item = Self::Authorization<'_>> {
188 self.base.authorization_list()
189 }
190}
191
192impl<T: Transaction> OpTxTr for OpTransaction<T> {
193 fn enveloped_tx(&self) -> Option<&Bytes> {
194 self.enveloped_tx.as_ref()
195 }
196
197 fn source_hash(&self) -> Option<B256> {
198 if self.tx_type() != DEPOSIT_TRANSACTION_TYPE {
199 return None;
200 }
201 Some(self.deposit.source_hash)
202 }
203
204 fn mint(&self) -> Option<u128> {
205 self.deposit.mint
206 }
207
208 fn is_system_transaction(&self) -> bool {
209 self.deposit.is_system_transaction
210 }
211}
212
213#[derive(Default, Debug)]
215pub struct OpTransactionBuilder {
216 base: TxEnvBuilder,
217 enveloped_tx: Option<Bytes>,
218 deposit: DepositTransactionParts,
219}
220
221impl OpTransactionBuilder {
222 pub fn new() -> Self {
224 Self {
225 base: TxEnvBuilder::new(),
226 enveloped_tx: None,
227 deposit: DepositTransactionParts::default(),
228 }
229 }
230
231 pub fn base(mut self, base: TxEnvBuilder) -> Self {
233 self.base = base;
234 self
235 }
236
237 pub fn enveloped_tx(mut self, enveloped_tx: Option<Bytes>) -> Self {
239 self.enveloped_tx = enveloped_tx;
240 self
241 }
242
243 pub fn source_hash(mut self, source_hash: B256) -> Self {
245 self.deposit.source_hash = source_hash;
246 self
247 }
248
249 pub fn mint(mut self, mint: u128) -> Self {
251 self.deposit.mint = Some(mint);
252 self
253 }
254
255 pub fn is_system_transaction(mut self) -> Self {
257 self.deposit.is_system_transaction = true;
258 self
259 }
260
261 pub fn not_system_transaction(mut self) -> Self {
263 self.deposit.is_system_transaction = false;
264 self
265 }
266
267 pub fn is_deposit_tx(mut self) -> Self {
269 self.base = self.base.tx_type(Some(DEPOSIT_TRANSACTION_TYPE));
270 self
271 }
272
273 pub fn build_fill(mut self) -> OpTransaction<TxEnv> {
283 let tx_type = self.base.get_tx_type();
284 if tx_type.is_some() {
285 if tx_type == Some(DEPOSIT_TRANSACTION_TYPE) {
286 if self.deposit.source_hash == B256::ZERO {
288 self.deposit.source_hash = B256::from([1u8; 32]);
289 }
290 self.enveloped_tx = None;
292 } else {
293 self.enveloped_tx = Some(vec![0x00].into());
295 }
296 } else if self.deposit.source_hash != B256::ZERO {
297 self.base = self.base.tx_type(Some(DEPOSIT_TRANSACTION_TYPE));
299 self.enveloped_tx = None;
301 } else if self.enveloped_tx.is_none() {
302 self.enveloped_tx = Some(vec![0x00].into());
304 }
305
306 let base = self.base.build_fill();
307
308 OpTransaction {
309 base,
310 enveloped_tx: self.enveloped_tx,
311 deposit: self.deposit,
312 }
313 }
314
315 pub fn build(mut self) -> Result<OpTransaction<TxEnv>, OpBuildError> {
318 let tx_type = self.base.get_tx_type();
319 if tx_type.is_some() {
320 if Some(DEPOSIT_TRANSACTION_TYPE) == tx_type {
321 if self.deposit.source_hash == B256::ZERO {
323 return Err(OpBuildError::MissingSourceHashForDeposit);
324 }
325 } else if self.enveloped_tx.is_none() {
326 return Err(OpBuildError::MissingEnvelopedTxBytes);
328 }
329 } else if self.deposit.source_hash != B256::ZERO {
330 self.base = self.base.tx_type(Some(DEPOSIT_TRANSACTION_TYPE));
332 } else if self.enveloped_tx.is_none() {
333 return Err(OpBuildError::MissingEnvelopedTxBytes);
335 }
336
337 let base = self.base.build()?;
338
339 Ok(OpTransaction {
340 base,
341 enveloped_tx: self.enveloped_tx,
342 deposit: self.deposit,
343 })
344 }
345}
346
347#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
349#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
350pub enum OpBuildError {
351 Base(TxEnvBuildError),
353 MissingEnvelopedTxBytes,
355 MissingSourceHashForDeposit,
357}
358
359impl From<TxEnvBuildError> for OpBuildError {
360 fn from(error: TxEnvBuildError) -> Self {
361 OpBuildError::Base(error)
362 }
363}
364
365#[cfg(test)]
366mod tests {
367 use super::*;
368 use revm::{
369 context_interface::Transaction,
370 primitives::{Address, B256},
371 };
372
373 #[test]
374 fn test_deposit_transaction_fields() {
375 let base_tx = TxEnv::builder()
376 .gas_limit(10)
377 .gas_price(100)
378 .gas_priority_fee(Some(5));
379
380 let op_tx = OpTransaction::builder()
381 .base(base_tx)
382 .enveloped_tx(None)
383 .not_system_transaction()
384 .mint(0u128)
385 .source_hash(B256::from([1u8; 32]))
386 .build()
387 .unwrap();
388 assert_eq!(op_tx.gas_limit(), 10);
392 assert_eq!(op_tx.kind(), revm::primitives::TxKind::Call(Address::ZERO));
393 assert_eq!(op_tx.effective_gas_price(90), 100);
395 assert_eq!(op_tx.max_fee_per_gas(), 100);
396 }
397}