Skip to main content

foundry_evm_core/env/
transaction.rs

1//! Mutable transaction environments and execution-family adapters.
2
3use alloy_primitives::{Address, B256, Bytes, U256};
4use revm::{
5    context::{Transaction, TxEnv},
6    context_interface::{
7        either::Either,
8        transaction::{AccessList, RecoveredAuthorization, SignedAuthorization},
9    },
10    primitives::TxKind,
11};
12use tempo_revm::TempoTxEnv;
13
14#[cfg(feature = "optimism")]
15use op_revm::transaction::deposit::DEPOSIT_TRANSACTION_TYPE;
16
17/// Extension of [`Transaction`] with mutable setters, allowing EVM-agnostic mutation of transaction
18/// fields.
19pub trait FoundryTransaction: Transaction {
20    /// Sets the transaction type.
21    fn set_tx_type(&mut self, tx_type: u8);
22
23    /// Sets the caller (sender) address.
24    fn set_caller(&mut self, caller: Address);
25
26    /// Sets the gas limit.
27    fn set_gas_limit(&mut self, gas_limit: u64);
28
29    /// Sets the gas price (or max fee per gas for EIP-1559).
30    fn set_gas_price(&mut self, gas_price: u128);
31
32    /// Sets the transaction kind (call or create).
33    fn set_kind(&mut self, kind: TxKind);
34
35    /// Sets the value sent with the transaction.
36    fn set_value(&mut self, value: U256);
37
38    /// Sets the transaction input data.
39    fn set_data(&mut self, data: Bytes);
40
41    /// Sets the nonce.
42    fn set_nonce(&mut self, nonce: u64);
43
44    /// Sets the chain ID.
45    fn set_chain_id(&mut self, chain_id: Option<u64>);
46
47    /// Sets the access list.
48    fn set_access_list(&mut self, access_list: AccessList);
49
50    /// Returns a mutable reference to the EIP-7702 authorization list.
51    fn authorization_list_mut(
52        &mut self,
53    ) -> &mut Vec<Either<SignedAuthorization, RecoveredAuthorization>>;
54
55    /// Sets the max priority fee per gas.
56    fn set_gas_priority_fee(&mut self, gas_priority_fee: Option<u128>);
57
58    /// Sets the blob versioned hashes.
59    fn set_blob_hashes(&mut self, blob_hashes: Vec<B256>);
60
61    /// Sets the max fee per blob gas.
62    fn set_max_fee_per_blob_gas(&mut self, max_fee_per_blob_gas: u128);
63
64    /// Sets the EIP-7702 signed authorization list.
65    fn set_signed_authorization(&mut self, auth: Vec<SignedAuthorization>) {
66        *self.authorization_list_mut() = auth.into_iter().map(Either::Left).collect();
67    }
68
69    // `OpTransaction` methods
70
71    /// Enveloped transaction bytes.
72    fn enveloped_tx(&self) -> Option<&Bytes> {
73        None
74    }
75
76    /// Set Enveloped transaction bytes.
77    fn set_enveloped_tx(&mut self, _bytes: Bytes) {}
78
79    /// Source hash of the deposit transaction.
80    fn source_hash(&self) -> Option<B256> {
81        None
82    }
83
84    /// Sets source hash of the deposit transaction.
85    fn set_source_hash(&mut self, _source_hash: B256) {}
86
87    /// Mint of the deposit transaction
88    fn mint(&self) -> Option<u128> {
89        None
90    }
91
92    /// Sets mint of the deposit transaction.
93    fn set_mint(&mut self, _mint: u128) {}
94
95    /// Whether the transaction is a system transaction
96    fn is_system_transaction(&self) -> bool {
97        false
98    }
99
100    /// Sets whether the transaction is a system transaction
101    fn set_system_transaction(&mut self, _is_system_transaction: bool) {}
102
103    /// Returns `true` if transaction is an Optimism deposit transaction.
104    fn is_deposit(&self) -> bool {
105        #[cfg(feature = "optimism")]
106        {
107            self.tx_type() == DEPOSIT_TRANSACTION_TYPE
108        }
109        #[cfg(not(feature = "optimism"))]
110        {
111            false
112        }
113    }
114
115    // Tempo methods
116
117    /// Returns the fee token address for this transaction.
118    fn fee_token(&self) -> Option<Address> {
119        None
120    }
121
122    /// Sets the fee token address for this transaction.
123    fn set_fee_token(&mut self, _token: Option<Address>) {}
124
125    /// Returns the fee payer for this transaction.
126    fn fee_payer(&self) -> Option<Option<Address>> {
127        None
128    }
129
130    /// Sets the fee payer for this transaction.
131    fn set_fee_payer(&mut self, _payer: Option<Option<Address>>) {}
132
133    /// Returns whether this transaction executes a single contract creation.
134    fn is_single_create(&self) -> bool {
135        self.kind().is_create()
136    }
137}
138
139impl FoundryTransaction for TxEnv {
140    fn set_tx_type(&mut self, tx_type: u8) {
141        self.tx_type = tx_type;
142    }
143
144    fn set_caller(&mut self, caller: Address) {
145        self.caller = caller;
146    }
147
148    fn set_gas_limit(&mut self, gas_limit: u64) {
149        self.gas_limit = gas_limit;
150    }
151
152    fn set_gas_price(&mut self, gas_price: u128) {
153        self.gas_price = gas_price;
154    }
155
156    fn set_kind(&mut self, kind: TxKind) {
157        self.kind = kind;
158    }
159
160    fn set_value(&mut self, value: U256) {
161        self.value = value;
162    }
163
164    fn set_data(&mut self, data: Bytes) {
165        self.data = data;
166    }
167
168    fn set_nonce(&mut self, nonce: u64) {
169        self.nonce = nonce;
170    }
171
172    fn set_chain_id(&mut self, chain_id: Option<u64>) {
173        self.chain_id = chain_id;
174    }
175
176    fn set_access_list(&mut self, access_list: AccessList) {
177        self.access_list = access_list;
178    }
179
180    fn authorization_list_mut(
181        &mut self,
182    ) -> &mut Vec<Either<SignedAuthorization, RecoveredAuthorization>> {
183        &mut self.authorization_list
184    }
185
186    fn set_gas_priority_fee(&mut self, gas_priority_fee: Option<u128>) {
187        self.gas_priority_fee = gas_priority_fee;
188    }
189
190    fn set_blob_hashes(&mut self, blob_hashes: Vec<B256>) {
191        self.blob_hashes = blob_hashes;
192    }
193
194    fn set_max_fee_per_blob_gas(&mut self, max_fee_per_blob_gas: u128) {
195        self.max_fee_per_blob_gas = max_fee_per_blob_gas;
196    }
197}
198
199impl FoundryTransaction for TempoTxEnv {
200    fn set_tx_type(&mut self, tx_type: u8) {
201        self.inner.set_tx_type(tx_type);
202    }
203
204    fn set_caller(&mut self, caller: Address) {
205        self.inner.set_caller(caller);
206    }
207
208    fn set_gas_limit(&mut self, gas_limit: u64) {
209        self.inner.set_gas_limit(gas_limit);
210    }
211
212    fn set_gas_price(&mut self, gas_price: u128) {
213        self.inner.set_gas_price(gas_price);
214    }
215
216    fn set_kind(&mut self, kind: TxKind) {
217        self.inner.set_kind(kind);
218        if let Some(call) =
219            self.tempo_tx_env.as_deref_mut().and_then(|env| env.aa_calls.first_mut())
220        {
221            call.to = kind;
222        }
223    }
224
225    fn set_value(&mut self, value: U256) {
226        self.inner.set_value(value);
227        if let Some(call) =
228            self.tempo_tx_env.as_deref_mut().and_then(|env| env.aa_calls.first_mut())
229        {
230            call.value = value;
231        }
232    }
233
234    fn set_data(&mut self, data: Bytes) {
235        self.inner.set_data(data.clone());
236        if let Some(call) =
237            self.tempo_tx_env.as_deref_mut().and_then(|env| env.aa_calls.first_mut())
238        {
239            call.input = data;
240        }
241    }
242
243    fn set_nonce(&mut self, nonce: u64) {
244        self.inner.set_nonce(nonce);
245    }
246
247    fn set_chain_id(&mut self, chain_id: Option<u64>) {
248        self.inner.set_chain_id(chain_id);
249    }
250
251    fn set_access_list(&mut self, access_list: AccessList) {
252        self.inner.set_access_list(access_list);
253    }
254
255    fn authorization_list_mut(
256        &mut self,
257    ) -> &mut Vec<Either<SignedAuthorization, RecoveredAuthorization>> {
258        self.inner.authorization_list_mut()
259    }
260
261    fn set_gas_priority_fee(&mut self, gas_priority_fee: Option<u128>) {
262        self.inner.set_gas_priority_fee(gas_priority_fee);
263    }
264
265    fn set_blob_hashes(&mut self, _blob_hashes: Vec<B256>) {}
266
267    fn set_max_fee_per_blob_gas(&mut self, _max_fee_per_blob_gas: u128) {}
268
269    fn fee_token(&self) -> Option<Address> {
270        self.fee_token
271    }
272
273    fn set_fee_token(&mut self, token: Option<Address>) {
274        self.fee_token = token;
275    }
276
277    fn fee_payer(&self) -> Option<Option<Address>> {
278        self.fee_payer
279    }
280
281    fn set_fee_payer(&mut self, payer: Option<Option<Address>>) {
282        self.fee_payer = payer;
283    }
284
285    fn is_single_create(&self) -> bool {
286        self.kind().is_create()
287            && self.tempo_tx_env.as_ref().is_none_or(|env| env.aa_calls.len() == 1)
288    }
289}
290
291#[cfg(feature = "base")]
292mod base {
293    use super::*;
294    use base_common_evm::{BaseTransaction, BaseTxTr, DEPOSIT_TRANSACTION_TYPE};
295
296    impl<TX: FoundryTransaction> FoundryTransaction for BaseTransaction<TX> {
297        fn set_tx_type(&mut self, tx_type: u8) {
298            self.base.set_tx_type(tx_type);
299        }
300
301        fn set_caller(&mut self, caller: Address) {
302            self.base.set_caller(caller);
303        }
304
305        fn set_gas_limit(&mut self, gas_limit: u64) {
306            self.base.set_gas_limit(gas_limit);
307        }
308
309        fn set_gas_price(&mut self, gas_price: u128) {
310            self.base.set_gas_price(gas_price);
311        }
312
313        fn set_kind(&mut self, kind: TxKind) {
314            self.base.set_kind(kind);
315        }
316
317        fn set_value(&mut self, value: U256) {
318            self.base.set_value(value);
319        }
320
321        fn set_data(&mut self, data: Bytes) {
322            self.base.set_data(data);
323        }
324
325        fn set_nonce(&mut self, nonce: u64) {
326            self.base.set_nonce(nonce);
327        }
328
329        fn set_chain_id(&mut self, chain_id: Option<u64>) {
330            self.base.set_chain_id(chain_id);
331        }
332
333        fn set_access_list(&mut self, access_list: AccessList) {
334            self.base.set_access_list(access_list);
335        }
336
337        fn authorization_list_mut(
338            &mut self,
339        ) -> &mut Vec<Either<SignedAuthorization, RecoveredAuthorization>> {
340            self.base.authorization_list_mut()
341        }
342
343        fn set_gas_priority_fee(&mut self, gas_priority_fee: Option<u128>) {
344            self.base.set_gas_priority_fee(gas_priority_fee);
345        }
346
347        fn set_blob_hashes(&mut self, blob_hashes: Vec<B256>) {
348            self.base.set_blob_hashes(blob_hashes);
349        }
350
351        fn set_max_fee_per_blob_gas(&mut self, max_fee_per_blob_gas: u128) {
352            self.base.set_max_fee_per_blob_gas(max_fee_per_blob_gas);
353        }
354
355        fn enveloped_tx(&self) -> Option<&Bytes> {
356            BaseTxTr::enveloped_tx(self)
357        }
358
359        fn set_enveloped_tx(&mut self, bytes: Bytes) {
360            self.enveloped_tx = Some(bytes);
361        }
362
363        fn source_hash(&self) -> Option<B256> {
364            BaseTxTr::source_hash(self)
365        }
366
367        fn set_source_hash(&mut self, source_hash: B256) {
368            self.deposit.source_hash = source_hash;
369        }
370
371        fn mint(&self) -> Option<u128> {
372            BaseTxTr::mint(self)
373        }
374
375        fn set_mint(&mut self, mint: u128) {
376            self.deposit.mint = Some(mint);
377        }
378
379        fn is_system_transaction(&self) -> bool {
380            BaseTxTr::is_system_transaction(self)
381        }
382
383        fn set_system_transaction(&mut self, is_system_transaction: bool) {
384            self.deposit.is_system_transaction = is_system_transaction;
385        }
386
387        fn is_deposit(&self) -> bool {
388            self.tx_type() == DEPOSIT_TRANSACTION_TYPE
389        }
390    }
391}
392
393#[cfg(feature = "optimism")]
394mod optimism {
395    use super::*;
396    use alloy_op_evm::OpTx;
397    use op_revm::{OpTransaction, transaction::OpTxTr};
398
399    impl<TX: FoundryTransaction> FoundryTransaction for OpTransaction<TX> {
400        fn set_tx_type(&mut self, tx_type: u8) {
401            self.base.set_tx_type(tx_type);
402        }
403
404        fn set_caller(&mut self, caller: Address) {
405            self.base.set_caller(caller);
406        }
407
408        fn set_gas_limit(&mut self, gas_limit: u64) {
409            self.base.set_gas_limit(gas_limit);
410        }
411
412        fn set_gas_price(&mut self, gas_price: u128) {
413            self.base.set_gas_price(gas_price);
414        }
415
416        fn set_kind(&mut self, kind: TxKind) {
417            self.base.set_kind(kind);
418        }
419
420        fn set_value(&mut self, value: U256) {
421            self.base.set_value(value);
422        }
423
424        fn set_data(&mut self, data: Bytes) {
425            self.base.set_data(data);
426        }
427
428        fn set_nonce(&mut self, nonce: u64) {
429            self.base.set_nonce(nonce);
430        }
431
432        fn set_chain_id(&mut self, chain_id: Option<u64>) {
433            self.base.set_chain_id(chain_id);
434        }
435
436        fn set_access_list(&mut self, access_list: AccessList) {
437            self.base.set_access_list(access_list);
438        }
439
440        fn authorization_list_mut(
441            &mut self,
442        ) -> &mut Vec<Either<SignedAuthorization, RecoveredAuthorization>> {
443            self.base.authorization_list_mut()
444        }
445
446        fn set_gas_priority_fee(&mut self, gas_priority_fee: Option<u128>) {
447            self.base.set_gas_priority_fee(gas_priority_fee);
448        }
449
450        fn set_blob_hashes(&mut self, _blob_hashes: Vec<B256>) {}
451
452        fn set_max_fee_per_blob_gas(&mut self, _max_fee_per_blob_gas: u128) {}
453
454        fn enveloped_tx(&self) -> Option<&Bytes> {
455            OpTxTr::enveloped_tx(self)
456        }
457
458        fn set_enveloped_tx(&mut self, bytes: Bytes) {
459            self.enveloped_tx = Some(bytes);
460        }
461
462        fn source_hash(&self) -> Option<B256> {
463            OpTxTr::source_hash(self)
464        }
465
466        fn set_source_hash(&mut self, source_hash: B256) {
467            if self.tx_type() == DEPOSIT_TRANSACTION_TYPE {
468                self.deposit.source_hash = source_hash;
469            }
470        }
471
472        fn mint(&self) -> Option<u128> {
473            OpTxTr::mint(self)
474        }
475
476        fn set_mint(&mut self, mint: u128) {
477            if self.tx_type() == DEPOSIT_TRANSACTION_TYPE {
478                self.deposit.mint = Some(mint);
479            }
480        }
481
482        fn is_system_transaction(&self) -> bool {
483            OpTxTr::is_system_transaction(self)
484        }
485
486        fn set_system_transaction(&mut self, is_system_transaction: bool) {
487            if self.tx_type() == DEPOSIT_TRANSACTION_TYPE {
488                self.deposit.is_system_transaction = is_system_transaction;
489            }
490        }
491    }
492
493    impl FoundryTransaction for OpTx {
494        fn set_tx_type(&mut self, tx_type: u8) {
495            self.0.set_tx_type(tx_type);
496        }
497
498        fn set_caller(&mut self, caller: Address) {
499            self.0.set_caller(caller);
500        }
501
502        fn set_gas_limit(&mut self, gas_limit: u64) {
503            self.0.set_gas_limit(gas_limit);
504        }
505
506        fn set_gas_price(&mut self, gas_price: u128) {
507            self.0.set_gas_price(gas_price);
508        }
509
510        fn set_kind(&mut self, kind: TxKind) {
511            self.0.set_kind(kind);
512        }
513
514        fn set_value(&mut self, value: U256) {
515            self.0.set_value(value);
516        }
517
518        fn set_data(&mut self, data: Bytes) {
519            self.0.set_data(data);
520        }
521
522        fn set_nonce(&mut self, nonce: u64) {
523            self.0.set_nonce(nonce);
524        }
525
526        fn set_chain_id(&mut self, chain_id: Option<u64>) {
527            self.0.set_chain_id(chain_id);
528        }
529
530        fn set_access_list(&mut self, access_list: AccessList) {
531            self.0.set_access_list(access_list);
532        }
533
534        fn authorization_list_mut(
535            &mut self,
536        ) -> &mut Vec<Either<SignedAuthorization, RecoveredAuthorization>> {
537            self.0.authorization_list_mut()
538        }
539
540        fn set_gas_priority_fee(&mut self, gas_priority_fee: Option<u128>) {
541            self.0.set_gas_priority_fee(gas_priority_fee);
542        }
543
544        fn set_blob_hashes(&mut self, _blob_hashes: Vec<B256>) {}
545
546        fn set_max_fee_per_blob_gas(&mut self, _max_fee_per_blob_gas: u128) {}
547
548        fn enveloped_tx(&self) -> Option<&Bytes> {
549            FoundryTransaction::enveloped_tx(&self.0)
550        }
551
552        fn set_enveloped_tx(&mut self, bytes: Bytes) {
553            self.0.set_enveloped_tx(bytes);
554        }
555
556        fn source_hash(&self) -> Option<B256> {
557            FoundryTransaction::source_hash(&self.0)
558        }
559
560        fn set_source_hash(&mut self, source_hash: B256) {
561            self.0.set_source_hash(source_hash);
562        }
563
564        fn mint(&self) -> Option<u128> {
565            FoundryTransaction::mint(&self.0)
566        }
567
568        fn set_mint(&mut self, mint: u128) {
569            self.0.set_mint(mint);
570        }
571
572        fn is_system_transaction(&self) -> bool {
573            FoundryTransaction::is_system_transaction(&self.0)
574        }
575
576        fn set_system_transaction(&mut self, is_system_transaction: bool) {
577            self.0.set_system_transaction(is_system_transaction);
578        }
579    }
580}
581
582#[cfg(test)]
583mod tests {
584    use super::*;
585    use tempo_alloy::primitives::transaction::Call;
586
587    #[test]
588    fn tempo_tx_env_setters_update_aa_call_payload() {
589        let old_to = TxKind::Call(Address::with_last_byte(0xAA));
590        let new_to = TxKind::Create;
591        let new_value = U256::from(123);
592        let new_input = Bytes::from_static(b"local bytecode");
593
594        let mut tx_env = TempoTxEnv {
595            inner: TxEnv {
596                kind: old_to,
597                value: U256::ONE,
598                data: Bytes::from_static(b"original bytecode"),
599                ..Default::default()
600            },
601            tempo_tx_env: Some(Box::new(tempo_revm::TempoBatchCallEnv {
602                aa_calls: vec![Call {
603                    to: old_to,
604                    value: U256::ONE,
605                    input: Bytes::from_static(b"original bytecode"),
606                }],
607                ..Default::default()
608            })),
609            ..Default::default()
610        };
611
612        tx_env.set_kind(new_to);
613        tx_env.set_value(new_value);
614        tx_env.set_data(new_input.clone());
615
616        assert_eq!(tx_env.inner.kind, new_to);
617        assert_eq!(tx_env.inner.value, new_value);
618        assert_eq!(tx_env.inner.data, new_input);
619
620        let call = &tx_env.tempo_tx_env.as_ref().unwrap().aa_calls[0];
621        assert_eq!(call.to, new_to);
622        assert_eq!(call.value, new_value);
623        assert_eq!(call.input, new_input);
624    }
625}