Skip to main content

foundry_evm_core/
env.rs

1use crate::backend::JournaledState;
2use alloy_chains::NamedChain;
3use alloy_consensus::{Transaction as _, Typed2718};
4use alloy_evm::FromRecoveredTx;
5use alloy_network::{AnyRpcTransaction, AnyTxEnvelope, TransactionResponse};
6use alloy_primitives::{Address, B256, Bytes, U256};
7use foundry_evm_networks::celo::CELO_DYNAMIC_FEE_TX_TYPE;
8use revm::{
9    Context, Database, Journal,
10    context::{Block, BlockEnv, Cfg, CfgEnv, Transaction, TxEnv},
11    context_interface::{
12        ContextTr,
13        either::Either,
14        transaction::{AccessList, RecoveredAuthorization, SignedAuthorization},
15    },
16    inspector::JournalExt,
17    primitives::{TxKind, hardfork::SpecId},
18};
19use std::fmt::Debug;
20use tempo_revm::{TempoBlockEnv, TempoTxEnv};
21
22#[cfg(feature = "optimism")]
23use op_revm::transaction::deposit::DEPOSIT_TRANSACTION_TYPE;
24
25pub use alloy_evm::EvmEnv;
26
27/// Extension of [`Block`] with mutable setters, allowing EVM-agnostic mutation of block fields.
28pub trait FoundryBlock: Block {
29    /// Sets the block number.
30    fn set_number(&mut self, number: U256);
31
32    /// Sets the slot number.
33    fn set_slot_num(&mut self, slot_num: u64);
34
35    /// Sets the beneficiary (coinbase) address.
36    fn set_beneficiary(&mut self, beneficiary: Address);
37
38    /// Sets the block timestamp.
39    fn set_timestamp(&mut self, timestamp: U256);
40
41    /// Sets the gas limit.
42    fn set_gas_limit(&mut self, gas_limit: u64);
43
44    /// Sets the base fee per gas.
45    fn set_basefee(&mut self, basefee: u64);
46
47    /// Sets the block difficulty.
48    fn set_difficulty(&mut self, difficulty: U256);
49
50    /// Sets the prevrandao value.
51    fn set_prevrandao(&mut self, prevrandao: Option<B256>);
52
53    /// Sets the excess blob gas and blob gasprice.
54    fn set_blob_excess_gas_and_price(
55        &mut self,
56        _excess_blob_gas: u64,
57        _base_fee_update_fraction: u64,
58    );
59
60    // Tempo methods
61
62    /// Returns the milliseconds portion of the block timestamp.
63    fn timestamp_millis_part(&self) -> u64 {
64        0
65    }
66
67    /// Sets the milliseconds portion of the block timestamp.
68    fn set_timestamp_millis_part(&mut self, _millis: u64) {}
69}
70
71impl FoundryBlock for BlockEnv {
72    fn set_number(&mut self, number: U256) {
73        self.number = number;
74    }
75
76    fn set_slot_num(&mut self, slot_num: u64) {
77        self.slot_num = slot_num;
78    }
79
80    fn set_beneficiary(&mut self, beneficiary: Address) {
81        self.beneficiary = beneficiary;
82    }
83
84    fn set_timestamp(&mut self, timestamp: U256) {
85        self.timestamp = timestamp;
86    }
87
88    fn set_gas_limit(&mut self, gas_limit: u64) {
89        self.gas_limit = gas_limit;
90    }
91
92    fn set_basefee(&mut self, basefee: u64) {
93        self.basefee = basefee;
94    }
95
96    fn set_difficulty(&mut self, difficulty: U256) {
97        self.difficulty = difficulty;
98    }
99
100    fn set_prevrandao(&mut self, prevrandao: Option<B256>) {
101        self.prevrandao = prevrandao;
102    }
103
104    fn set_blob_excess_gas_and_price(
105        &mut self,
106        excess_blob_gas: u64,
107        base_fee_update_fraction: u64,
108    ) {
109        self.set_blob_excess_gas_and_price(excess_blob_gas, base_fee_update_fraction);
110    }
111}
112
113impl FoundryBlock for TempoBlockEnv {
114    fn set_number(&mut self, number: U256) {
115        self.inner.set_number(number);
116    }
117
118    fn set_slot_num(&mut self, slot_num: u64) {
119        self.inner.set_slot_num(slot_num);
120    }
121
122    fn set_beneficiary(&mut self, beneficiary: Address) {
123        self.inner.set_beneficiary(beneficiary);
124    }
125
126    fn set_timestamp(&mut self, timestamp: U256) {
127        self.inner.set_timestamp(timestamp);
128    }
129
130    fn set_gas_limit(&mut self, gas_limit: u64) {
131        self.inner.set_gas_limit(gas_limit);
132    }
133
134    fn set_basefee(&mut self, basefee: u64) {
135        self.inner.set_basefee(basefee);
136    }
137
138    fn set_difficulty(&mut self, difficulty: U256) {
139        self.inner.set_difficulty(difficulty);
140    }
141
142    fn set_prevrandao(&mut self, prevrandao: Option<B256>) {
143        self.inner.set_prevrandao(prevrandao);
144    }
145
146    fn set_blob_excess_gas_and_price(
147        &mut self,
148        _excess_blob_gas: u64,
149        _base_fee_update_fraction: u64,
150    ) {
151    }
152
153    fn timestamp_millis_part(&self) -> u64 {
154        self.timestamp_millis_part
155    }
156
157    fn set_timestamp_millis_part(&mut self, millis: u64) {
158        self.timestamp_millis_part = millis;
159    }
160}
161
162/// Extension of [`Transaction`] with mutable setters, allowing EVM-agnostic mutation of transaction
163/// fields.
164pub trait FoundryTransaction: Transaction {
165    /// Sets the transaction type.
166    fn set_tx_type(&mut self, tx_type: u8);
167
168    /// Sets the caller (sender) address.
169    fn set_caller(&mut self, caller: Address);
170
171    /// Sets the gas limit.
172    fn set_gas_limit(&mut self, gas_limit: u64);
173
174    /// Sets the gas price (or max fee per gas for EIP-1559).
175    fn set_gas_price(&mut self, gas_price: u128);
176
177    /// Sets the transaction kind (call or create).
178    fn set_kind(&mut self, kind: TxKind);
179
180    /// Sets the value sent with the transaction.
181    fn set_value(&mut self, value: U256);
182
183    /// Sets the transaction input data.
184    fn set_data(&mut self, data: Bytes);
185
186    /// Sets the nonce.
187    fn set_nonce(&mut self, nonce: u64);
188
189    /// Sets the chain ID.
190    fn set_chain_id(&mut self, chain_id: Option<u64>);
191
192    /// Sets the access list.
193    fn set_access_list(&mut self, access_list: AccessList);
194
195    /// Returns a mutable reference to the EIP-7702 authorization list.
196    fn authorization_list_mut(
197        &mut self,
198    ) -> &mut Vec<Either<SignedAuthorization, RecoveredAuthorization>>;
199
200    /// Sets the max priority fee per gas.
201    fn set_gas_priority_fee(&mut self, gas_priority_fee: Option<u128>);
202
203    /// Sets the blob versioned hashes.
204    fn set_blob_hashes(&mut self, blob_hashes: Vec<B256>);
205
206    /// Sets the max fee per blob gas.
207    fn set_max_fee_per_blob_gas(&mut self, max_fee_per_blob_gas: u128);
208
209    /// Sets the EIP-7702 signed authorization list.
210    fn set_signed_authorization(&mut self, auth: Vec<SignedAuthorization>) {
211        *self.authorization_list_mut() = auth.into_iter().map(Either::Left).collect();
212    }
213
214    // `OpTransaction` methods
215
216    /// Enveloped transaction bytes.
217    fn enveloped_tx(&self) -> Option<&Bytes> {
218        None
219    }
220
221    /// Set Enveloped transaction bytes.
222    fn set_enveloped_tx(&mut self, _bytes: Bytes) {}
223
224    /// Source hash of the deposit transaction.
225    fn source_hash(&self) -> Option<B256> {
226        None
227    }
228
229    /// Sets source hash of the deposit transaction.
230    fn set_source_hash(&mut self, _source_hash: B256) {}
231
232    /// Mint of the deposit transaction
233    fn mint(&self) -> Option<u128> {
234        None
235    }
236
237    /// Sets mint of the deposit transaction.
238    fn set_mint(&mut self, _mint: u128) {}
239
240    /// Whether the transaction is a system transaction
241    fn is_system_transaction(&self) -> bool {
242        false
243    }
244
245    /// Sets whether the transaction is a system transaction
246    fn set_system_transaction(&mut self, _is_system_transaction: bool) {}
247
248    /// Returns `true` if transaction is an Optimism deposit transaction.
249    fn is_deposit(&self) -> bool {
250        #[cfg(feature = "optimism")]
251        {
252            self.tx_type() == DEPOSIT_TRANSACTION_TYPE
253        }
254        #[cfg(not(feature = "optimism"))]
255        {
256            false
257        }
258    }
259
260    // Tempo methods
261
262    /// Returns the fee token address for this transaction.
263    fn fee_token(&self) -> Option<Address> {
264        None
265    }
266
267    /// Sets the fee token address for this transaction.
268    fn set_fee_token(&mut self, _token: Option<Address>) {}
269
270    /// Returns the fee payer for this transaction.
271    fn fee_payer(&self) -> Option<Option<Address>> {
272        None
273    }
274
275    /// Sets the fee payer for this transaction.
276    fn set_fee_payer(&mut self, _payer: Option<Option<Address>>) {}
277}
278
279impl FoundryTransaction for TxEnv {
280    fn set_tx_type(&mut self, tx_type: u8) {
281        self.tx_type = tx_type;
282    }
283
284    fn set_caller(&mut self, caller: Address) {
285        self.caller = caller;
286    }
287
288    fn set_gas_limit(&mut self, gas_limit: u64) {
289        self.gas_limit = gas_limit;
290    }
291
292    fn set_gas_price(&mut self, gas_price: u128) {
293        self.gas_price = gas_price;
294    }
295
296    fn set_kind(&mut self, kind: TxKind) {
297        self.kind = kind;
298    }
299
300    fn set_value(&mut self, value: U256) {
301        self.value = value;
302    }
303
304    fn set_data(&mut self, data: Bytes) {
305        self.data = data;
306    }
307
308    fn set_nonce(&mut self, nonce: u64) {
309        self.nonce = nonce;
310    }
311
312    fn set_chain_id(&mut self, chain_id: Option<u64>) {
313        self.chain_id = chain_id;
314    }
315
316    fn set_access_list(&mut self, access_list: AccessList) {
317        self.access_list = access_list;
318    }
319
320    fn authorization_list_mut(
321        &mut self,
322    ) -> &mut Vec<Either<SignedAuthorization, RecoveredAuthorization>> {
323        &mut self.authorization_list
324    }
325
326    fn set_gas_priority_fee(&mut self, gas_priority_fee: Option<u128>) {
327        self.gas_priority_fee = gas_priority_fee;
328    }
329
330    fn set_blob_hashes(&mut self, blob_hashes: Vec<B256>) {
331        self.blob_hashes = blob_hashes;
332    }
333
334    fn set_max_fee_per_blob_gas(&mut self, max_fee_per_blob_gas: u128) {
335        self.max_fee_per_blob_gas = max_fee_per_blob_gas;
336    }
337}
338
339impl FoundryTransaction for TempoTxEnv {
340    fn set_tx_type(&mut self, tx_type: u8) {
341        self.inner.set_tx_type(tx_type);
342    }
343
344    fn set_caller(&mut self, caller: Address) {
345        self.inner.set_caller(caller);
346    }
347
348    fn set_gas_limit(&mut self, gas_limit: u64) {
349        self.inner.set_gas_limit(gas_limit);
350    }
351
352    fn set_gas_price(&mut self, gas_price: u128) {
353        self.inner.set_gas_price(gas_price);
354    }
355
356    fn set_kind(&mut self, kind: TxKind) {
357        self.inner.set_kind(kind);
358        if let Some(call) =
359            self.tempo_tx_env.as_deref_mut().and_then(|env| env.aa_calls.first_mut())
360        {
361            call.to = kind;
362        }
363    }
364
365    fn set_value(&mut self, value: U256) {
366        self.inner.set_value(value);
367        if let Some(call) =
368            self.tempo_tx_env.as_deref_mut().and_then(|env| env.aa_calls.first_mut())
369        {
370            call.value = value;
371        }
372    }
373
374    fn set_data(&mut self, data: Bytes) {
375        self.inner.set_data(data.clone());
376        if let Some(call) =
377            self.tempo_tx_env.as_deref_mut().and_then(|env| env.aa_calls.first_mut())
378        {
379            call.input = data;
380        }
381    }
382
383    fn set_nonce(&mut self, nonce: u64) {
384        self.inner.set_nonce(nonce);
385    }
386
387    fn set_chain_id(&mut self, chain_id: Option<u64>) {
388        self.inner.set_chain_id(chain_id);
389    }
390
391    fn set_access_list(&mut self, access_list: AccessList) {
392        self.inner.set_access_list(access_list);
393    }
394
395    fn authorization_list_mut(
396        &mut self,
397    ) -> &mut Vec<Either<SignedAuthorization, RecoveredAuthorization>> {
398        self.inner.authorization_list_mut()
399    }
400
401    fn set_gas_priority_fee(&mut self, gas_priority_fee: Option<u128>) {
402        self.inner.set_gas_priority_fee(gas_priority_fee);
403    }
404
405    fn set_blob_hashes(&mut self, _blob_hashes: Vec<B256>) {}
406
407    fn set_max_fee_per_blob_gas(&mut self, _max_fee_per_blob_gas: u128) {}
408
409    fn fee_token(&self) -> Option<Address> {
410        self.fee_token
411    }
412
413    fn set_fee_token(&mut self, token: Option<Address>) {
414        self.fee_token = token;
415    }
416
417    fn fee_payer(&self) -> Option<Option<Address>> {
418        self.fee_payer
419    }
420
421    fn set_fee_payer(&mut self, payer: Option<Option<Address>>) {
422        self.fee_payer = payer;
423    }
424}
425
426/// Foundry extension for chain context type
427///
428/// Every family that doesn't need chain metadata uses `()`.
429pub trait FoundryChain<Tx>: Clone + Debug + Default + Send + Sync {
430    /// Builds chain context for a standalone synthetic transaction.
431    fn for_transaction(_tx: &Tx) -> Self {
432        Self::default()
433    }
434
435    /// Builds chain context for a transaction at an exact block position.
436    fn for_block(
437        _grandparent: &[Tx],
438        _parent: &[Tx],
439        _current: &[Tx],
440        _current_tx_index: usize,
441    ) -> Self {
442        Self::default()
443    }
444
445    /// Refreshes journal state derived from the active chain position.
446    fn refresh_journal<J: FoundryJournal>(&self, _journal: &mut J) {}
447}
448
449impl<Tx> FoundryChain<Tx> for () {}
450
451/// Access to a configuration's underlying environment and hardfork updates.
452pub trait FoundryCfg:
453    Cfg<Spec: Into<SpecId> + Copy + Debug> + Clone + From<CfgEnv<Self::Spec>> + Into<CfgEnv<Self::Spec>>
454{
455    /// Reference to the underlying configuration.
456    fn cfg_env(&self) -> &CfgEnv<Self::Spec>;
457
458    /// Mutable reference to the underlying configuration.
459    fn cfg_env_mut(&mut self) -> &mut CfgEnv<Self::Spec>;
460
461    /// Updates the hardfork and its gas parameters.
462    fn set_spec_and_gas_params(&mut self, spec: Self::Spec) {
463        self.cfg_env_mut().set_spec_and_mainnet_gas_params(spec);
464    }
465}
466
467impl<SPEC: Into<SpecId> + Copy + Debug> FoundryCfg for CfgEnv<SPEC> {
468    fn cfg_env(&self) -> &Self {
469        self
470    }
471
472    fn cfg_env_mut(&mut self) -> &mut Self {
473        self
474    }
475}
476
477#[cfg(feature = "monad")]
478impl FoundryCfg for monad_revm::MonadCfgEnv {
479    fn cfg_env(&self) -> &CfgEnv<Self::Spec> {
480        self.inner()
481    }
482
483    fn cfg_env_mut(&mut self) -> &mut CfgEnv<Self::Spec> {
484        self.inner_mut()
485    }
486
487    fn set_spec_and_gas_params(&mut self, spec: Self::Spec) {
488        self.inner_mut().spec = spec;
489        self.inner_mut().set_gas_params(monad_revm::instructions::monad_gas_params(spec));
490    }
491}
492
493/// Foundry extension for Journal type
494pub trait FoundryJournal: JournalExt {
495    /// Mutable access to the database and journal inner.
496    fn db_journal_inner_mut(&mut self) -> (&mut Self::Database, &mut JournaledState);
497
498    /// Reference to the journal inner.
499    fn journal_inner(&self) -> &JournaledState;
500
501    /// Captures Monad's reserve-balance tracker for the active transaction.
502    #[cfg(feature = "monad")]
503    fn capture_reserve_balance(
504        &self,
505    ) -> monad_revm::reserve_balance::tracker::ReserveBalanceTracker {
506        monad_revm::reserve_balance::tracker::ReserveBalanceTracker::default()
507    }
508
509    /// Restores Monad's reserve-balance tracker for the active transaction.
510    #[cfg(feature = "monad")]
511    fn restore_reserve_balance(
512        &mut self,
513        _tracker: monad_revm::reserve_balance::tracker::ReserveBalanceTracker,
514    ) {
515    }
516
517    /// Whether transaction boundaries currently preserve the reserve-balance tracker, e.g. for
518    /// an isolated call that models an inner call of the enclosing transaction rather than a
519    /// new one.
520    #[cfg(feature = "monad")]
521    fn preserves_reserve_balance(&self) -> bool {
522        false
523    }
524
525    /// Sets whether transaction boundaries preserve the reserve-balance tracker.
526    #[cfg(feature = "monad")]
527    fn set_preserve_reserve_balance(&mut self, _preserve: bool) {}
528}
529
530impl<DB: Database> FoundryJournal for Journal<DB> {
531    fn db_journal_inner_mut(&mut self) -> (&mut DB, &mut JournaledState) {
532        (&mut self.database, &mut self.inner)
533    }
534
535    fn journal_inner(&self) -> &JournaledState {
536        &self.inner
537    }
538}
539
540#[cfg(feature = "monad")]
541impl<DB: Database> FoundryJournal for monad_revm::MonadJournal<DB> {
542    fn db_journal_inner_mut(&mut self) -> (&mut DB, &mut JournaledState) {
543        Journal::db_journal_inner_mut(self)
544    }
545
546    fn journal_inner(&self) -> &JournaledState {
547        Journal::journal_inner(self)
548    }
549
550    fn capture_reserve_balance(
551        &self,
552    ) -> monad_revm::reserve_balance::tracker::ReserveBalanceTracker {
553        monad_revm::MonadJournalTr::reserve_balance(self).clone()
554    }
555
556    fn restore_reserve_balance(
557        &mut self,
558        tracker: monad_revm::reserve_balance::tracker::ReserveBalanceTracker,
559    ) {
560        *monad_revm::MonadJournalTr::reserve_balance_mut(self) = tracker;
561    }
562
563    fn preserves_reserve_balance(&self) -> bool {
564        monad_revm::MonadJournalTr::preserves_reserve_balance_tracker(self)
565    }
566
567    fn set_preserve_reserve_balance(&mut self, preserve: bool) {
568        monad_revm::MonadJournalTr::set_preserve_reserve_balance_tracker(self, preserve);
569    }
570}
571
572/// Extension trait providing mutable field access to block, tx, and cfg environments.
573///
574/// [`ContextTr`] only exposes immutable references for block, tx, and cfg.
575/// Cheatcodes like `vm.warp()`, `vm.roll()`, `vm.chainId()` need to mutate these fields.
576pub trait FoundryContextExt:
577    ContextTr<
578        Block: FoundryBlock + Clone,
579        Tx: FoundryTransaction + Clone,
580        Cfg: FoundryCfg<Spec = Self::Spec>,
581        Journal: FoundryJournal,
582        Chain: FoundryChain<Self::Tx>,
583    >
584{
585    /// Specification id type
586    ///
587    /// Bubbled-up from `ContextTr::Cfg` for convenience and simplified bounds.
588    type Spec: Into<SpecId> + Copy + Debug;
589
590    /// Mutable reference to the block environment.
591    fn block_mut(&mut self) -> &mut Self::Block;
592
593    /// Mutable reference to the transaction environment.
594    fn tx_mut(&mut self) -> &mut Self::Tx;
595
596    /// Mutable reference to the configuration environment.
597    fn cfg_mut(&mut self) -> &mut Self::Cfg;
598
599    /// Reference to the underlying [`CfgEnv`].
600    fn cfg_env(&self) -> &CfgEnv<Self::Spec> {
601        self.cfg().cfg_env()
602    }
603
604    /// Mutable reference to the underlying [`CfgEnv`].
605    fn cfg_env_mut(&mut self) -> &mut CfgEnv<Self::Spec> {
606        self.cfg_mut().cfg_env_mut()
607    }
608
609    /// Mutable reference to the db and the journal inner.
610    fn db_journal_inner_mut(&mut self) -> (&mut Self::Db, &mut JournaledState) {
611        self.journal_mut().db_journal_inner_mut()
612    }
613
614    /// Reference to the journal inner.
615    fn journal_inner(&self) -> &JournaledState {
616        self.journal().journal_inner()
617    }
618
619    /// Sets the spec and refreshes gas params for the concrete EVM family.
620    fn set_spec_and_gas_params(&mut self, spec: Self::Spec) {
621        self.cfg_mut().set_spec_and_gas_params(spec);
622    }
623
624    /// Sets block environment.
625    fn set_block(&mut self, block: Self::Block) {
626        *self.block_mut() = block;
627    }
628
629    /// Sets transaction environment.
630    fn set_tx(&mut self, tx: Self::Tx) {
631        *self.tx_mut() = tx;
632    }
633
634    /// Sets configuration environment.
635    fn set_cfg(&mut self, cfg: Self::Cfg) {
636        *self.cfg_mut() = cfg;
637    }
638
639    /// Sets journal inner.
640    fn set_journal_inner(&mut self, journal_inner: JournaledState) {
641        *self.db_journal_inner_mut().1 = journal_inner;
642    }
643
644    /// Sets EVM environment.
645    fn set_evm(&mut self, evm_env: EvmEnv<Self::Spec, Self::Block>) {
646        *self.cfg_mut() = evm_env.cfg_env.into();
647        *self.block_mut() = evm_env.block_env;
648    }
649
650    /// Cloned transaction environment.
651    fn tx_clone(&self) -> Self::Tx {
652        self.tx().clone()
653    }
654
655    /// Cloned EVM environment (Cfg + Block).
656    fn evm_clone(&self) -> EvmEnv<Self::Spec, Self::Block> {
657        EvmEnv::new(self.cfg().clone().into(), self.block().clone())
658    }
659}
660
661/// Refreshes journal state derived from a context's active chain position.
662pub fn refresh_chain_journal<CTX: FoundryContextExt>(context: &mut CTX) {
663    let chain = context.chain().clone();
664    chain.refresh_journal(context.journal_mut());
665}
666
667impl<
668    BLOCK: FoundryBlock + Clone,
669    TX: FoundryTransaction + Clone,
670    CFG: FoundryCfg,
671    DB: Database,
672    J: FoundryJournal<Database = DB>,
673    C: FoundryChain<TX>,
674> FoundryContextExt for Context<BLOCK, TX, CFG, DB, J, C>
675{
676    type Spec = <Self::Cfg as Cfg>::Spec;
677
678    fn block_mut(&mut self) -> &mut Self::Block {
679        &mut self.block
680    }
681
682    fn tx_mut(&mut self) -> &mut Self::Tx {
683        &mut self.tx
684    }
685
686    fn cfg_mut(&mut self) -> &mut Self::Cfg {
687        &mut self.cfg
688    }
689}
690
691/// Trait for converting an [`AnyRpcTransaction`] into a specific `TxEnv`.
692///
693/// Ethereum envelopes delegate to [`FromRecoveredTx`]. Implementations may also explicitly
694/// project compatible network-specific envelopes into their execution environment.
695pub trait FromAnyRpcTransaction: Sized {
696    /// Tries to convert an [`AnyRpcTransaction`] into `Self`.
697    fn from_any_rpc_transaction(tx: &AnyRpcTransaction) -> eyre::Result<Self>;
698}
699
700impl FromAnyRpcTransaction for TxEnv {
701    fn from_any_rpc_transaction(tx: &AnyRpcTransaction) -> eyre::Result<Self> {
702        if let Some(envelope) = tx.as_envelope() {
703            return Ok(Self::from_recovered_tx(envelope, tx.from()));
704        }
705
706        // CIP-64 transactions have EIP-1559 execution fields plus a Celo-specific fee currency.
707        // Foundry does not model fee payment in TxEnv, but can replay their EVM payload. Preserve
708        // the custom type so revm does not compare the fee-currency price with the native-CELO
709        // base fee. Keep this projection restricted to active Celo chains so an unrelated network
710        // cannot silently acquire semantics for its own type 0x7b envelope.
711        if let AnyTxEnvelope::Unknown(unknown) = &*tx.inner.inner
712            && unknown.ty() == CELO_DYNAMIC_FEE_TX_TYPE
713            && matches!(
714                unknown.chain_id().and_then(NamedChain::from_chain_id),
715                Some(NamedChain::Celo | NamedChain::CeloSepolia)
716            )
717        {
718            return Ok(Self {
719                tx_type: CELO_DYNAMIC_FEE_TX_TYPE,
720                caller: tx.from(),
721                gas_limit: unknown.gas_limit(),
722                gas_price: unknown.max_fee_per_gas(),
723                gas_priority_fee: unknown.max_priority_fee_per_gas(),
724                kind: unknown.kind(),
725                value: unknown.value(),
726                data: unknown.input().clone(),
727                nonce: unknown.nonce(),
728                chain_id: unknown.chain_id(),
729                access_list: unknown.access_list().cloned().unwrap_or_default(),
730                ..Default::default()
731            });
732        }
733
734        eyre::bail!("cannot convert unknown transaction type to TxEnv");
735    }
736}
737
738impl FromAnyRpcTransaction for TempoTxEnv {
739    fn from_any_rpc_transaction(tx: &AnyRpcTransaction) -> eyre::Result<Self> {
740        if let Some(envelope) = tx.as_envelope() {
741            return Ok(TxEnv::from_recovered_tx(envelope, tx.from()).into());
742        }
743
744        // Handle Tempo transactions from `Unknown` envelope variant.
745        if let AnyTxEnvelope::Unknown(unknown) = &*tx.inner.inner
746            && unknown.ty() == tempo_alloy::primitives::TEMPO_TX_TYPE_ID
747        {
748            let base = TxEnv {
749                tx_type: unknown.ty(),
750                caller: tx.from(),
751                gas_limit: unknown.gas_limit(),
752                gas_price: unknown.max_fee_per_gas(),
753                gas_priority_fee: unknown.max_priority_fee_per_gas(),
754                kind: unknown.kind(),
755                value: unknown.value(),
756                data: unknown.input().clone(),
757                nonce: unknown.nonce(),
758                chain_id: unknown.chain_id(),
759                access_list: unknown.access_list().cloned().unwrap_or_default(),
760                ..Default::default()
761            };
762            let fee_token =
763                unknown.inner.fields.get_deserialized::<Address>("feeToken").and_then(Result::ok);
764            return Ok(Self { inner: base, fee_token, ..Default::default() });
765        }
766
767        eyre::bail!("cannot convert unknown transaction type to TempoTxEnv");
768    }
769}
770
771#[cfg(feature = "base")]
772mod base {
773    use super::*;
774    use base_common_consensus::BaseTxEnvelope;
775    use base_common_evm::{
776        BaseTransaction, BaseTxTr, DEPOSIT_TRANSACTION_TYPE, EIP8130_TRANSACTION_TYPE,
777    };
778    use base_common_rpc_types::Transaction as BaseRpcTransaction;
779
780    impl<TX: FoundryTransaction> FoundryTransaction for BaseTransaction<TX> {
781        fn set_tx_type(&mut self, tx_type: u8) {
782            self.base.set_tx_type(tx_type);
783        }
784
785        fn set_caller(&mut self, caller: Address) {
786            self.base.set_caller(caller);
787        }
788
789        fn set_gas_limit(&mut self, gas_limit: u64) {
790            self.base.set_gas_limit(gas_limit);
791        }
792
793        fn set_gas_price(&mut self, gas_price: u128) {
794            self.base.set_gas_price(gas_price);
795        }
796
797        fn set_kind(&mut self, kind: TxKind) {
798            self.base.set_kind(kind);
799        }
800
801        fn set_value(&mut self, value: U256) {
802            self.base.set_value(value);
803        }
804
805        fn set_data(&mut self, data: Bytes) {
806            self.base.set_data(data);
807        }
808
809        fn set_nonce(&mut self, nonce: u64) {
810            self.base.set_nonce(nonce);
811        }
812
813        fn set_chain_id(&mut self, chain_id: Option<u64>) {
814            self.base.set_chain_id(chain_id);
815        }
816
817        fn set_access_list(&mut self, access_list: AccessList) {
818            self.base.set_access_list(access_list);
819        }
820
821        fn authorization_list_mut(
822            &mut self,
823        ) -> &mut Vec<Either<SignedAuthorization, RecoveredAuthorization>> {
824            self.base.authorization_list_mut()
825        }
826
827        fn set_gas_priority_fee(&mut self, gas_priority_fee: Option<u128>) {
828            self.base.set_gas_priority_fee(gas_priority_fee);
829        }
830
831        fn set_blob_hashes(&mut self, blob_hashes: Vec<B256>) {
832            self.base.set_blob_hashes(blob_hashes);
833        }
834
835        fn set_max_fee_per_blob_gas(&mut self, max_fee_per_blob_gas: u128) {
836            self.base.set_max_fee_per_blob_gas(max_fee_per_blob_gas);
837        }
838
839        fn enveloped_tx(&self) -> Option<&Bytes> {
840            BaseTxTr::enveloped_tx(self)
841        }
842
843        fn set_enveloped_tx(&mut self, bytes: Bytes) {
844            self.enveloped_tx = Some(bytes);
845        }
846
847        fn source_hash(&self) -> Option<B256> {
848            BaseTxTr::source_hash(self)
849        }
850
851        fn set_source_hash(&mut self, source_hash: B256) {
852            self.deposit.source_hash = source_hash;
853        }
854
855        fn mint(&self) -> Option<u128> {
856            BaseTxTr::mint(self)
857        }
858
859        fn set_mint(&mut self, mint: u128) {
860            self.deposit.mint = Some(mint);
861        }
862
863        fn is_system_transaction(&self) -> bool {
864            BaseTxTr::is_system_transaction(self)
865        }
866
867        fn set_system_transaction(&mut self, is_system_transaction: bool) {
868            self.deposit.is_system_transaction = is_system_transaction;
869        }
870
871        fn is_deposit(&self) -> bool {
872            self.tx_type() == DEPOSIT_TRANSACTION_TYPE
873        }
874    }
875
876    impl FromAnyRpcTransaction for BaseTransaction<TxEnv> {
877        fn from_any_rpc_transaction(tx: &AnyRpcTransaction) -> eyre::Result<Self> {
878            let envelope = match BaseTxEnvelope::try_from(tx.clone()) {
879                Ok(envelope) => envelope,
880                Err(_) if tx.ty() == EIP8130_TRANSACTION_TYPE => {
881                    let rpc_tx =
882                        serde_json::from_value::<BaseRpcTransaction>(serde_json::to_value(tx)?)
883                            .map_err(|err| {
884                                eyre::eyre!(
885                                    "cannot convert RPC transaction to Base envelope: {err}"
886                                )
887                            })?;
888                    rpc_tx.inner.into_inner()
889                }
890                Err(_) => eyre::bail!("cannot convert transaction to BaseTxEnvelope"),
891            };
892            Ok(Self::from_recovered_tx(&envelope, tx.from()))
893        }
894    }
895}
896
897#[cfg(feature = "optimism")]
898mod optimism {
899    use super::*;
900    use alloy_eips::eip2718::Encodable2718;
901    use alloy_op_evm::OpTx;
902    use op_alloy_consensus::{DEPOSIT_TX_TYPE_ID, TxDeposit};
903    use op_revm::{OpTransaction, transaction::OpTxTr};
904
905    impl<TX: FoundryTransaction> FoundryTransaction for OpTransaction<TX> {
906        fn set_tx_type(&mut self, tx_type: u8) {
907            self.base.set_tx_type(tx_type);
908        }
909
910        fn set_caller(&mut self, caller: Address) {
911            self.base.set_caller(caller);
912        }
913
914        fn set_gas_limit(&mut self, gas_limit: u64) {
915            self.base.set_gas_limit(gas_limit);
916        }
917
918        fn set_gas_price(&mut self, gas_price: u128) {
919            self.base.set_gas_price(gas_price);
920        }
921
922        fn set_kind(&mut self, kind: TxKind) {
923            self.base.set_kind(kind);
924        }
925
926        fn set_value(&mut self, value: U256) {
927            self.base.set_value(value);
928        }
929
930        fn set_data(&mut self, data: Bytes) {
931            self.base.set_data(data);
932        }
933
934        fn set_nonce(&mut self, nonce: u64) {
935            self.base.set_nonce(nonce);
936        }
937
938        fn set_chain_id(&mut self, chain_id: Option<u64>) {
939            self.base.set_chain_id(chain_id);
940        }
941
942        fn set_access_list(&mut self, access_list: AccessList) {
943            self.base.set_access_list(access_list);
944        }
945
946        fn authorization_list_mut(
947            &mut self,
948        ) -> &mut Vec<Either<SignedAuthorization, RecoveredAuthorization>> {
949            self.base.authorization_list_mut()
950        }
951
952        fn set_gas_priority_fee(&mut self, gas_priority_fee: Option<u128>) {
953            self.base.set_gas_priority_fee(gas_priority_fee);
954        }
955
956        fn set_blob_hashes(&mut self, _blob_hashes: Vec<B256>) {}
957
958        fn set_max_fee_per_blob_gas(&mut self, _max_fee_per_blob_gas: u128) {}
959
960        fn enveloped_tx(&self) -> Option<&Bytes> {
961            OpTxTr::enveloped_tx(self)
962        }
963
964        fn set_enveloped_tx(&mut self, bytes: Bytes) {
965            self.enveloped_tx = Some(bytes);
966        }
967
968        fn source_hash(&self) -> Option<B256> {
969            OpTxTr::source_hash(self)
970        }
971
972        fn set_source_hash(&mut self, source_hash: B256) {
973            if self.tx_type() == DEPOSIT_TRANSACTION_TYPE {
974                self.deposit.source_hash = source_hash;
975            }
976        }
977
978        fn mint(&self) -> Option<u128> {
979            OpTxTr::mint(self)
980        }
981
982        fn set_mint(&mut self, mint: u128) {
983            if self.tx_type() == DEPOSIT_TRANSACTION_TYPE {
984                self.deposit.mint = Some(mint);
985            }
986        }
987
988        fn is_system_transaction(&self) -> bool {
989            OpTxTr::is_system_transaction(self)
990        }
991
992        fn set_system_transaction(&mut self, is_system_transaction: bool) {
993            if self.tx_type() == DEPOSIT_TRANSACTION_TYPE {
994                self.deposit.is_system_transaction = is_system_transaction;
995            }
996        }
997    }
998
999    impl FoundryTransaction for OpTx {
1000        fn set_tx_type(&mut self, tx_type: u8) {
1001            self.0.set_tx_type(tx_type);
1002        }
1003
1004        fn set_caller(&mut self, caller: Address) {
1005            self.0.set_caller(caller);
1006        }
1007
1008        fn set_gas_limit(&mut self, gas_limit: u64) {
1009            self.0.set_gas_limit(gas_limit);
1010        }
1011
1012        fn set_gas_price(&mut self, gas_price: u128) {
1013            self.0.set_gas_price(gas_price);
1014        }
1015
1016        fn set_kind(&mut self, kind: TxKind) {
1017            self.0.set_kind(kind);
1018        }
1019
1020        fn set_value(&mut self, value: U256) {
1021            self.0.set_value(value);
1022        }
1023
1024        fn set_data(&mut self, data: Bytes) {
1025            self.0.set_data(data);
1026        }
1027
1028        fn set_nonce(&mut self, nonce: u64) {
1029            self.0.set_nonce(nonce);
1030        }
1031
1032        fn set_chain_id(&mut self, chain_id: Option<u64>) {
1033            self.0.set_chain_id(chain_id);
1034        }
1035
1036        fn set_access_list(&mut self, access_list: AccessList) {
1037            self.0.set_access_list(access_list);
1038        }
1039
1040        fn authorization_list_mut(
1041            &mut self,
1042        ) -> &mut Vec<Either<SignedAuthorization, RecoveredAuthorization>> {
1043            self.0.authorization_list_mut()
1044        }
1045
1046        fn set_gas_priority_fee(&mut self, gas_priority_fee: Option<u128>) {
1047            self.0.set_gas_priority_fee(gas_priority_fee);
1048        }
1049
1050        fn set_blob_hashes(&mut self, _blob_hashes: Vec<B256>) {}
1051
1052        fn set_max_fee_per_blob_gas(&mut self, _max_fee_per_blob_gas: u128) {}
1053
1054        fn enveloped_tx(&self) -> Option<&Bytes> {
1055            FoundryTransaction::enveloped_tx(&self.0)
1056        }
1057
1058        fn set_enveloped_tx(&mut self, bytes: Bytes) {
1059            self.0.set_enveloped_tx(bytes);
1060        }
1061
1062        fn source_hash(&self) -> Option<B256> {
1063            FoundryTransaction::source_hash(&self.0)
1064        }
1065
1066        fn set_source_hash(&mut self, source_hash: B256) {
1067            self.0.set_source_hash(source_hash);
1068        }
1069
1070        fn mint(&self) -> Option<u128> {
1071            FoundryTransaction::mint(&self.0)
1072        }
1073
1074        fn set_mint(&mut self, mint: u128) {
1075            self.0.set_mint(mint);
1076        }
1077
1078        fn is_system_transaction(&self) -> bool {
1079            FoundryTransaction::is_system_transaction(&self.0)
1080        }
1081
1082        fn set_system_transaction(&mut self, is_system_transaction: bool) {
1083            self.0.set_system_transaction(is_system_transaction);
1084        }
1085    }
1086
1087    impl FromAnyRpcTransaction for OpTx {
1088        fn from_any_rpc_transaction(tx: &AnyRpcTransaction) -> eyre::Result<Self> {
1089            if let Some(envelope) = tx.as_envelope() {
1090                return Ok(Self(OpTransaction::<TxEnv> {
1091                    base: TxEnv::from_recovered_tx(envelope, tx.from()),
1092                    // The L1 data fee is charged off these bytes, and op-revm rejects a
1093                    // non-deposit transaction that arrives without them.
1094                    enveloped_tx: Some(envelope.encoded_2718().into()),
1095                    deposit: Default::default(),
1096                }));
1097            }
1098
1099            // Handle OP deposit transactions from `Unknown` envelope variant.
1100            if let AnyTxEnvelope::Unknown(unknown) = &*tx.inner.inner
1101                && unknown.ty() == DEPOSIT_TX_TYPE_ID
1102            {
1103                let mut fields = unknown.inner.fields.clone();
1104                fields.insert("from".to_string(), serde_json::to_value(tx.from())?);
1105                let deposit_tx: TxDeposit = fields
1106                    .deserialize_into()
1107                    .map_err(|e| eyre::eyre!("failed to deserialize deposit tx: {e}"))?;
1108                return Ok(Self::from_recovered_tx(&deposit_tx, deposit_tx.from));
1109            }
1110
1111            eyre::bail!("cannot convert unknown transaction type to OpTransaction");
1112        }
1113    }
1114}
1115
1116#[cfg(test)]
1117mod tests {
1118    use super::*;
1119    use alloy_consensus::{Signed, TxEip1559, transaction::Recovered};
1120    use alloy_evm::{EthEvmFactory, EvmFactory};
1121    use alloy_network::{AnyTxType, UnknownTxEnvelope, UnknownTypedTransaction};
1122    use alloy_primitives::Signature;
1123    use alloy_rpc_types::{Transaction as RpcTransaction, TransactionInfo};
1124    use alloy_serde::WithOtherFields;
1125    use foundry_evm_hardforks::TempoHardfork;
1126    use revm::database::EmptyDB;
1127    use std::num::NonZeroU64;
1128    use tempo_alloy::primitives::{
1129        AASigned, TempoSignature, TempoTransaction, TempoTxEnvelope,
1130        transaction::{Call, PrimitiveSignature},
1131    };
1132    use tempo_evm::TempoEvmFactory;
1133
1134    #[cfg(feature = "base")]
1135    use base_common_evm::{BaseEvmFactory, BaseSpecId, BaseTransaction, BaseUpgrade};
1136
1137    #[test]
1138    fn eth_evm_foundry_context_ext_implementation() {
1139        let mut evm = EthEvmFactory::default().create_evm(EmptyDB::default(), EvmEnv::default());
1140
1141        // Test EVM Context Block mutation
1142        evm.ctx_mut().block_mut().set_number(U256::from(123));
1143        assert_eq!(evm.ctx().block().number(), U256::from(123));
1144
1145        // Test EVM Context Tx mutation
1146        evm.ctx_mut().tx_mut().set_nonce(99);
1147        assert_eq!(evm.ctx().tx().nonce(), 99);
1148
1149        // Test EVM Context Cfg mutation
1150        evm.ctx_mut().cfg_mut().spec = SpecId::AMSTERDAM;
1151        assert_eq!(evm.ctx().cfg().spec, SpecId::AMSTERDAM);
1152
1153        // Round-trip test to ensure no issues with cloning and setting tx_env and evm_env
1154        let tx_env = evm.ctx().tx_clone();
1155        evm.ctx_mut().set_tx(tx_env);
1156        let evm_env = evm.ctx().evm_clone();
1157        evm.ctx_mut().set_evm(evm_env);
1158    }
1159
1160    #[cfg(feature = "base")]
1161    #[test]
1162    fn base_evm_foundry_context_ext_implementation() {
1163        let mut evm = BaseEvmFactory::default().create_evm(EmptyDB::default(), EvmEnv::default());
1164
1165        evm.ctx_mut().block_mut().set_number(U256::from(123));
1166        assert_eq!(evm.ctx().block().number(), U256::from(123));
1167
1168        evm.ctx_mut().tx_mut().set_nonce(99);
1169        assert_eq!(evm.ctx().tx().nonce(), 99);
1170
1171        evm.ctx_mut().cfg_mut().spec = BaseSpecId::new(BaseUpgrade::Beryl);
1172        assert_eq!(evm.ctx().cfg().spec, BaseSpecId::new(BaseUpgrade::Beryl));
1173
1174        let tx_env = evm.ctx().tx_clone();
1175        evm.ctx_mut().set_tx(tx_env);
1176        let evm_env = evm.ctx().evm_clone();
1177        evm.ctx_mut().set_evm(evm_env);
1178    }
1179
1180    #[test]
1181    #[cfg(feature = "monad")]
1182    fn monad_evm_foundry_context_ext_implementation() {
1183        let mut evm = alloy_monad_evm::MonadEvmFactory::default().create_evm(
1184            EmptyDB::default(),
1185            EvmEnv::new(
1186                CfgEnv::new_with_spec(monad_revm::MonadHardfork::MonadNine),
1187                BlockEnv::default(),
1188            ),
1189        );
1190
1191        // Test EVM Context Block mutation
1192        evm.ctx_mut().block_mut().set_number(U256::from(123));
1193        assert_eq!(evm.ctx().block().number(), U256::from(123));
1194
1195        // Test EVM Context Tx mutation
1196        evm.ctx_mut().tx_mut().set_nonce(99);
1197        assert_eq!(evm.ctx().tx().nonce(), 99);
1198
1199        // Test EVM Context Cfg mutation
1200        evm.ctx_mut().cfg_mut().spec = monad_revm::MonadHardfork::MonadEight;
1201        assert_eq!(evm.ctx().cfg().spec, monad_revm::MonadHardfork::MonadEight);
1202
1203        // Round-trip test to ensure no issues with cloning and setting tx_env and evm_env
1204        let tx_env = evm.ctx().tx_clone();
1205        evm.ctx_mut().set_tx(tx_env);
1206        let evm_env = evm.ctx().evm_clone();
1207        evm.ctx_mut().set_evm(evm_env);
1208        evm.ctx_mut().journal_mut().set_preserve_reserve_balance(true);
1209        let mut inner = evm.ctx().journal_inner().clone();
1210        inner.depth = 2;
1211        evm.ctx_mut().set_journal_inner(inner);
1212        assert_eq!(evm.ctx().journal_inner().depth, 2);
1213        assert!(evm.ctx().journal().preserves_reserve_balance());
1214    }
1215
1216    #[test]
1217    #[cfg(feature = "monad")]
1218    fn monad_memory_limit_follows_hardfork_transitions() {
1219        const FOUNDRY_MEMORY_LIMIT: u64 = 128 * 1024 * 1024;
1220
1221        let mut cfg = CfgEnv::new_with_spec(monad_revm::MonadHardfork::MonadEight);
1222        cfg.memory_limit = FOUNDRY_MEMORY_LIMIT;
1223        let mut evm = alloy_monad_evm::MonadEvmFactory::default()
1224            .create_evm(EmptyDB::default(), EvmEnv::new(cfg, BlockEnv::default()));
1225
1226        assert_eq!(evm.ctx().cfg().memory_limit(), FOUNDRY_MEMORY_LIMIT);
1227
1228        evm.ctx_mut().set_spec_and_gas_params(monad_revm::MonadHardfork::MonadNine);
1229        assert_eq!(evm.ctx().cfg().inner().memory_limit, FOUNDRY_MEMORY_LIMIT);
1230        assert_eq!(evm.ctx().cfg().memory_limit(), monad_revm::cfg::MONAD_MEMORY_LIMIT);
1231        assert_eq!(
1232            evm.ctx().cfg().inner().gas_params,
1233            monad_revm::instructions::monad_gas_params(monad_revm::MonadHardfork::MonadNine)
1234        );
1235
1236        evm.ctx_mut().set_spec_and_gas_params(monad_revm::MonadHardfork::MonadEight);
1237        assert_eq!(evm.ctx().cfg().memory_limit(), FOUNDRY_MEMORY_LIMIT);
1238        assert_eq!(
1239            evm.ctx().cfg().inner().gas_params,
1240            monad_revm::instructions::monad_gas_params(monad_revm::MonadHardfork::MonadEight)
1241        );
1242    }
1243
1244    #[test]
1245    fn tempo_evm_foundry_context_ext_implementation() {
1246        let mut evm = TempoEvmFactory::default().create_evm(EmptyDB::default(), EvmEnv::default());
1247
1248        // Test EVM Context Block mutation
1249        evm.ctx_mut().block_mut().set_number(U256::from(123));
1250        assert_eq!(evm.ctx().block().number(), U256::from(123));
1251
1252        // Test EVM Context Tx mutation
1253        evm.ctx_mut().tx_mut().set_nonce(99);
1254        assert_eq!(evm.ctx().tx().nonce(), 99);
1255
1256        // Test EVM Context Cfg mutation
1257        evm.ctx_mut().cfg_mut().spec = TempoHardfork::Genesis;
1258        assert_eq!(evm.ctx().cfg().spec, TempoHardfork::Genesis);
1259
1260        // Round-trip test to ensure no issues with cloning and setting tx_env and evm_env
1261        let tx_env = evm.ctx().tx_clone();
1262        evm.ctx_mut().set_tx(tx_env);
1263        let evm_env = evm.ctx().evm_clone();
1264        evm.ctx_mut().set_evm(evm_env);
1265    }
1266
1267    #[test]
1268    fn tempo_tx_env_setters_update_aa_call_payload() {
1269        let old_to = TxKind::Call(Address::with_last_byte(0xAA));
1270        let new_to = TxKind::Create;
1271        let new_value = U256::from(123);
1272        let new_input = Bytes::from_static(b"local bytecode");
1273
1274        let mut tx_env = TempoTxEnv {
1275            inner: TxEnv {
1276                kind: old_to,
1277                value: U256::from(1),
1278                data: Bytes::from_static(b"original bytecode"),
1279                ..Default::default()
1280            },
1281            tempo_tx_env: Some(Box::new(tempo_revm::TempoBatchCallEnv {
1282                aa_calls: vec![Call {
1283                    to: old_to,
1284                    value: U256::from(1),
1285                    input: Bytes::from_static(b"original bytecode"),
1286                }],
1287                ..Default::default()
1288            })),
1289            ..Default::default()
1290        };
1291
1292        tx_env.set_kind(new_to);
1293        tx_env.set_value(new_value);
1294        tx_env.set_data(new_input.clone());
1295
1296        assert_eq!(tx_env.inner.kind, new_to);
1297        assert_eq!(tx_env.inner.value, new_value);
1298        assert_eq!(tx_env.inner.data, new_input);
1299
1300        let call = &tx_env.tempo_tx_env.as_ref().unwrap().aa_calls[0];
1301        assert_eq!(call.to, new_to);
1302        assert_eq!(call.value, new_value);
1303        assert_eq!(call.input, new_input);
1304    }
1305
1306    fn make_signed_eip1559() -> Signed<TxEip1559> {
1307        Signed::new_unchecked(
1308            TxEip1559 {
1309                chain_id: 1,
1310                nonce: 42,
1311                gas_limit: 21001,
1312                to: TxKind::Call(Address::with_last_byte(0xBB)),
1313                value: U256::from(101),
1314                ..Default::default()
1315            },
1316            Signature::new(U256::ZERO, U256::ZERO, false),
1317            B256::ZERO,
1318        )
1319    }
1320
1321    #[test]
1322    fn from_any_rpc_transaction_for_eth() {
1323        let from = Address::random();
1324        let signed_tx = make_signed_eip1559();
1325        let rpc_tx = RpcTransaction::from_transaction(
1326            Recovered::new_unchecked(signed_tx.into(), from),
1327            TransactionInfo::default(),
1328        );
1329
1330        let any_tx = <AnyRpcTransaction as From<RpcTransaction>>::from(rpc_tx);
1331        let tx_env = TxEnv::from_any_rpc_transaction(&any_tx).unwrap();
1332
1333        assert_eq!(tx_env.caller, from);
1334        assert_eq!(tx_env.nonce, 42);
1335        assert_eq!(tx_env.gas_limit, 21001);
1336        assert_eq!(tx_env.value, U256::from(101));
1337        assert_eq!(tx_env.kind, TxKind::Call(Address::with_last_byte(0xBB)));
1338    }
1339
1340    #[cfg(feature = "base")]
1341    #[test]
1342    fn from_any_rpc_transaction_for_base_eth_envelope() {
1343        let from = Address::random();
1344        let signed_tx = make_signed_eip1559();
1345        let rpc_tx = RpcTransaction::from_transaction(
1346            Recovered::new_unchecked(signed_tx.into(), from),
1347            TransactionInfo::default(),
1348        );
1349        let any_tx = <AnyRpcTransaction as From<RpcTransaction>>::from(rpc_tx);
1350
1351        let tx_env = BaseTransaction::<TxEnv>::from_any_rpc_transaction(&any_tx).unwrap();
1352        assert_eq!(tx_env.base.caller, from);
1353        assert_eq!(tx_env.base.nonce, 42);
1354        assert_eq!(tx_env.base.gas_limit, 21001);
1355        assert_eq!(tx_env.base.value, U256::from(101));
1356        assert!(tx_env.enveloped_tx.is_some());
1357    }
1358
1359    #[test]
1360    fn from_any_rpc_transaction_unknown_envelope_errors() {
1361        let unknown = AnyTxEnvelope::Unknown(UnknownTxEnvelope {
1362            hash: B256::ZERO,
1363            inner: UnknownTypedTransaction {
1364                ty: AnyTxType(0xFF),
1365                fields: Default::default(),
1366                memo: Default::default(),
1367            },
1368        });
1369        let from = Address::random();
1370        let any_tx = AnyRpcTransaction::new(WithOtherFields::new(RpcTransaction {
1371            inner: Recovered::new_unchecked(unknown, from),
1372            block_hash: None,
1373            block_number: None,
1374            transaction_index: None,
1375            effective_gas_price: None,
1376            block_timestamp: None,
1377        }));
1378
1379        let result = TxEnv::from_any_rpc_transaction(&any_tx).unwrap_err();
1380        assert!(result.to_string().contains("unknown transaction type"));
1381    }
1382
1383    #[test]
1384    fn from_any_rpc_transaction_for_celo_dynamic_fee() {
1385        let from = Address::with_last_byte(0xAA);
1386        let to = Address::with_last_byte(0xBB);
1387        let fee_currency = Address::with_last_byte(0xCC);
1388        let json = serde_json::json!({
1389            "accessList": [],
1390            "blockHash": B256::ZERO,
1391            "blockNumber": "0x1",
1392            "chainId": "0xa4ec",
1393            "feeCurrency": fee_currency,
1394            "from": from,
1395            "gas": "0x5208",
1396            "gasPrice": "0x3",
1397            "hash": B256::ZERO,
1398            "input": "0x1234",
1399            "maxFeePerGas": "0x3",
1400            "maxPriorityFeePerGas": "0x1",
1401            "nonce": "0x2a",
1402            "r": B256::ZERO,
1403            "s": B256::ZERO,
1404            "to": to,
1405            "transactionIndex": "0x0",
1406            "type": "0x7b",
1407            "v": "0x0",
1408            "value": "0x65",
1409            "yParity": "0x0"
1410        });
1411        let mut non_celo_json = json.clone();
1412        non_celo_json["chainId"] = serde_json::json!("0x1");
1413        let non_celo_tx: AnyRpcTransaction = serde_json::from_value(non_celo_json).unwrap();
1414        assert!(TxEnv::from_any_rpc_transaction(&non_celo_tx).is_err());
1415
1416        let any_tx: AnyRpcTransaction = serde_json::from_value(json).unwrap();
1417
1418        let tx_env = TxEnv::from_any_rpc_transaction(&any_tx).unwrap();
1419
1420        assert_eq!(tx_env.tx_type, CELO_DYNAMIC_FEE_TX_TYPE);
1421        assert_eq!(tx_env.caller, from);
1422        assert_eq!(tx_env.nonce, 42);
1423        assert_eq!(tx_env.gas_limit, 21000);
1424        assert_eq!(tx_env.gas_price, 3);
1425        assert_eq!(tx_env.gas_priority_fee, Some(1));
1426        assert_eq!(tx_env.kind, TxKind::Call(to));
1427        assert_eq!(tx_env.value, U256::from(101));
1428        assert_eq!(tx_env.data, Bytes::from_static(&[0x12, 0x34]));
1429        assert_eq!(tx_env.chain_id, Some(42_220));
1430    }
1431
1432    #[test]
1433    fn from_any_rpc_transaction_for_tempo_eth_envelope() {
1434        let from = Address::random();
1435        let signed_tx = make_signed_eip1559();
1436        let rpc_tx = RpcTransaction::from_transaction(
1437            Recovered::new_unchecked(signed_tx.into(), from),
1438            TransactionInfo::default(),
1439        );
1440        let any_tx = <AnyRpcTransaction as From<RpcTransaction>>::from(rpc_tx);
1441
1442        let tx_env = TempoTxEnv::from_any_rpc_transaction(&any_tx).unwrap();
1443        assert_eq!(tx_env.inner.caller, from);
1444        assert_eq!(tx_env.inner.nonce, 42);
1445        assert_eq!(tx_env.inner.gas_limit, 21001);
1446        assert_eq!(tx_env.inner.value, U256::from(101));
1447        assert_eq!(tx_env.fee_token, None);
1448    }
1449
1450    #[test]
1451    fn from_any_rpc_transaction_for_tempo_aa() {
1452        let from = Address::random();
1453        let fee_token = Some(Address::random());
1454        let tempo_tx = TempoTransaction {
1455            chain_id: 42431,
1456            nonce: 42,
1457            gas_limit: 424242,
1458            fee_token,
1459            nonce_key: U256::from(4242),
1460            valid_after: NonZeroU64::new(1800000000),
1461            ..Default::default()
1462        };
1463        let aa_signed = AASigned::new_unhashed(
1464            tempo_tx,
1465            TempoSignature::Primitive(PrimitiveSignature::Secp256k1(Signature::new(
1466                U256::ZERO,
1467                U256::ZERO,
1468                false,
1469            ))),
1470        );
1471
1472        // Build a concrete Tempo RPC transaction, serialize to JSON, deserialize as
1473        // AnyRpcTransaction.
1474        let rpc_tx = RpcTransaction::from_transaction(
1475            Recovered::new_unchecked(TempoTxEnvelope::AA(aa_signed), from),
1476            TransactionInfo::default(),
1477        );
1478        let json = serde_json::to_value(&rpc_tx).unwrap();
1479        let any_tx: AnyRpcTransaction = serde_json::from_value(json).unwrap();
1480
1481        let tx_env = TempoTxEnv::from_any_rpc_transaction(&any_tx).unwrap();
1482        assert_eq!(tx_env.inner.caller, from);
1483        assert_eq!(tx_env.inner.nonce, 42);
1484        assert_eq!(tx_env.inner.gas_limit, 424242);
1485        assert_eq!(tx_env.inner.chain_id, Some(42431));
1486        assert_eq!(tx_env.fee_token, fee_token);
1487    }
1488
1489    #[cfg(feature = "optimism")]
1490    mod optimism {
1491        use super::*;
1492        use alloy_consensus::Sealed;
1493        use alloy_eips::eip2718::Encodable2718;
1494        use alloy_op_evm::{OpEvmFactory, OpTx};
1495        use op_alloy_consensus::{OpTxEnvelope, TxDeposit, transaction::OpTransactionInfo};
1496        use op_alloy_rpc_types::Transaction as OpRpcTransaction;
1497        use op_revm::OpSpecId;
1498
1499        #[test]
1500        fn op_evm_foundry_context_ext_implementation() {
1501            let mut evm =
1502                OpEvmFactory::<OpTx>::default().create_evm(EmptyDB::default(), EvmEnv::default());
1503
1504            // Test EVM Context Block mutation
1505            evm.ctx_mut().block_mut().set_number(U256::from(123));
1506            assert_eq!(evm.ctx().block().number(), U256::from(123));
1507
1508            // Test EVM Context Tx mutation
1509            evm.ctx_mut().tx_mut().set_nonce(99);
1510            assert_eq!(evm.ctx().tx().nonce(), 99);
1511
1512            // Test EVM Context Cfg mutation
1513            evm.ctx_mut().cfg_mut().spec = OpSpecId::JOVIAN;
1514            assert_eq!(evm.ctx().cfg().spec, OpSpecId::JOVIAN);
1515
1516            // Round-trip test to ensure no issues with cloning and setting tx_env and evm_env
1517            let tx_env = evm.ctx().tx_clone();
1518            evm.ctx_mut().set_tx(tx_env);
1519            let evm_env = evm.ctx().evm_clone();
1520            evm.ctx_mut().set_evm(evm_env);
1521        }
1522
1523        #[test]
1524        fn from_any_rpc_transaction_for_op() {
1525            let from = Address::random();
1526            let signed_tx = make_signed_eip1559();
1527
1528            // Build the eth TxEnv to compare against op base
1529            let rpc_tx = RpcTransaction::from_transaction(
1530                Recovered::new_unchecked(signed_tx.into(), from),
1531                TransactionInfo::default(),
1532            );
1533            let any_tx = <AnyRpcTransaction as From<RpcTransaction>>::from(rpc_tx);
1534            let expected_base = TxEnv::from_any_rpc_transaction(&any_tx).unwrap();
1535
1536            let op_tx_env = OpTx::from_any_rpc_transaction(&any_tx).unwrap();
1537            assert_eq!(op_tx_env.base, expected_base);
1538            // op-revm charges the L1 data fee off these bytes and rejects a non-deposit
1539            // transaction that arrives without them.
1540            assert_eq!(
1541                op_tx_env.enveloped_tx,
1542                Some(any_tx.as_envelope().unwrap().encoded_2718().into())
1543            );
1544        }
1545
1546        #[test]
1547        fn from_any_rpc_transaction_for_op_deposit() {
1548            let from = Address::random();
1549            let source_hash = B256::random();
1550            let deposit = TxDeposit {
1551                source_hash,
1552                from,
1553                to: TxKind::Call(Address::with_last_byte(0xCC)),
1554                mint: 1111,
1555                value: U256::from(200),
1556                gas_limit: 21000,
1557                is_system_transaction: true,
1558                input: Default::default(),
1559            };
1560
1561            // Build a concrete OpRpcTransaction, serialize to JSON, deserialize as
1562            // AnyRpcTransaction.
1563            let op_rpc_tx = OpRpcTransaction::from_transaction(
1564                Recovered::new_unchecked(OpTxEnvelope::Deposit(Sealed::new(deposit)), from),
1565                OpTransactionInfo::default(),
1566            );
1567            let json = serde_json::to_value(&op_rpc_tx).unwrap();
1568            let any_tx: AnyRpcTransaction = serde_json::from_value(json).unwrap();
1569
1570            let op_tx_env = OpTx::from_any_rpc_transaction(&any_tx).unwrap();
1571            assert_eq!(op_tx_env.base.caller, from);
1572            assert_eq!(op_tx_env.base.kind, TxKind::Call(Address::with_last_byte(0xCC)));
1573            assert_eq!(op_tx_env.base.value, U256::from(200));
1574            assert_eq!(op_tx_env.base.gas_limit, 21000);
1575            assert_eq!(op_tx_env.deposit.source_hash, source_hash);
1576            assert_eq!(op_tx_env.deposit.mint, Some(1111));
1577            assert!(op_tx_env.deposit.is_system_transaction);
1578        }
1579    }
1580}