Skip to main content

foundry_evm_core/
env.rs

1use std::fmt::Debug;
2
3use alloy_consensus::Typed2718;
4pub use alloy_evm::EvmEnv;
5use alloy_evm::FromRecoveredTx;
6use alloy_network::{AnyRpcTransaction, AnyTxEnvelope, TransactionResponse};
7use alloy_primitives::{Address, B256, Bytes, U256};
8#[cfg(feature = "optimism")]
9use op_revm::transaction::deposit::DEPOSIT_TRANSACTION_TYPE;
10use revm::{
11    Context, Database, Journal,
12    context::{Block, BlockEnv, Cfg, CfgEnv, Transaction, TxEnv},
13    context_interface::{
14        ContextTr,
15        either::Either,
16        transaction::{AccessList, RecoveredAuthorization, SignedAuthorization},
17    },
18    inspector::JournalExt,
19    primitives::{TxKind, hardfork::SpecId},
20};
21use tempo_revm::{TempoBlockEnv, TempoTxEnv};
22
23use crate::backend::JournaledState;
24
25/// Extension of [`Block`] with mutable setters, allowing EVM-agnostic mutation of block fields.
26pub trait FoundryBlock: Block {
27    /// Sets the block number.
28    fn set_number(&mut self, number: U256);
29
30    /// Sets the beneficiary (coinbase) address.
31    fn set_beneficiary(&mut self, beneficiary: Address);
32
33    /// Sets the block timestamp.
34    fn set_timestamp(&mut self, timestamp: U256);
35
36    /// Sets the gas limit.
37    fn set_gas_limit(&mut self, gas_limit: u64);
38
39    /// Sets the base fee per gas.
40    fn set_basefee(&mut self, basefee: u64);
41
42    /// Sets the block difficulty.
43    fn set_difficulty(&mut self, difficulty: U256);
44
45    /// Sets the prevrandao value.
46    fn set_prevrandao(&mut self, prevrandao: Option<B256>);
47
48    /// Sets the excess blob gas and blob gasprice.
49    fn set_blob_excess_gas_and_price(
50        &mut self,
51        _excess_blob_gas: u64,
52        _base_fee_update_fraction: u64,
53    );
54
55    // Tempo methods
56
57    /// Returns the milliseconds portion of the block timestamp.
58    fn timestamp_millis_part(&self) -> u64 {
59        0
60    }
61
62    /// Sets the milliseconds portion of the block timestamp.
63    fn set_timestamp_millis_part(&mut self, _millis: u64) {}
64}
65
66impl FoundryBlock for BlockEnv {
67    fn set_number(&mut self, number: U256) {
68        self.number = number;
69    }
70
71    fn set_beneficiary(&mut self, beneficiary: Address) {
72        self.beneficiary = beneficiary;
73    }
74
75    fn set_timestamp(&mut self, timestamp: U256) {
76        self.timestamp = timestamp;
77    }
78
79    fn set_gas_limit(&mut self, gas_limit: u64) {
80        self.gas_limit = gas_limit;
81    }
82
83    fn set_basefee(&mut self, basefee: u64) {
84        self.basefee = basefee;
85    }
86
87    fn set_difficulty(&mut self, difficulty: U256) {
88        self.difficulty = difficulty;
89    }
90
91    fn set_prevrandao(&mut self, prevrandao: Option<B256>) {
92        self.prevrandao = prevrandao;
93    }
94
95    fn set_blob_excess_gas_and_price(
96        &mut self,
97        excess_blob_gas: u64,
98        base_fee_update_fraction: u64,
99    ) {
100        self.set_blob_excess_gas_and_price(excess_blob_gas, base_fee_update_fraction);
101    }
102}
103
104impl FoundryBlock for TempoBlockEnv {
105    fn set_number(&mut self, number: U256) {
106        self.inner.set_number(number);
107    }
108
109    fn set_beneficiary(&mut self, beneficiary: Address) {
110        self.inner.set_beneficiary(beneficiary);
111    }
112
113    fn set_timestamp(&mut self, timestamp: U256) {
114        self.inner.set_timestamp(timestamp);
115    }
116
117    fn set_gas_limit(&mut self, gas_limit: u64) {
118        self.inner.set_gas_limit(gas_limit);
119    }
120
121    fn set_basefee(&mut self, basefee: u64) {
122        self.inner.set_basefee(basefee);
123    }
124
125    fn set_difficulty(&mut self, difficulty: U256) {
126        self.inner.set_difficulty(difficulty);
127    }
128
129    fn set_prevrandao(&mut self, prevrandao: Option<B256>) {
130        self.inner.set_prevrandao(prevrandao);
131    }
132
133    fn set_blob_excess_gas_and_price(
134        &mut self,
135        _excess_blob_gas: u64,
136        _base_fee_update_fraction: u64,
137    ) {
138    }
139
140    fn timestamp_millis_part(&self) -> u64 {
141        self.timestamp_millis_part
142    }
143
144    fn set_timestamp_millis_part(&mut self, millis: u64) {
145        self.timestamp_millis_part = millis;
146    }
147}
148
149/// Extension of [`Transaction`] with mutable setters, allowing EVM-agnostic mutation of transaction
150/// fields.
151pub trait FoundryTransaction: Transaction {
152    /// Sets the transaction type.
153    fn set_tx_type(&mut self, tx_type: u8);
154
155    /// Sets the caller (sender) address.
156    fn set_caller(&mut self, caller: Address);
157
158    /// Sets the gas limit.
159    fn set_gas_limit(&mut self, gas_limit: u64);
160
161    /// Sets the gas price (or max fee per gas for EIP-1559).
162    fn set_gas_price(&mut self, gas_price: u128);
163
164    /// Sets the transaction kind (call or create).
165    fn set_kind(&mut self, kind: TxKind);
166
167    /// Sets the value sent with the transaction.
168    fn set_value(&mut self, value: U256);
169
170    /// Sets the transaction input data.
171    fn set_data(&mut self, data: Bytes);
172
173    /// Sets the nonce.
174    fn set_nonce(&mut self, nonce: u64);
175
176    /// Sets the chain ID.
177    fn set_chain_id(&mut self, chain_id: Option<u64>);
178
179    /// Sets the access list.
180    fn set_access_list(&mut self, access_list: AccessList);
181
182    /// Returns a mutable reference to the EIP-7702 authorization list.
183    fn authorization_list_mut(
184        &mut self,
185    ) -> &mut Vec<Either<SignedAuthorization, RecoveredAuthorization>>;
186
187    /// Sets the max priority fee per gas.
188    fn set_gas_priority_fee(&mut self, gas_priority_fee: Option<u128>);
189
190    /// Sets the blob versioned hashes.
191    fn set_blob_hashes(&mut self, blob_hashes: Vec<B256>);
192
193    /// Sets the max fee per blob gas.
194    fn set_max_fee_per_blob_gas(&mut self, max_fee_per_blob_gas: u128);
195
196    /// Sets the EIP-7702 signed authorization list.
197    fn set_signed_authorization(&mut self, auth: Vec<SignedAuthorization>) {
198        *self.authorization_list_mut() = auth.into_iter().map(Either::Left).collect();
199    }
200
201    // `OpTransaction` methods
202
203    /// Enveloped transaction bytes.
204    fn enveloped_tx(&self) -> Option<&Bytes> {
205        None
206    }
207
208    /// Set Enveloped transaction bytes.
209    fn set_enveloped_tx(&mut self, _bytes: Bytes) {}
210
211    /// Source hash of the deposit transaction.
212    fn source_hash(&self) -> Option<B256> {
213        None
214    }
215
216    /// Sets source hash of the deposit transaction.
217    fn set_source_hash(&mut self, _source_hash: B256) {}
218
219    /// Mint of the deposit transaction
220    fn mint(&self) -> Option<u128> {
221        None
222    }
223
224    /// Sets mint of the deposit transaction.
225    fn set_mint(&mut self, _mint: u128) {}
226
227    /// Whether the transaction is a system transaction
228    fn is_system_transaction(&self) -> bool {
229        false
230    }
231
232    /// Sets whether the transaction is a system transaction
233    fn set_system_transaction(&mut self, _is_system_transaction: bool) {}
234
235    /// Returns `true` if transaction is an Optimism deposit transaction.
236    fn is_deposit(&self) -> bool {
237        #[cfg(feature = "optimism")]
238        {
239            self.tx_type() == DEPOSIT_TRANSACTION_TYPE
240        }
241        #[cfg(not(feature = "optimism"))]
242        {
243            false
244        }
245    }
246
247    // Tempo methods
248
249    /// Returns the fee token address for this transaction.
250    fn fee_token(&self) -> Option<Address> {
251        None
252    }
253
254    /// Sets the fee token address for this transaction.
255    fn set_fee_token(&mut self, _token: Option<Address>) {}
256
257    /// Returns the fee payer for this transaction.
258    fn fee_payer(&self) -> Option<Option<Address>> {
259        None
260    }
261
262    /// Sets the fee payer for this transaction.
263    fn set_fee_payer(&mut self, _payer: Option<Option<Address>>) {}
264}
265
266impl FoundryTransaction for TxEnv {
267    fn set_tx_type(&mut self, tx_type: u8) {
268        self.tx_type = tx_type;
269    }
270
271    fn set_caller(&mut self, caller: Address) {
272        self.caller = caller;
273    }
274
275    fn set_gas_limit(&mut self, gas_limit: u64) {
276        self.gas_limit = gas_limit;
277    }
278
279    fn set_gas_price(&mut self, gas_price: u128) {
280        self.gas_price = gas_price;
281    }
282
283    fn set_kind(&mut self, kind: TxKind) {
284        self.kind = kind;
285    }
286
287    fn set_value(&mut self, value: U256) {
288        self.value = value;
289    }
290
291    fn set_data(&mut self, data: Bytes) {
292        self.data = data;
293    }
294
295    fn set_nonce(&mut self, nonce: u64) {
296        self.nonce = nonce;
297    }
298
299    fn set_chain_id(&mut self, chain_id: Option<u64>) {
300        self.chain_id = chain_id;
301    }
302
303    fn set_access_list(&mut self, access_list: AccessList) {
304        self.access_list = access_list;
305    }
306
307    fn authorization_list_mut(
308        &mut self,
309    ) -> &mut Vec<Either<SignedAuthorization, RecoveredAuthorization>> {
310        &mut self.authorization_list
311    }
312
313    fn set_gas_priority_fee(&mut self, gas_priority_fee: Option<u128>) {
314        self.gas_priority_fee = gas_priority_fee;
315    }
316
317    fn set_blob_hashes(&mut self, blob_hashes: Vec<B256>) {
318        self.blob_hashes = blob_hashes;
319    }
320
321    fn set_max_fee_per_blob_gas(&mut self, max_fee_per_blob_gas: u128) {
322        self.max_fee_per_blob_gas = max_fee_per_blob_gas;
323    }
324}
325
326impl FoundryTransaction for TempoTxEnv {
327    fn set_tx_type(&mut self, tx_type: u8) {
328        self.inner.set_tx_type(tx_type);
329    }
330
331    fn set_caller(&mut self, caller: Address) {
332        self.inner.set_caller(caller);
333    }
334
335    fn set_gas_limit(&mut self, gas_limit: u64) {
336        self.inner.set_gas_limit(gas_limit);
337    }
338
339    fn set_gas_price(&mut self, gas_price: u128) {
340        self.inner.set_gas_price(gas_price);
341    }
342
343    fn set_kind(&mut self, kind: TxKind) {
344        self.inner.set_kind(kind);
345    }
346
347    fn set_value(&mut self, value: U256) {
348        self.inner.set_value(value);
349    }
350
351    fn set_data(&mut self, data: Bytes) {
352        self.inner.set_data(data);
353    }
354
355    fn set_nonce(&mut self, nonce: u64) {
356        self.inner.set_nonce(nonce);
357    }
358
359    fn set_chain_id(&mut self, chain_id: Option<u64>) {
360        self.inner.set_chain_id(chain_id);
361    }
362
363    fn set_access_list(&mut self, access_list: AccessList) {
364        self.inner.set_access_list(access_list);
365    }
366
367    fn authorization_list_mut(
368        &mut self,
369    ) -> &mut Vec<Either<SignedAuthorization, RecoveredAuthorization>> {
370        self.inner.authorization_list_mut()
371    }
372
373    fn set_gas_priority_fee(&mut self, gas_priority_fee: Option<u128>) {
374        self.inner.set_gas_priority_fee(gas_priority_fee);
375    }
376
377    fn set_blob_hashes(&mut self, _blob_hashes: Vec<B256>) {}
378
379    fn set_max_fee_per_blob_gas(&mut self, _max_fee_per_blob_gas: u128) {}
380
381    fn fee_token(&self) -> Option<Address> {
382        self.fee_token
383    }
384
385    fn set_fee_token(&mut self, token: Option<Address>) {
386        self.fee_token = token;
387    }
388
389    fn fee_payer(&self) -> Option<Option<Address>> {
390        self.fee_payer
391    }
392
393    fn set_fee_payer(&mut self, payer: Option<Option<Address>>) {
394        self.fee_payer = payer;
395    }
396}
397
398/// Extension trait providing mutable field access to block, tx, and cfg environments.
399///
400/// [`ContextTr`] only exposes immutable references for block, tx, and cfg.
401/// Cheatcodes like `vm.warp()`, `vm.roll()`, `vm.chainId()` need to mutate these fields.
402pub trait FoundryContextExt:
403    ContextTr<
404        Block: FoundryBlock + Clone,
405        Tx: FoundryTransaction + Clone,
406        Cfg = CfgEnv<Self::Spec>,
407        Journal: JournalExt,
408    >
409{
410    /// Specification id type
411    ///
412    /// Bubbled-up from `ContextTr::Cfg` for convenience and simplified bounds.
413    type Spec: Into<SpecId> + Copy + Debug;
414
415    /// Mutable reference to the block environment.
416    fn block_mut(&mut self) -> &mut Self::Block;
417
418    /// Mutable reference to the transaction environment.
419    fn tx_mut(&mut self) -> &mut Self::Tx;
420
421    /// Mutable reference to the configuration environment.
422    fn cfg_mut(&mut self) -> &mut Self::Cfg;
423
424    /// Mutable reference to the db and the journal inner.
425    fn db_journal_inner_mut(&mut self) -> (&mut Self::Db, &mut JournaledState);
426
427    /// Sets block environment.
428    fn set_block(&mut self, block: Self::Block) {
429        *self.block_mut() = block;
430    }
431
432    /// Sets transaction environment.
433    fn set_tx(&mut self, tx: Self::Tx) {
434        *self.tx_mut() = tx;
435    }
436
437    /// Sets configuration environment.
438    fn set_cfg(&mut self, cfg: Self::Cfg) {
439        *self.cfg_mut() = cfg;
440    }
441
442    /// Sets journal inner.
443    fn set_journal_inner(&mut self, journal_inner: JournaledState) {
444        *self.db_journal_inner_mut().1 = journal_inner;
445    }
446
447    /// Sets EVM environment.
448    fn set_evm(&mut self, evm_env: EvmEnv<Self::Spec, Self::Block>) {
449        *self.cfg_mut() = evm_env.cfg_env;
450        *self.block_mut() = evm_env.block_env;
451    }
452
453    /// Cloned transaction environment.
454    fn tx_clone(&self) -> Self::Tx {
455        self.tx().clone()
456    }
457
458    /// Cloned EVM environment (Cfg + Block).
459    fn evm_clone(&self) -> EvmEnv<Self::Spec, Self::Block> {
460        EvmEnv::new(self.cfg().clone(), self.block().clone())
461    }
462}
463
464impl<
465    BLOCK: FoundryBlock + Clone,
466    TX: FoundryTransaction + Clone,
467    SPEC: Into<SpecId> + Copy + Debug,
468    DB: Database,
469    C,
470> FoundryContextExt for Context<BLOCK, TX, CfgEnv<SPEC>, DB, Journal<DB>, C>
471{
472    type Spec = <Self::Cfg as Cfg>::Spec;
473
474    fn block_mut(&mut self) -> &mut Self::Block {
475        &mut self.block
476    }
477
478    fn tx_mut(&mut self) -> &mut Self::Tx {
479        &mut self.tx
480    }
481
482    fn cfg_mut(&mut self) -> &mut Self::Cfg {
483        &mut self.cfg
484    }
485
486    fn db_journal_inner_mut(&mut self) -> (&mut Self::Db, &mut JournaledState) {
487        (&mut self.journaled_state.database, &mut self.journaled_state.inner)
488    }
489}
490
491/// Trait for converting an [`AnyRpcTransaction`] into a specific `TxEnv`.
492///
493/// Implementations extract the inner [`alloy_consensus::TxEnvelope`] via
494/// [`as_envelope()`](alloy_network::AnyTxEnvelope::as_envelope) then delegate to
495/// [`FromRecoveredTx`].
496pub trait FromAnyRpcTransaction: Sized {
497    /// Tries to convert an [`AnyRpcTransaction`] into `Self`.
498    fn from_any_rpc_transaction(tx: &AnyRpcTransaction) -> eyre::Result<Self>;
499}
500
501impl FromAnyRpcTransaction for TxEnv {
502    fn from_any_rpc_transaction(tx: &AnyRpcTransaction) -> eyre::Result<Self> {
503        if let Some(envelope) = tx.as_envelope() {
504            Ok(Self::from_recovered_tx(envelope, tx.from()))
505        } else {
506            eyre::bail!("cannot convert unknown transaction type to TxEnv")
507        }
508    }
509}
510
511impl FromAnyRpcTransaction for TempoTxEnv {
512    fn from_any_rpc_transaction(tx: &AnyRpcTransaction) -> eyre::Result<Self> {
513        use alloy_consensus::Transaction as _;
514        if let Some(envelope) = tx.as_envelope() {
515            return Ok(TxEnv::from_recovered_tx(envelope, tx.from()).into());
516        }
517
518        // Handle Tempo transactions from `Unknown` envelope variant.
519        if let AnyTxEnvelope::Unknown(unknown) = &*tx.inner.inner
520            && unknown.ty() == tempo_alloy::primitives::TEMPO_TX_TYPE_ID
521        {
522            let base = TxEnv {
523                tx_type: unknown.ty(),
524                caller: tx.from(),
525                gas_limit: unknown.gas_limit(),
526                gas_price: unknown.max_fee_per_gas(),
527                gas_priority_fee: unknown.max_priority_fee_per_gas(),
528                kind: unknown.kind(),
529                value: unknown.value(),
530                data: unknown.input().clone(),
531                nonce: unknown.nonce(),
532                chain_id: unknown.chain_id(),
533                access_list: unknown.access_list().cloned().unwrap_or_default(),
534                ..Default::default()
535            };
536            let fee_token =
537                unknown.inner.fields.get_deserialized::<Address>("feeToken").and_then(Result::ok);
538            return Ok(Self { inner: base, fee_token, ..Default::default() });
539        }
540
541        eyre::bail!("cannot convert unknown transaction type to TempoTxEnv")
542    }
543}
544
545#[cfg(feature = "optimism")]
546mod optimism {
547    use super::*;
548    use alloy_op_evm::OpTx;
549    use op_alloy_consensus::{DEPOSIT_TX_TYPE_ID, TxDeposit};
550    use op_revm::{OpTransaction, transaction::OpTxTr};
551
552    impl<TX: FoundryTransaction> FoundryTransaction for OpTransaction<TX> {
553        fn set_tx_type(&mut self, tx_type: u8) {
554            self.base.set_tx_type(tx_type);
555        }
556
557        fn set_caller(&mut self, caller: Address) {
558            self.base.set_caller(caller);
559        }
560
561        fn set_gas_limit(&mut self, gas_limit: u64) {
562            self.base.set_gas_limit(gas_limit);
563        }
564
565        fn set_gas_price(&mut self, gas_price: u128) {
566            self.base.set_gas_price(gas_price);
567        }
568
569        fn set_kind(&mut self, kind: TxKind) {
570            self.base.set_kind(kind);
571        }
572
573        fn set_value(&mut self, value: U256) {
574            self.base.set_value(value);
575        }
576
577        fn set_data(&mut self, data: Bytes) {
578            self.base.set_data(data);
579        }
580
581        fn set_nonce(&mut self, nonce: u64) {
582            self.base.set_nonce(nonce);
583        }
584
585        fn set_chain_id(&mut self, chain_id: Option<u64>) {
586            self.base.set_chain_id(chain_id);
587        }
588
589        fn set_access_list(&mut self, access_list: AccessList) {
590            self.base.set_access_list(access_list);
591        }
592
593        fn authorization_list_mut(
594            &mut self,
595        ) -> &mut Vec<Either<SignedAuthorization, RecoveredAuthorization>> {
596            self.base.authorization_list_mut()
597        }
598
599        fn set_gas_priority_fee(&mut self, gas_priority_fee: Option<u128>) {
600            self.base.set_gas_priority_fee(gas_priority_fee);
601        }
602
603        fn set_blob_hashes(&mut self, _blob_hashes: Vec<B256>) {}
604
605        fn set_max_fee_per_blob_gas(&mut self, _max_fee_per_blob_gas: u128) {}
606
607        fn enveloped_tx(&self) -> Option<&Bytes> {
608            OpTxTr::enveloped_tx(self)
609        }
610
611        fn set_enveloped_tx(&mut self, bytes: Bytes) {
612            self.enveloped_tx = Some(bytes);
613        }
614
615        fn source_hash(&self) -> Option<B256> {
616            OpTxTr::source_hash(self)
617        }
618
619        fn set_source_hash(&mut self, source_hash: B256) {
620            if self.tx_type() == DEPOSIT_TRANSACTION_TYPE {
621                self.deposit.source_hash = source_hash;
622            }
623        }
624
625        fn mint(&self) -> Option<u128> {
626            OpTxTr::mint(self)
627        }
628
629        fn set_mint(&mut self, mint: u128) {
630            if self.tx_type() == DEPOSIT_TRANSACTION_TYPE {
631                self.deposit.mint = Some(mint);
632            }
633        }
634
635        fn is_system_transaction(&self) -> bool {
636            OpTxTr::is_system_transaction(self)
637        }
638
639        fn set_system_transaction(&mut self, is_system_transaction: bool) {
640            if self.tx_type() == DEPOSIT_TRANSACTION_TYPE {
641                self.deposit.is_system_transaction = is_system_transaction;
642            }
643        }
644    }
645
646    impl FoundryTransaction for OpTx {
647        fn set_tx_type(&mut self, tx_type: u8) {
648            self.0.set_tx_type(tx_type);
649        }
650
651        fn set_caller(&mut self, caller: Address) {
652            self.0.set_caller(caller);
653        }
654
655        fn set_gas_limit(&mut self, gas_limit: u64) {
656            self.0.set_gas_limit(gas_limit);
657        }
658
659        fn set_gas_price(&mut self, gas_price: u128) {
660            self.0.set_gas_price(gas_price);
661        }
662
663        fn set_kind(&mut self, kind: TxKind) {
664            self.0.set_kind(kind);
665        }
666
667        fn set_value(&mut self, value: U256) {
668            self.0.set_value(value);
669        }
670
671        fn set_data(&mut self, data: Bytes) {
672            self.0.set_data(data);
673        }
674
675        fn set_nonce(&mut self, nonce: u64) {
676            self.0.set_nonce(nonce);
677        }
678
679        fn set_chain_id(&mut self, chain_id: Option<u64>) {
680            self.0.set_chain_id(chain_id);
681        }
682
683        fn set_access_list(&mut self, access_list: AccessList) {
684            self.0.set_access_list(access_list);
685        }
686
687        fn authorization_list_mut(
688            &mut self,
689        ) -> &mut Vec<Either<SignedAuthorization, RecoveredAuthorization>> {
690            self.0.authorization_list_mut()
691        }
692
693        fn set_gas_priority_fee(&mut self, gas_priority_fee: Option<u128>) {
694            self.0.set_gas_priority_fee(gas_priority_fee);
695        }
696
697        fn set_blob_hashes(&mut self, _blob_hashes: Vec<B256>) {}
698
699        fn set_max_fee_per_blob_gas(&mut self, _max_fee_per_blob_gas: u128) {}
700
701        fn enveloped_tx(&self) -> Option<&Bytes> {
702            FoundryTransaction::enveloped_tx(&self.0)
703        }
704
705        fn set_enveloped_tx(&mut self, bytes: Bytes) {
706            self.0.set_enveloped_tx(bytes);
707        }
708
709        fn source_hash(&self) -> Option<B256> {
710            FoundryTransaction::source_hash(&self.0)
711        }
712
713        fn set_source_hash(&mut self, source_hash: B256) {
714            self.0.set_source_hash(source_hash);
715        }
716
717        fn mint(&self) -> Option<u128> {
718            FoundryTransaction::mint(&self.0)
719        }
720
721        fn set_mint(&mut self, mint: u128) {
722            self.0.set_mint(mint);
723        }
724
725        fn is_system_transaction(&self) -> bool {
726            FoundryTransaction::is_system_transaction(&self.0)
727        }
728
729        fn set_system_transaction(&mut self, is_system_transaction: bool) {
730            self.0.set_system_transaction(is_system_transaction);
731        }
732    }
733
734    impl FromAnyRpcTransaction for OpTx {
735        fn from_any_rpc_transaction(tx: &AnyRpcTransaction) -> eyre::Result<Self> {
736            if let Some(envelope) = tx.as_envelope() {
737                return Ok(Self(OpTransaction::<TxEnv> {
738                    base: TxEnv::from_recovered_tx(envelope, tx.from()),
739                    enveloped_tx: None,
740                    deposit: Default::default(),
741                }));
742            }
743
744            // Handle OP deposit transactions from `Unknown` envelope variant.
745            if let AnyTxEnvelope::Unknown(unknown) = &*tx.inner.inner
746                && unknown.ty() == DEPOSIT_TX_TYPE_ID
747            {
748                let mut fields = unknown.inner.fields.clone();
749                fields.insert("from".to_string(), serde_json::to_value(tx.from())?);
750                let deposit_tx: TxDeposit = fields
751                    .deserialize_into()
752                    .map_err(|e| eyre::eyre!("failed to deserialize deposit tx: {e}"))?;
753                return Ok(Self::from_recovered_tx(&deposit_tx, deposit_tx.from));
754            }
755
756            eyre::bail!("cannot convert unknown transaction type to OpTransaction")
757        }
758    }
759}
760
761#[cfg(test)]
762mod tests {
763    use std::num::NonZeroU64;
764
765    use super::*;
766    use alloy_consensus::{Signed, TxEip1559, transaction::Recovered};
767    use alloy_evm::{EthEvmFactory, EvmFactory};
768    use alloy_network::{AnyTxType, UnknownTxEnvelope, UnknownTypedTransaction};
769    use alloy_primitives::Signature;
770    use alloy_rpc_types::{Transaction as RpcTransaction, TransactionInfo};
771    use alloy_serde::WithOtherFields;
772    use foundry_evm_hardforks::TempoHardfork;
773    use revm::database::EmptyDB;
774    use tempo_alloy::primitives::{
775        AASigned, TempoSignature, TempoTransaction, TempoTxEnvelope,
776        transaction::PrimitiveSignature,
777    };
778    use tempo_evm::TempoEvmFactory;
779
780    #[test]
781    fn eth_evm_foundry_context_ext_implementation() {
782        let mut evm = EthEvmFactory::default().create_evm(EmptyDB::default(), EvmEnv::default());
783
784        // Test EVM Context Block mutation
785        evm.ctx_mut().block_mut().set_number(U256::from(123));
786        assert_eq!(evm.ctx().block().number(), U256::from(123));
787
788        // Test EVM Context Tx mutation
789        evm.ctx_mut().tx_mut().set_nonce(99);
790        assert_eq!(evm.ctx().tx().nonce(), 99);
791
792        // Test EVM Context Cfg mutation
793        evm.ctx_mut().cfg_mut().spec = SpecId::AMSTERDAM;
794        assert_eq!(evm.ctx().cfg().spec, SpecId::AMSTERDAM);
795
796        // Round-trip test to ensure no issues with cloning and setting tx_env and evm_env
797        let tx_env = evm.ctx().tx_clone();
798        evm.ctx_mut().set_tx(tx_env);
799        let evm_env = evm.ctx().evm_clone();
800        evm.ctx_mut().set_evm(evm_env);
801    }
802
803    #[test]
804    fn tempo_evm_foundry_context_ext_implementation() {
805        let mut evm = TempoEvmFactory::default().create_evm(EmptyDB::default(), EvmEnv::default());
806
807        // Test EVM Context Block mutation
808        evm.ctx_mut().block_mut().set_number(U256::from(123));
809        assert_eq!(evm.ctx().block().number(), U256::from(123));
810
811        // Test EVM Context Tx mutation
812        evm.ctx_mut().tx_mut().set_nonce(99);
813        assert_eq!(evm.ctx().tx().nonce(), 99);
814
815        // Test EVM Context Cfg mutation
816        evm.ctx_mut().cfg_mut().spec = TempoHardfork::Genesis;
817        assert_eq!(evm.ctx().cfg().spec, TempoHardfork::Genesis);
818
819        // Round-trip test to ensure no issues with cloning and setting tx_env and evm_env
820        let tx_env = evm.ctx().tx_clone();
821        evm.ctx_mut().set_tx(tx_env);
822        let evm_env = evm.ctx().evm_clone();
823        evm.ctx_mut().set_evm(evm_env);
824    }
825
826    fn make_signed_eip1559() -> Signed<TxEip1559> {
827        Signed::new_unchecked(
828            TxEip1559 {
829                chain_id: 1,
830                nonce: 42,
831                gas_limit: 21001,
832                to: TxKind::Call(Address::with_last_byte(0xBB)),
833                value: U256::from(101),
834                ..Default::default()
835            },
836            Signature::new(U256::ZERO, U256::ZERO, false),
837            B256::ZERO,
838        )
839    }
840
841    #[test]
842    fn from_any_rpc_transaction_for_eth() {
843        let from = Address::random();
844        let signed_tx = make_signed_eip1559();
845        let rpc_tx = RpcTransaction::from_transaction(
846            Recovered::new_unchecked(signed_tx.into(), from),
847            TransactionInfo::default(),
848        );
849
850        let any_tx = <AnyRpcTransaction as From<RpcTransaction>>::from(rpc_tx);
851        let tx_env = TxEnv::from_any_rpc_transaction(&any_tx).unwrap();
852
853        assert_eq!(tx_env.caller, from);
854        assert_eq!(tx_env.nonce, 42);
855        assert_eq!(tx_env.gas_limit, 21001);
856        assert_eq!(tx_env.value, U256::from(101));
857        assert_eq!(tx_env.kind, TxKind::Call(Address::with_last_byte(0xBB)));
858    }
859
860    #[test]
861    fn from_any_rpc_transaction_unknown_envelope_errors() {
862        let unknown = AnyTxEnvelope::Unknown(UnknownTxEnvelope {
863            hash: B256::ZERO,
864            inner: UnknownTypedTransaction {
865                ty: AnyTxType(0xFF),
866                fields: Default::default(),
867                memo: Default::default(),
868            },
869        });
870        let from = Address::random();
871        let any_tx = AnyRpcTransaction::new(WithOtherFields::new(RpcTransaction {
872            inner: Recovered::new_unchecked(unknown, from),
873            block_hash: None,
874            block_number: None,
875            transaction_index: None,
876            effective_gas_price: None,
877            block_timestamp: None,
878        }));
879
880        let result = TxEnv::from_any_rpc_transaction(&any_tx).unwrap_err();
881        assert!(result.to_string().contains("unknown transaction type"));
882    }
883
884    #[test]
885    fn from_any_rpc_transaction_for_tempo_eth_envelope() {
886        let from = Address::random();
887        let signed_tx = make_signed_eip1559();
888        let rpc_tx = RpcTransaction::from_transaction(
889            Recovered::new_unchecked(signed_tx.into(), from),
890            TransactionInfo::default(),
891        );
892        let any_tx = <AnyRpcTransaction as From<RpcTransaction>>::from(rpc_tx);
893
894        let tx_env = TempoTxEnv::from_any_rpc_transaction(&any_tx).unwrap();
895        assert_eq!(tx_env.inner.caller, from);
896        assert_eq!(tx_env.inner.nonce, 42);
897        assert_eq!(tx_env.inner.gas_limit, 21001);
898        assert_eq!(tx_env.inner.value, U256::from(101));
899        assert_eq!(tx_env.fee_token, None);
900    }
901
902    #[test]
903    fn from_any_rpc_transaction_for_tempo_aa() {
904        let from = Address::random();
905        let fee_token = Some(Address::random());
906        let tempo_tx = TempoTransaction {
907            chain_id: 42431,
908            nonce: 42,
909            gas_limit: 424242,
910            fee_token,
911            nonce_key: U256::from(4242),
912            valid_after: NonZeroU64::new(1800000000),
913            ..Default::default()
914        };
915        let aa_signed = AASigned::new_unhashed(
916            tempo_tx,
917            TempoSignature::Primitive(PrimitiveSignature::Secp256k1(Signature::new(
918                U256::ZERO,
919                U256::ZERO,
920                false,
921            ))),
922        );
923
924        // Build a concrete Tempo RPC transaction, serialize to JSON, deserialize as
925        // AnyRpcTransaction.
926        let rpc_tx = RpcTransaction::from_transaction(
927            Recovered::new_unchecked(TempoTxEnvelope::AA(aa_signed), from),
928            TransactionInfo::default(),
929        );
930        let json = serde_json::to_value(&rpc_tx).unwrap();
931        let any_tx: AnyRpcTransaction = serde_json::from_value(json).unwrap();
932
933        let tx_env = TempoTxEnv::from_any_rpc_transaction(&any_tx).unwrap();
934        assert_eq!(tx_env.inner.caller, from);
935        assert_eq!(tx_env.inner.nonce, 42);
936        assert_eq!(tx_env.inner.gas_limit, 424242);
937        assert_eq!(tx_env.inner.chain_id, Some(42431));
938        assert_eq!(tx_env.fee_token, fee_token);
939    }
940
941    #[cfg(feature = "optimism")]
942    mod optimism {
943        use super::*;
944        use alloy_consensus::Sealed;
945        use alloy_op_evm::{OpEvmFactory, OpTx};
946        use op_alloy_consensus::{OpTxEnvelope, TxDeposit, transaction::OpTransactionInfo};
947        use op_alloy_rpc_types::Transaction as OpRpcTransaction;
948        use op_revm::OpSpecId;
949
950        #[test]
951        fn op_evm_foundry_context_ext_implementation() {
952            let mut evm =
953                OpEvmFactory::<OpTx>::default().create_evm(EmptyDB::default(), EvmEnv::default());
954
955            // Test EVM Context Block mutation
956            evm.ctx_mut().block_mut().set_number(U256::from(123));
957            assert_eq!(evm.ctx().block().number(), U256::from(123));
958
959            // Test EVM Context Tx mutation
960            evm.ctx_mut().tx_mut().set_nonce(99);
961            assert_eq!(evm.ctx().tx().nonce(), 99);
962
963            // Test EVM Context Cfg mutation
964            evm.ctx_mut().cfg_mut().spec = OpSpecId::JOVIAN;
965            assert_eq!(evm.ctx().cfg().spec, OpSpecId::JOVIAN);
966
967            // Round-trip test to ensure no issues with cloning and setting tx_env and evm_env
968            let tx_env = evm.ctx().tx_clone();
969            evm.ctx_mut().set_tx(tx_env);
970            let evm_env = evm.ctx().evm_clone();
971            evm.ctx_mut().set_evm(evm_env);
972        }
973
974        #[test]
975        fn from_any_rpc_transaction_for_op() {
976            let from = Address::random();
977            let signed_tx = make_signed_eip1559();
978
979            // Build the eth TxEnv to compare against op base
980            let rpc_tx = RpcTransaction::from_transaction(
981                Recovered::new_unchecked(signed_tx.into(), from),
982                TransactionInfo::default(),
983            );
984            let any_tx = <AnyRpcTransaction as From<RpcTransaction>>::from(rpc_tx);
985            let expected_base = TxEnv::from_any_rpc_transaction(&any_tx).unwrap();
986
987            let op_tx_env = OpTx::from_any_rpc_transaction(&any_tx).unwrap();
988            assert_eq!(op_tx_env.base, expected_base);
989        }
990
991        #[test]
992        fn from_any_rpc_transaction_for_op_deposit() {
993            let from = Address::random();
994            let source_hash = B256::random();
995            let deposit = TxDeposit {
996                source_hash,
997                from,
998                to: TxKind::Call(Address::with_last_byte(0xCC)),
999                mint: 1111,
1000                value: U256::from(200),
1001                gas_limit: 21000,
1002                is_system_transaction: true,
1003                input: Default::default(),
1004            };
1005
1006            // Build a concrete OpRpcTransaction, serialize to JSON, deserialize as
1007            // AnyRpcTransaction.
1008            let op_rpc_tx = OpRpcTransaction::from_transaction(
1009                Recovered::new_unchecked(OpTxEnvelope::Deposit(Sealed::new(deposit)), from),
1010                OpTransactionInfo::default(),
1011            );
1012            let json = serde_json::to_value(&op_rpc_tx).unwrap();
1013            let any_tx: AnyRpcTransaction = serde_json::from_value(json).unwrap();
1014
1015            let op_tx_env = OpTx::from_any_rpc_transaction(&any_tx).unwrap();
1016            assert_eq!(op_tx_env.base.caller, from);
1017            assert_eq!(op_tx_env.base.kind, TxKind::Call(Address::with_last_byte(0xCC)));
1018            assert_eq!(op_tx_env.base.value, U256::from(200));
1019            assert_eq!(op_tx_env.base.gas_limit, 21000);
1020            assert_eq!(op_tx_env.deposit.source_hash, source_hash);
1021            assert_eq!(op_tx_env.deposit.mint, Some(1111));
1022            assert!(op_tx_env.deposit.is_system_transaction);
1023        }
1024    }
1025}