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
134impl FoundryTransaction for TxEnv {
135    fn set_tx_type(&mut self, tx_type: u8) {
136        self.tx_type = tx_type;
137    }
138
139    fn set_caller(&mut self, caller: Address) {
140        self.caller = caller;
141    }
142
143    fn set_gas_limit(&mut self, gas_limit: u64) {
144        self.gas_limit = gas_limit;
145    }
146
147    fn set_gas_price(&mut self, gas_price: u128) {
148        self.gas_price = gas_price;
149    }
150
151    fn set_kind(&mut self, kind: TxKind) {
152        self.kind = kind;
153    }
154
155    fn set_value(&mut self, value: U256) {
156        self.value = value;
157    }
158
159    fn set_data(&mut self, data: Bytes) {
160        self.data = data;
161    }
162
163    fn set_nonce(&mut self, nonce: u64) {
164        self.nonce = nonce;
165    }
166
167    fn set_chain_id(&mut self, chain_id: Option<u64>) {
168        self.chain_id = chain_id;
169    }
170
171    fn set_access_list(&mut self, access_list: AccessList) {
172        self.access_list = access_list;
173    }
174
175    fn authorization_list_mut(
176        &mut self,
177    ) -> &mut Vec<Either<SignedAuthorization, RecoveredAuthorization>> {
178        &mut self.authorization_list
179    }
180
181    fn set_gas_priority_fee(&mut self, gas_priority_fee: Option<u128>) {
182        self.gas_priority_fee = gas_priority_fee;
183    }
184
185    fn set_blob_hashes(&mut self, blob_hashes: Vec<B256>) {
186        self.blob_hashes = blob_hashes;
187    }
188
189    fn set_max_fee_per_blob_gas(&mut self, max_fee_per_blob_gas: u128) {
190        self.max_fee_per_blob_gas = max_fee_per_blob_gas;
191    }
192}
193
194impl FoundryTransaction for TempoTxEnv {
195    fn set_tx_type(&mut self, tx_type: u8) {
196        self.inner.set_tx_type(tx_type);
197    }
198
199    fn set_caller(&mut self, caller: Address) {
200        self.inner.set_caller(caller);
201    }
202
203    fn set_gas_limit(&mut self, gas_limit: u64) {
204        self.inner.set_gas_limit(gas_limit);
205    }
206
207    fn set_gas_price(&mut self, gas_price: u128) {
208        self.inner.set_gas_price(gas_price);
209    }
210
211    fn set_kind(&mut self, kind: TxKind) {
212        self.inner.set_kind(kind);
213        if let Some(call) =
214            self.tempo_tx_env.as_deref_mut().and_then(|env| env.aa_calls.first_mut())
215        {
216            call.to = kind;
217        }
218    }
219
220    fn set_value(&mut self, value: U256) {
221        self.inner.set_value(value);
222        if let Some(call) =
223            self.tempo_tx_env.as_deref_mut().and_then(|env| env.aa_calls.first_mut())
224        {
225            call.value = value;
226        }
227    }
228
229    fn set_data(&mut self, data: Bytes) {
230        self.inner.set_data(data.clone());
231        if let Some(call) =
232            self.tempo_tx_env.as_deref_mut().and_then(|env| env.aa_calls.first_mut())
233        {
234            call.input = data;
235        }
236    }
237
238    fn set_nonce(&mut self, nonce: u64) {
239        self.inner.set_nonce(nonce);
240    }
241
242    fn set_chain_id(&mut self, chain_id: Option<u64>) {
243        self.inner.set_chain_id(chain_id);
244    }
245
246    fn set_access_list(&mut self, access_list: AccessList) {
247        self.inner.set_access_list(access_list);
248    }
249
250    fn authorization_list_mut(
251        &mut self,
252    ) -> &mut Vec<Either<SignedAuthorization, RecoveredAuthorization>> {
253        self.inner.authorization_list_mut()
254    }
255
256    fn set_gas_priority_fee(&mut self, gas_priority_fee: Option<u128>) {
257        self.inner.set_gas_priority_fee(gas_priority_fee);
258    }
259
260    fn set_blob_hashes(&mut self, _blob_hashes: Vec<B256>) {}
261
262    fn set_max_fee_per_blob_gas(&mut self, _max_fee_per_blob_gas: u128) {}
263
264    fn fee_token(&self) -> Option<Address> {
265        self.fee_token
266    }
267
268    fn set_fee_token(&mut self, token: Option<Address>) {
269        self.fee_token = token;
270    }
271
272    fn fee_payer(&self) -> Option<Option<Address>> {
273        self.fee_payer
274    }
275
276    fn set_fee_payer(&mut self, payer: Option<Option<Address>>) {
277        self.fee_payer = payer;
278    }
279}
280
281#[cfg(feature = "base")]
282mod base {
283    use super::*;
284    use base_common_evm::{BaseTransaction, BaseTxTr, DEPOSIT_TRANSACTION_TYPE};
285
286    impl<TX: FoundryTransaction> FoundryTransaction for BaseTransaction<TX> {
287        fn set_tx_type(&mut self, tx_type: u8) {
288            self.base.set_tx_type(tx_type);
289        }
290
291        fn set_caller(&mut self, caller: Address) {
292            self.base.set_caller(caller);
293        }
294
295        fn set_gas_limit(&mut self, gas_limit: u64) {
296            self.base.set_gas_limit(gas_limit);
297        }
298
299        fn set_gas_price(&mut self, gas_price: u128) {
300            self.base.set_gas_price(gas_price);
301        }
302
303        fn set_kind(&mut self, kind: TxKind) {
304            self.base.set_kind(kind);
305        }
306
307        fn set_value(&mut self, value: U256) {
308            self.base.set_value(value);
309        }
310
311        fn set_data(&mut self, data: Bytes) {
312            self.base.set_data(data);
313        }
314
315        fn set_nonce(&mut self, nonce: u64) {
316            self.base.set_nonce(nonce);
317        }
318
319        fn set_chain_id(&mut self, chain_id: Option<u64>) {
320            self.base.set_chain_id(chain_id);
321        }
322
323        fn set_access_list(&mut self, access_list: AccessList) {
324            self.base.set_access_list(access_list);
325        }
326
327        fn authorization_list_mut(
328            &mut self,
329        ) -> &mut Vec<Either<SignedAuthorization, RecoveredAuthorization>> {
330            self.base.authorization_list_mut()
331        }
332
333        fn set_gas_priority_fee(&mut self, gas_priority_fee: Option<u128>) {
334            self.base.set_gas_priority_fee(gas_priority_fee);
335        }
336
337        fn set_blob_hashes(&mut self, blob_hashes: Vec<B256>) {
338            self.base.set_blob_hashes(blob_hashes);
339        }
340
341        fn set_max_fee_per_blob_gas(&mut self, max_fee_per_blob_gas: u128) {
342            self.base.set_max_fee_per_blob_gas(max_fee_per_blob_gas);
343        }
344
345        fn enveloped_tx(&self) -> Option<&Bytes> {
346            BaseTxTr::enveloped_tx(self)
347        }
348
349        fn set_enveloped_tx(&mut self, bytes: Bytes) {
350            self.enveloped_tx = Some(bytes);
351        }
352
353        fn source_hash(&self) -> Option<B256> {
354            BaseTxTr::source_hash(self)
355        }
356
357        fn set_source_hash(&mut self, source_hash: B256) {
358            self.deposit.source_hash = source_hash;
359        }
360
361        fn mint(&self) -> Option<u128> {
362            BaseTxTr::mint(self)
363        }
364
365        fn set_mint(&mut self, mint: u128) {
366            self.deposit.mint = Some(mint);
367        }
368
369        fn is_system_transaction(&self) -> bool {
370            BaseTxTr::is_system_transaction(self)
371        }
372
373        fn set_system_transaction(&mut self, is_system_transaction: bool) {
374            self.deposit.is_system_transaction = is_system_transaction;
375        }
376
377        fn is_deposit(&self) -> bool {
378            self.tx_type() == DEPOSIT_TRANSACTION_TYPE
379        }
380    }
381}
382
383#[cfg(feature = "optimism")]
384mod optimism {
385    use super::*;
386    use alloy_op_evm::OpTx;
387    use op_revm::{OpTransaction, transaction::OpTxTr};
388
389    impl<TX: FoundryTransaction> FoundryTransaction for OpTransaction<TX> {
390        fn set_tx_type(&mut self, tx_type: u8) {
391            self.base.set_tx_type(tx_type);
392        }
393
394        fn set_caller(&mut self, caller: Address) {
395            self.base.set_caller(caller);
396        }
397
398        fn set_gas_limit(&mut self, gas_limit: u64) {
399            self.base.set_gas_limit(gas_limit);
400        }
401
402        fn set_gas_price(&mut self, gas_price: u128) {
403            self.base.set_gas_price(gas_price);
404        }
405
406        fn set_kind(&mut self, kind: TxKind) {
407            self.base.set_kind(kind);
408        }
409
410        fn set_value(&mut self, value: U256) {
411            self.base.set_value(value);
412        }
413
414        fn set_data(&mut self, data: Bytes) {
415            self.base.set_data(data);
416        }
417
418        fn set_nonce(&mut self, nonce: u64) {
419            self.base.set_nonce(nonce);
420        }
421
422        fn set_chain_id(&mut self, chain_id: Option<u64>) {
423            self.base.set_chain_id(chain_id);
424        }
425
426        fn set_access_list(&mut self, access_list: AccessList) {
427            self.base.set_access_list(access_list);
428        }
429
430        fn authorization_list_mut(
431            &mut self,
432        ) -> &mut Vec<Either<SignedAuthorization, RecoveredAuthorization>> {
433            self.base.authorization_list_mut()
434        }
435
436        fn set_gas_priority_fee(&mut self, gas_priority_fee: Option<u128>) {
437            self.base.set_gas_priority_fee(gas_priority_fee);
438        }
439
440        fn set_blob_hashes(&mut self, _blob_hashes: Vec<B256>) {}
441
442        fn set_max_fee_per_blob_gas(&mut self, _max_fee_per_blob_gas: u128) {}
443
444        fn enveloped_tx(&self) -> Option<&Bytes> {
445            OpTxTr::enveloped_tx(self)
446        }
447
448        fn set_enveloped_tx(&mut self, bytes: Bytes) {
449            self.enveloped_tx = Some(bytes);
450        }
451
452        fn source_hash(&self) -> Option<B256> {
453            OpTxTr::source_hash(self)
454        }
455
456        fn set_source_hash(&mut self, source_hash: B256) {
457            if self.tx_type() == DEPOSIT_TRANSACTION_TYPE {
458                self.deposit.source_hash = source_hash;
459            }
460        }
461
462        fn mint(&self) -> Option<u128> {
463            OpTxTr::mint(self)
464        }
465
466        fn set_mint(&mut self, mint: u128) {
467            if self.tx_type() == DEPOSIT_TRANSACTION_TYPE {
468                self.deposit.mint = Some(mint);
469            }
470        }
471
472        fn is_system_transaction(&self) -> bool {
473            OpTxTr::is_system_transaction(self)
474        }
475
476        fn set_system_transaction(&mut self, is_system_transaction: bool) {
477            if self.tx_type() == DEPOSIT_TRANSACTION_TYPE {
478                self.deposit.is_system_transaction = is_system_transaction;
479            }
480        }
481    }
482
483    impl FoundryTransaction for OpTx {
484        fn set_tx_type(&mut self, tx_type: u8) {
485            self.0.set_tx_type(tx_type);
486        }
487
488        fn set_caller(&mut self, caller: Address) {
489            self.0.set_caller(caller);
490        }
491
492        fn set_gas_limit(&mut self, gas_limit: u64) {
493            self.0.set_gas_limit(gas_limit);
494        }
495
496        fn set_gas_price(&mut self, gas_price: u128) {
497            self.0.set_gas_price(gas_price);
498        }
499
500        fn set_kind(&mut self, kind: TxKind) {
501            self.0.set_kind(kind);
502        }
503
504        fn set_value(&mut self, value: U256) {
505            self.0.set_value(value);
506        }
507
508        fn set_data(&mut self, data: Bytes) {
509            self.0.set_data(data);
510        }
511
512        fn set_nonce(&mut self, nonce: u64) {
513            self.0.set_nonce(nonce);
514        }
515
516        fn set_chain_id(&mut self, chain_id: Option<u64>) {
517            self.0.set_chain_id(chain_id);
518        }
519
520        fn set_access_list(&mut self, access_list: AccessList) {
521            self.0.set_access_list(access_list);
522        }
523
524        fn authorization_list_mut(
525            &mut self,
526        ) -> &mut Vec<Either<SignedAuthorization, RecoveredAuthorization>> {
527            self.0.authorization_list_mut()
528        }
529
530        fn set_gas_priority_fee(&mut self, gas_priority_fee: Option<u128>) {
531            self.0.set_gas_priority_fee(gas_priority_fee);
532        }
533
534        fn set_blob_hashes(&mut self, _blob_hashes: Vec<B256>) {}
535
536        fn set_max_fee_per_blob_gas(&mut self, _max_fee_per_blob_gas: u128) {}
537
538        fn enveloped_tx(&self) -> Option<&Bytes> {
539            FoundryTransaction::enveloped_tx(&self.0)
540        }
541
542        fn set_enveloped_tx(&mut self, bytes: Bytes) {
543            self.0.set_enveloped_tx(bytes);
544        }
545
546        fn source_hash(&self) -> Option<B256> {
547            FoundryTransaction::source_hash(&self.0)
548        }
549
550        fn set_source_hash(&mut self, source_hash: B256) {
551            self.0.set_source_hash(source_hash);
552        }
553
554        fn mint(&self) -> Option<u128> {
555            FoundryTransaction::mint(&self.0)
556        }
557
558        fn set_mint(&mut self, mint: u128) {
559            self.0.set_mint(mint);
560        }
561
562        fn is_system_transaction(&self) -> bool {
563            FoundryTransaction::is_system_transaction(&self.0)
564        }
565
566        fn set_system_transaction(&mut self, is_system_transaction: bool) {
567            self.0.set_system_transaction(is_system_transaction);
568        }
569    }
570}
571
572#[cfg(test)]
573mod tests {
574    use super::*;
575    use tempo_alloy::primitives::transaction::Call;
576
577    #[test]
578    fn tempo_tx_env_setters_update_aa_call_payload() {
579        let old_to = TxKind::Call(Address::with_last_byte(0xAA));
580        let new_to = TxKind::Create;
581        let new_value = U256::from(123);
582        let new_input = Bytes::from_static(b"local bytecode");
583
584        let mut tx_env = TempoTxEnv {
585            inner: TxEnv {
586                kind: old_to,
587                value: U256::ONE,
588                data: Bytes::from_static(b"original bytecode"),
589                ..Default::default()
590            },
591            tempo_tx_env: Some(Box::new(tempo_revm::TempoBatchCallEnv {
592                aa_calls: vec![Call {
593                    to: old_to,
594                    value: U256::ONE,
595                    input: Bytes::from_static(b"original bytecode"),
596                }],
597                ..Default::default()
598            })),
599            ..Default::default()
600        };
601
602        tx_env.set_kind(new_to);
603        tx_env.set_value(new_value);
604        tx_env.set_data(new_input.clone());
605
606        assert_eq!(tx_env.inner.kind, new_to);
607        assert_eq!(tx_env.inner.value, new_value);
608        assert_eq!(tx_env.inner.data, new_input);
609
610        let call = &tx_env.tempo_tx_env.as_ref().unwrap().aa_calls[0];
611        assert_eq!(call.to, new_to);
612        assert_eq!(call.value, new_value);
613        assert_eq!(call.input, new_input);
614    }
615}