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        if let Some(call) =
346            self.tempo_tx_env.as_deref_mut().and_then(|env| env.aa_calls.first_mut())
347        {
348            call.to = kind;
349        }
350    }
351
352    fn set_value(&mut self, value: U256) {
353        self.inner.set_value(value);
354        if let Some(call) =
355            self.tempo_tx_env.as_deref_mut().and_then(|env| env.aa_calls.first_mut())
356        {
357            call.value = value;
358        }
359    }
360
361    fn set_data(&mut self, data: Bytes) {
362        self.inner.set_data(data.clone());
363        if let Some(call) =
364            self.tempo_tx_env.as_deref_mut().and_then(|env| env.aa_calls.first_mut())
365        {
366            call.input = data;
367        }
368    }
369
370    fn set_nonce(&mut self, nonce: u64) {
371        self.inner.set_nonce(nonce);
372    }
373
374    fn set_chain_id(&mut self, chain_id: Option<u64>) {
375        self.inner.set_chain_id(chain_id);
376    }
377
378    fn set_access_list(&mut self, access_list: AccessList) {
379        self.inner.set_access_list(access_list);
380    }
381
382    fn authorization_list_mut(
383        &mut self,
384    ) -> &mut Vec<Either<SignedAuthorization, RecoveredAuthorization>> {
385        self.inner.authorization_list_mut()
386    }
387
388    fn set_gas_priority_fee(&mut self, gas_priority_fee: Option<u128>) {
389        self.inner.set_gas_priority_fee(gas_priority_fee);
390    }
391
392    fn set_blob_hashes(&mut self, _blob_hashes: Vec<B256>) {}
393
394    fn set_max_fee_per_blob_gas(&mut self, _max_fee_per_blob_gas: u128) {}
395
396    fn fee_token(&self) -> Option<Address> {
397        self.fee_token
398    }
399
400    fn set_fee_token(&mut self, token: Option<Address>) {
401        self.fee_token = token;
402    }
403
404    fn fee_payer(&self) -> Option<Option<Address>> {
405        self.fee_payer
406    }
407
408    fn set_fee_payer(&mut self, payer: Option<Option<Address>>) {
409        self.fee_payer = payer;
410    }
411}
412
413/// Foundry extension for chain context type
414///
415/// Every family that doesn't need chain metadata uses `()`.
416pub trait FoundryChain<Tx>: Clone + Debug + Default + Send + Sync {
417    /// Builds chain context for a standalone synthetic transaction.
418    fn for_transaction(_tx: &Tx) -> Self {
419        Self::default()
420    }
421
422    /// Builds chain context for a transaction at an exact block position.
423    fn for_block(
424        _grandparent: &[Tx],
425        _parent: &[Tx],
426        _current: &[Tx],
427        _current_tx_index: usize,
428    ) -> Self {
429        Self::default()
430    }
431
432    /// Refreshes journal state derived from the active chain position.
433    fn refresh_journal<J: FoundryJournal>(&self, _journal: &mut J) {}
434}
435
436impl<Tx> FoundryChain<Tx> for () {}
437
438/// Foundry extension for Journal type
439pub trait FoundryJournal: JournalExt {
440    /// Captures Monad's reserve-balance tracker for the active transaction.
441    #[cfg(feature = "monad")]
442    fn capture_reserve_balance(
443        &self,
444    ) -> monad_revm::reserve_balance::tracker::ReserveBalanceTracker {
445        monad_revm::reserve_balance::tracker::ReserveBalanceTracker::default()
446    }
447
448    /// Restores Monad's reserve-balance tracker for the active transaction.
449    #[cfg(feature = "monad")]
450    fn restore_reserve_balance(
451        &mut self,
452        _tracker: monad_revm::reserve_balance::tracker::ReserveBalanceTracker,
453    ) {
454    }
455
456    /// Whether transaction boundaries currently preserve the reserve-balance tracker, e.g. for
457    /// an isolated call that models an inner call of the enclosing transaction rather than a
458    /// new one.
459    #[cfg(feature = "monad")]
460    fn preserves_reserve_balance(&self) -> bool {
461        false
462    }
463
464    /// Sets whether transaction boundaries preserve the reserve-balance tracker.
465    #[cfg(feature = "monad")]
466    fn set_preserve_reserve_balance(&mut self, _preserve: bool) {}
467}
468
469impl<DB: Database> FoundryJournal for Journal<DB> {}
470
471#[cfg(feature = "monad")]
472impl<DB: Database> FoundryJournal for monad_revm::MonadJournal<DB> {
473    fn capture_reserve_balance(
474        &self,
475    ) -> monad_revm::reserve_balance::tracker::ReserveBalanceTracker {
476        monad_revm::MonadJournalTr::reserve_balance(self).clone()
477    }
478
479    fn restore_reserve_balance(
480        &mut self,
481        tracker: monad_revm::reserve_balance::tracker::ReserveBalanceTracker,
482    ) {
483        *monad_revm::MonadJournalTr::reserve_balance_mut(self) = tracker;
484    }
485
486    fn preserves_reserve_balance(&self) -> bool {
487        monad_revm::MonadJournalTr::preserves_reserve_balance_tracker(self)
488    }
489
490    fn set_preserve_reserve_balance(&mut self, preserve: bool) {
491        monad_revm::MonadJournalTr::set_preserve_reserve_balance_tracker(self, preserve);
492    }
493}
494
495/// Extension trait providing mutable field access to block, tx, and cfg environments.
496///
497/// [`ContextTr`] only exposes immutable references for block, tx, and cfg.
498/// Cheatcodes like `vm.warp()`, `vm.roll()`, `vm.chainId()` need to mutate these fields.
499pub trait FoundryContextExt:
500    ContextTr<
501        Block: FoundryBlock + Clone,
502        Tx: FoundryTransaction + Clone,
503        Cfg: Cfg<Spec = Self::Spec> + Clone + From<CfgEnv<Self::Spec>> + Into<CfgEnv<Self::Spec>>,
504        Journal: FoundryJournal,
505        Chain: FoundryChain<Self::Tx>,
506    >
507{
508    /// Specification id type
509    ///
510    /// Bubbled-up from `ContextTr::Cfg` for convenience and simplified bounds.
511    type Spec: Into<SpecId> + Copy + Debug;
512
513    /// Mutable reference to the block environment.
514    fn block_mut(&mut self) -> &mut Self::Block;
515
516    /// Mutable reference to the transaction environment.
517    fn tx_mut(&mut self) -> &mut Self::Tx;
518
519    /// Mutable reference to the configuration environment.
520    fn cfg_mut(&mut self) -> &mut Self::Cfg;
521
522    /// Reference to the underlying [`CfgEnv`].
523    fn cfg_env(&self) -> &CfgEnv<Self::Spec>;
524
525    /// Mutable reference to the underlying [`CfgEnv`].
526    fn cfg_env_mut(&mut self) -> &mut CfgEnv<Self::Spec>;
527
528    /// Mutable reference to the db and the journal inner.
529    fn db_journal_inner_mut(&mut self) -> (&mut Self::Db, &mut JournaledState);
530
531    /// Reference to the journal inner.
532    fn journal_inner(&self) -> &JournaledState;
533
534    /// Sets the spec and refreshes gas params for the concrete EVM family.
535    fn set_spec_and_gas_params(&mut self, spec: Self::Spec) {
536        self.cfg_env_mut().set_spec_and_mainnet_gas_params(spec);
537    }
538
539    /// Sets block environment.
540    fn set_block(&mut self, block: Self::Block) {
541        *self.block_mut() = block;
542    }
543
544    /// Sets transaction environment.
545    fn set_tx(&mut self, tx: Self::Tx) {
546        *self.tx_mut() = tx;
547    }
548
549    /// Sets configuration environment.
550    fn set_cfg(&mut self, cfg: Self::Cfg) {
551        *self.cfg_mut() = cfg;
552    }
553
554    /// Sets journal inner.
555    fn set_journal_inner(&mut self, journal_inner: JournaledState) {
556        *self.db_journal_inner_mut().1 = journal_inner;
557    }
558
559    /// Sets EVM environment.
560    fn set_evm(&mut self, evm_env: EvmEnv<Self::Spec, Self::Block>) {
561        *self.cfg_mut() = evm_env.cfg_env.into();
562        *self.block_mut() = evm_env.block_env;
563    }
564
565    /// Cloned transaction environment.
566    fn tx_clone(&self) -> Self::Tx {
567        self.tx().clone()
568    }
569
570    /// Cloned EVM environment (Cfg + Block).
571    fn evm_clone(&self) -> EvmEnv<Self::Spec, Self::Block> {
572        EvmEnv::new(self.cfg().clone().into(), self.block().clone())
573    }
574}
575
576/// Refreshes journal state derived from a context's active chain position.
577pub fn refresh_chain_journal<CTX: FoundryContextExt>(context: &mut CTX) {
578    let chain = context.chain().clone();
579    chain.refresh_journal(context.journal_mut());
580}
581
582impl<
583    BLOCK: FoundryBlock + Clone,
584    TX: FoundryTransaction + Clone,
585    SPEC: Into<SpecId> + Copy + Debug,
586    DB: Database,
587    C: FoundryChain<TX>,
588> FoundryContextExt for Context<BLOCK, TX, CfgEnv<SPEC>, DB, Journal<DB>, C>
589{
590    type Spec = <Self::Cfg as Cfg>::Spec;
591    fn block_mut(&mut self) -> &mut Self::Block {
592        &mut self.block
593    }
594
595    fn tx_mut(&mut self) -> &mut Self::Tx {
596        &mut self.tx
597    }
598
599    fn cfg_mut(&mut self) -> &mut Self::Cfg {
600        &mut self.cfg
601    }
602
603    fn cfg_env(&self) -> &CfgEnv<Self::Spec> {
604        &self.cfg
605    }
606
607    fn cfg_env_mut(&mut self) -> &mut CfgEnv<Self::Spec> {
608        &mut self.cfg
609    }
610
611    fn db_journal_inner_mut(&mut self) -> (&mut Self::Db, &mut JournaledState) {
612        (&mut self.journaled_state.database, &mut self.journaled_state.inner)
613    }
614
615    fn journal_inner(&self) -> &JournaledState {
616        &self.journaled_state.inner
617    }
618}
619
620#[cfg(feature = "monad")]
621impl<DB: Database> FoundryContextExt
622    for Context<
623        BlockEnv,
624        TxEnv,
625        monad_revm::MonadCfgEnv,
626        DB,
627        monad_revm::MonadJournal<DB>,
628        monad_revm::MonadChainContext,
629    >
630{
631    type Spec = <Self::Cfg as Cfg>::Spec;
632    fn block_mut(&mut self) -> &mut Self::Block {
633        &mut self.block
634    }
635
636    fn tx_mut(&mut self) -> &mut Self::Tx {
637        &mut self.tx
638    }
639
640    fn cfg_mut(&mut self) -> &mut Self::Cfg {
641        &mut self.cfg
642    }
643
644    fn cfg_env(&self) -> &CfgEnv<Self::Spec> {
645        self.cfg.inner()
646    }
647
648    fn cfg_env_mut(&mut self) -> &mut CfgEnv<Self::Spec> {
649        self.cfg.inner_mut()
650    }
651
652    fn set_spec_and_gas_params(&mut self, spec: Self::Spec) {
653        let mut cfg = self.cfg.clone().into_inner();
654        cfg.spec = spec;
655        self.cfg = monad_revm::MonadCfgEnv::from(cfg);
656    }
657
658    fn db_journal_inner_mut(&mut self) -> (&mut Self::Db, &mut JournaledState) {
659        let journal: &mut Journal<DB> = std::ops::DerefMut::deref_mut(&mut self.journaled_state);
660        (&mut journal.database, &mut journal.inner)
661    }
662
663    fn journal_inner(&self) -> &JournaledState {
664        let journal: &Journal<DB> = std::ops::Deref::deref(&self.journaled_state);
665        &journal.inner
666    }
667}
668
669/// Trait for converting an [`AnyRpcTransaction`] into a specific `TxEnv`.
670///
671/// Implementations extract the inner [`alloy_consensus::TxEnvelope`] via
672/// [`as_envelope()`](alloy_network::AnyTxEnvelope::as_envelope) then delegate to
673/// [`FromRecoveredTx`].
674pub trait FromAnyRpcTransaction: Sized {
675    /// Tries to convert an [`AnyRpcTransaction`] into `Self`.
676    fn from_any_rpc_transaction(tx: &AnyRpcTransaction) -> eyre::Result<Self>;
677}
678
679impl FromAnyRpcTransaction for TxEnv {
680    fn from_any_rpc_transaction(tx: &AnyRpcTransaction) -> eyre::Result<Self> {
681        if let Some(envelope) = tx.as_envelope() {
682            Ok(Self::from_recovered_tx(envelope, tx.from()))
683        } else {
684            eyre::bail!("cannot convert unknown transaction type to TxEnv");
685        }
686    }
687}
688
689impl FromAnyRpcTransaction for TempoTxEnv {
690    fn from_any_rpc_transaction(tx: &AnyRpcTransaction) -> eyre::Result<Self> {
691        use alloy_consensus::Transaction as _;
692        if let Some(envelope) = tx.as_envelope() {
693            return Ok(TxEnv::from_recovered_tx(envelope, tx.from()).into());
694        }
695
696        // Handle Tempo transactions from `Unknown` envelope variant.
697        if let AnyTxEnvelope::Unknown(unknown) = &*tx.inner.inner
698            && unknown.ty() == tempo_alloy::primitives::TEMPO_TX_TYPE_ID
699        {
700            let base = TxEnv {
701                tx_type: unknown.ty(),
702                caller: tx.from(),
703                gas_limit: unknown.gas_limit(),
704                gas_price: unknown.max_fee_per_gas(),
705                gas_priority_fee: unknown.max_priority_fee_per_gas(),
706                kind: unknown.kind(),
707                value: unknown.value(),
708                data: unknown.input().clone(),
709                nonce: unknown.nonce(),
710                chain_id: unknown.chain_id(),
711                access_list: unknown.access_list().cloned().unwrap_or_default(),
712                ..Default::default()
713            };
714            let fee_token =
715                unknown.inner.fields.get_deserialized::<Address>("feeToken").and_then(Result::ok);
716            return Ok(Self { inner: base, fee_token, ..Default::default() });
717        }
718
719        eyre::bail!("cannot convert unknown transaction type to TempoTxEnv");
720    }
721}
722
723#[cfg(feature = "optimism")]
724mod optimism {
725    use super::*;
726    use alloy_op_evm::OpTx;
727    use op_alloy_consensus::{DEPOSIT_TX_TYPE_ID, TxDeposit};
728    use op_revm::{OpTransaction, transaction::OpTxTr};
729
730    impl<TX: FoundryTransaction> FoundryTransaction for OpTransaction<TX> {
731        fn set_tx_type(&mut self, tx_type: u8) {
732            self.base.set_tx_type(tx_type);
733        }
734
735        fn set_caller(&mut self, caller: Address) {
736            self.base.set_caller(caller);
737        }
738
739        fn set_gas_limit(&mut self, gas_limit: u64) {
740            self.base.set_gas_limit(gas_limit);
741        }
742
743        fn set_gas_price(&mut self, gas_price: u128) {
744            self.base.set_gas_price(gas_price);
745        }
746
747        fn set_kind(&mut self, kind: TxKind) {
748            self.base.set_kind(kind);
749        }
750
751        fn set_value(&mut self, value: U256) {
752            self.base.set_value(value);
753        }
754
755        fn set_data(&mut self, data: Bytes) {
756            self.base.set_data(data);
757        }
758
759        fn set_nonce(&mut self, nonce: u64) {
760            self.base.set_nonce(nonce);
761        }
762
763        fn set_chain_id(&mut self, chain_id: Option<u64>) {
764            self.base.set_chain_id(chain_id);
765        }
766
767        fn set_access_list(&mut self, access_list: AccessList) {
768            self.base.set_access_list(access_list);
769        }
770
771        fn authorization_list_mut(
772            &mut self,
773        ) -> &mut Vec<Either<SignedAuthorization, RecoveredAuthorization>> {
774            self.base.authorization_list_mut()
775        }
776
777        fn set_gas_priority_fee(&mut self, gas_priority_fee: Option<u128>) {
778            self.base.set_gas_priority_fee(gas_priority_fee);
779        }
780
781        fn set_blob_hashes(&mut self, _blob_hashes: Vec<B256>) {}
782
783        fn set_max_fee_per_blob_gas(&mut self, _max_fee_per_blob_gas: u128) {}
784
785        fn enveloped_tx(&self) -> Option<&Bytes> {
786            OpTxTr::enveloped_tx(self)
787        }
788
789        fn set_enveloped_tx(&mut self, bytes: Bytes) {
790            self.enveloped_tx = Some(bytes);
791        }
792
793        fn source_hash(&self) -> Option<B256> {
794            OpTxTr::source_hash(self)
795        }
796
797        fn set_source_hash(&mut self, source_hash: B256) {
798            if self.tx_type() == DEPOSIT_TRANSACTION_TYPE {
799                self.deposit.source_hash = source_hash;
800            }
801        }
802
803        fn mint(&self) -> Option<u128> {
804            OpTxTr::mint(self)
805        }
806
807        fn set_mint(&mut self, mint: u128) {
808            if self.tx_type() == DEPOSIT_TRANSACTION_TYPE {
809                self.deposit.mint = Some(mint);
810            }
811        }
812
813        fn is_system_transaction(&self) -> bool {
814            OpTxTr::is_system_transaction(self)
815        }
816
817        fn set_system_transaction(&mut self, is_system_transaction: bool) {
818            if self.tx_type() == DEPOSIT_TRANSACTION_TYPE {
819                self.deposit.is_system_transaction = is_system_transaction;
820            }
821        }
822    }
823
824    impl FoundryTransaction for OpTx {
825        fn set_tx_type(&mut self, tx_type: u8) {
826            self.0.set_tx_type(tx_type);
827        }
828
829        fn set_caller(&mut self, caller: Address) {
830            self.0.set_caller(caller);
831        }
832
833        fn set_gas_limit(&mut self, gas_limit: u64) {
834            self.0.set_gas_limit(gas_limit);
835        }
836
837        fn set_gas_price(&mut self, gas_price: u128) {
838            self.0.set_gas_price(gas_price);
839        }
840
841        fn set_kind(&mut self, kind: TxKind) {
842            self.0.set_kind(kind);
843        }
844
845        fn set_value(&mut self, value: U256) {
846            self.0.set_value(value);
847        }
848
849        fn set_data(&mut self, data: Bytes) {
850            self.0.set_data(data);
851        }
852
853        fn set_nonce(&mut self, nonce: u64) {
854            self.0.set_nonce(nonce);
855        }
856
857        fn set_chain_id(&mut self, chain_id: Option<u64>) {
858            self.0.set_chain_id(chain_id);
859        }
860
861        fn set_access_list(&mut self, access_list: AccessList) {
862            self.0.set_access_list(access_list);
863        }
864
865        fn authorization_list_mut(
866            &mut self,
867        ) -> &mut Vec<Either<SignedAuthorization, RecoveredAuthorization>> {
868            self.0.authorization_list_mut()
869        }
870
871        fn set_gas_priority_fee(&mut self, gas_priority_fee: Option<u128>) {
872            self.0.set_gas_priority_fee(gas_priority_fee);
873        }
874
875        fn set_blob_hashes(&mut self, _blob_hashes: Vec<B256>) {}
876
877        fn set_max_fee_per_blob_gas(&mut self, _max_fee_per_blob_gas: u128) {}
878
879        fn enveloped_tx(&self) -> Option<&Bytes> {
880            FoundryTransaction::enveloped_tx(&self.0)
881        }
882
883        fn set_enveloped_tx(&mut self, bytes: Bytes) {
884            self.0.set_enveloped_tx(bytes);
885        }
886
887        fn source_hash(&self) -> Option<B256> {
888            FoundryTransaction::source_hash(&self.0)
889        }
890
891        fn set_source_hash(&mut self, source_hash: B256) {
892            self.0.set_source_hash(source_hash);
893        }
894
895        fn mint(&self) -> Option<u128> {
896            FoundryTransaction::mint(&self.0)
897        }
898
899        fn set_mint(&mut self, mint: u128) {
900            self.0.set_mint(mint);
901        }
902
903        fn is_system_transaction(&self) -> bool {
904            FoundryTransaction::is_system_transaction(&self.0)
905        }
906
907        fn set_system_transaction(&mut self, is_system_transaction: bool) {
908            self.0.set_system_transaction(is_system_transaction);
909        }
910    }
911
912    impl FromAnyRpcTransaction for OpTx {
913        fn from_any_rpc_transaction(tx: &AnyRpcTransaction) -> eyre::Result<Self> {
914            if let Some(envelope) = tx.as_envelope() {
915                return Ok(Self(OpTransaction::<TxEnv> {
916                    base: TxEnv::from_recovered_tx(envelope, tx.from()),
917                    enveloped_tx: None,
918                    deposit: Default::default(),
919                }));
920            }
921
922            // Handle OP deposit transactions from `Unknown` envelope variant.
923            if let AnyTxEnvelope::Unknown(unknown) = &*tx.inner.inner
924                && unknown.ty() == DEPOSIT_TX_TYPE_ID
925            {
926                let mut fields = unknown.inner.fields.clone();
927                fields.insert("from".to_string(), serde_json::to_value(tx.from())?);
928                let deposit_tx: TxDeposit = fields
929                    .deserialize_into()
930                    .map_err(|e| eyre::eyre!("failed to deserialize deposit tx: {e}"))?;
931                return Ok(Self::from_recovered_tx(&deposit_tx, deposit_tx.from));
932            }
933
934            eyre::bail!("cannot convert unknown transaction type to OpTransaction");
935        }
936    }
937}
938
939#[cfg(test)]
940mod tests {
941    use std::num::NonZeroU64;
942
943    use super::*;
944    use alloy_consensus::{Signed, TxEip1559, transaction::Recovered};
945    use alloy_evm::{EthEvmFactory, EvmFactory};
946    use alloy_network::{AnyTxType, UnknownTxEnvelope, UnknownTypedTransaction};
947    use alloy_primitives::Signature;
948    use alloy_rpc_types::{Transaction as RpcTransaction, TransactionInfo};
949    use alloy_serde::WithOtherFields;
950    use foundry_evm_hardforks::TempoHardfork;
951    use revm::database::EmptyDB;
952    use tempo_alloy::primitives::{
953        AASigned, TempoSignature, TempoTransaction, TempoTxEnvelope,
954        transaction::{Call, PrimitiveSignature},
955    };
956    use tempo_evm::TempoEvmFactory;
957
958    #[test]
959    fn eth_evm_foundry_context_ext_implementation() {
960        let mut evm = EthEvmFactory::default().create_evm(EmptyDB::default(), EvmEnv::default());
961
962        // Test EVM Context Block mutation
963        evm.ctx_mut().block_mut().set_number(U256::from(123));
964        assert_eq!(evm.ctx().block().number(), U256::from(123));
965
966        // Test EVM Context Tx mutation
967        evm.ctx_mut().tx_mut().set_nonce(99);
968        assert_eq!(evm.ctx().tx().nonce(), 99);
969
970        // Test EVM Context Cfg mutation
971        evm.ctx_mut().cfg_mut().spec = SpecId::AMSTERDAM;
972        assert_eq!(evm.ctx().cfg().spec, SpecId::AMSTERDAM);
973
974        // Round-trip test to ensure no issues with cloning and setting tx_env and evm_env
975        let tx_env = evm.ctx().tx_clone();
976        evm.ctx_mut().set_tx(tx_env);
977        let evm_env = evm.ctx().evm_clone();
978        evm.ctx_mut().set_evm(evm_env);
979    }
980
981    #[test]
982    #[cfg(feature = "monad")]
983    fn monad_evm_foundry_context_ext_implementation() {
984        let mut evm = alloy_monad_evm::MonadEvmFactory::default().create_evm(
985            EmptyDB::default(),
986            EvmEnv::new(
987                CfgEnv::new_with_spec(monad_revm::MonadHardfork::MonadNine),
988                BlockEnv::default(),
989            ),
990        );
991
992        // Test EVM Context Block mutation
993        evm.ctx_mut().block_mut().set_number(U256::from(123));
994        assert_eq!(evm.ctx().block().number(), U256::from(123));
995
996        // Test EVM Context Tx mutation
997        evm.ctx_mut().tx_mut().set_nonce(99);
998        assert_eq!(evm.ctx().tx().nonce(), 99);
999
1000        // Test EVM Context Cfg mutation
1001        evm.ctx_mut().cfg_mut().spec = monad_revm::MonadHardfork::MonadEight;
1002        assert_eq!(evm.ctx().cfg().spec, monad_revm::MonadHardfork::MonadEight);
1003
1004        // Round-trip test to ensure no issues with cloning and setting tx_env and evm_env
1005        let tx_env = evm.ctx().tx_clone();
1006        evm.ctx_mut().set_tx(tx_env);
1007        let evm_env = evm.ctx().evm_clone();
1008        evm.ctx_mut().set_evm(evm_env);
1009    }
1010
1011    #[test]
1012    #[cfg(feature = "monad")]
1013    fn monad_memory_limit_follows_hardfork_transitions() {
1014        const FOUNDRY_MEMORY_LIMIT: u64 = 128 * 1024 * 1024;
1015
1016        let mut cfg = CfgEnv::new_with_spec(monad_revm::MonadHardfork::MonadEight);
1017        cfg.memory_limit = FOUNDRY_MEMORY_LIMIT;
1018        let mut evm = alloy_monad_evm::MonadEvmFactory::default()
1019            .create_evm(EmptyDB::default(), EvmEnv::new(cfg, BlockEnv::default()));
1020
1021        assert_eq!(evm.ctx().cfg().memory_limit(), FOUNDRY_MEMORY_LIMIT);
1022
1023        evm.ctx_mut().set_spec_and_gas_params(monad_revm::MonadHardfork::MonadNine);
1024        assert_eq!(evm.ctx().cfg().inner().memory_limit, FOUNDRY_MEMORY_LIMIT);
1025        assert_eq!(evm.ctx().cfg().memory_limit(), monad_revm::cfg::MONAD_MEMORY_LIMIT);
1026
1027        evm.ctx_mut().set_spec_and_gas_params(monad_revm::MonadHardfork::MonadEight);
1028        assert_eq!(evm.ctx().cfg().memory_limit(), FOUNDRY_MEMORY_LIMIT);
1029    }
1030
1031    #[test]
1032    fn tempo_evm_foundry_context_ext_implementation() {
1033        let mut evm = TempoEvmFactory::default().create_evm(EmptyDB::default(), EvmEnv::default());
1034
1035        // Test EVM Context Block mutation
1036        evm.ctx_mut().block_mut().set_number(U256::from(123));
1037        assert_eq!(evm.ctx().block().number(), U256::from(123));
1038
1039        // Test EVM Context Tx mutation
1040        evm.ctx_mut().tx_mut().set_nonce(99);
1041        assert_eq!(evm.ctx().tx().nonce(), 99);
1042
1043        // Test EVM Context Cfg mutation
1044        evm.ctx_mut().cfg_mut().spec = TempoHardfork::Genesis;
1045        assert_eq!(evm.ctx().cfg().spec, TempoHardfork::Genesis);
1046
1047        // Round-trip test to ensure no issues with cloning and setting tx_env and evm_env
1048        let tx_env = evm.ctx().tx_clone();
1049        evm.ctx_mut().set_tx(tx_env);
1050        let evm_env = evm.ctx().evm_clone();
1051        evm.ctx_mut().set_evm(evm_env);
1052    }
1053
1054    #[test]
1055    fn tempo_tx_env_setters_update_aa_call_payload() {
1056        let old_to = TxKind::Call(Address::with_last_byte(0xAA));
1057        let new_to = TxKind::Create;
1058        let new_value = U256::from(123);
1059        let new_input = Bytes::from_static(b"local bytecode");
1060
1061        let mut tx_env = TempoTxEnv {
1062            inner: TxEnv {
1063                kind: old_to,
1064                value: U256::from(1),
1065                data: Bytes::from_static(b"original bytecode"),
1066                ..Default::default()
1067            },
1068            tempo_tx_env: Some(Box::new(tempo_revm::TempoBatchCallEnv {
1069                aa_calls: vec![Call {
1070                    to: old_to,
1071                    value: U256::from(1),
1072                    input: Bytes::from_static(b"original bytecode"),
1073                }],
1074                ..Default::default()
1075            })),
1076            ..Default::default()
1077        };
1078
1079        tx_env.set_kind(new_to);
1080        tx_env.set_value(new_value);
1081        tx_env.set_data(new_input.clone());
1082
1083        assert_eq!(tx_env.inner.kind, new_to);
1084        assert_eq!(tx_env.inner.value, new_value);
1085        assert_eq!(tx_env.inner.data, new_input);
1086
1087        let call = &tx_env.tempo_tx_env.as_ref().unwrap().aa_calls[0];
1088        assert_eq!(call.to, new_to);
1089        assert_eq!(call.value, new_value);
1090        assert_eq!(call.input, new_input);
1091    }
1092
1093    fn make_signed_eip1559() -> Signed<TxEip1559> {
1094        Signed::new_unchecked(
1095            TxEip1559 {
1096                chain_id: 1,
1097                nonce: 42,
1098                gas_limit: 21001,
1099                to: TxKind::Call(Address::with_last_byte(0xBB)),
1100                value: U256::from(101),
1101                ..Default::default()
1102            },
1103            Signature::new(U256::ZERO, U256::ZERO, false),
1104            B256::ZERO,
1105        )
1106    }
1107
1108    #[test]
1109    fn from_any_rpc_transaction_for_eth() {
1110        let from = Address::random();
1111        let signed_tx = make_signed_eip1559();
1112        let rpc_tx = RpcTransaction::from_transaction(
1113            Recovered::new_unchecked(signed_tx.into(), from),
1114            TransactionInfo::default(),
1115        );
1116
1117        let any_tx = <AnyRpcTransaction as From<RpcTransaction>>::from(rpc_tx);
1118        let tx_env = TxEnv::from_any_rpc_transaction(&any_tx).unwrap();
1119
1120        assert_eq!(tx_env.caller, from);
1121        assert_eq!(tx_env.nonce, 42);
1122        assert_eq!(tx_env.gas_limit, 21001);
1123        assert_eq!(tx_env.value, U256::from(101));
1124        assert_eq!(tx_env.kind, TxKind::Call(Address::with_last_byte(0xBB)));
1125    }
1126
1127    #[test]
1128    fn from_any_rpc_transaction_unknown_envelope_errors() {
1129        let unknown = AnyTxEnvelope::Unknown(UnknownTxEnvelope {
1130            hash: B256::ZERO,
1131            inner: UnknownTypedTransaction {
1132                ty: AnyTxType(0xFF),
1133                fields: Default::default(),
1134                memo: Default::default(),
1135            },
1136        });
1137        let from = Address::random();
1138        let any_tx = AnyRpcTransaction::new(WithOtherFields::new(RpcTransaction {
1139            inner: Recovered::new_unchecked(unknown, from),
1140            block_hash: None,
1141            block_number: None,
1142            transaction_index: None,
1143            effective_gas_price: None,
1144            block_timestamp: None,
1145        }));
1146
1147        let result = TxEnv::from_any_rpc_transaction(&any_tx).unwrap_err();
1148        assert!(result.to_string().contains("unknown transaction type"));
1149    }
1150
1151    #[test]
1152    fn from_any_rpc_transaction_for_tempo_eth_envelope() {
1153        let from = Address::random();
1154        let signed_tx = make_signed_eip1559();
1155        let rpc_tx = RpcTransaction::from_transaction(
1156            Recovered::new_unchecked(signed_tx.into(), from),
1157            TransactionInfo::default(),
1158        );
1159        let any_tx = <AnyRpcTransaction as From<RpcTransaction>>::from(rpc_tx);
1160
1161        let tx_env = TempoTxEnv::from_any_rpc_transaction(&any_tx).unwrap();
1162        assert_eq!(tx_env.inner.caller, from);
1163        assert_eq!(tx_env.inner.nonce, 42);
1164        assert_eq!(tx_env.inner.gas_limit, 21001);
1165        assert_eq!(tx_env.inner.value, U256::from(101));
1166        assert_eq!(tx_env.fee_token, None);
1167    }
1168
1169    #[test]
1170    fn from_any_rpc_transaction_for_tempo_aa() {
1171        let from = Address::random();
1172        let fee_token = Some(Address::random());
1173        let tempo_tx = TempoTransaction {
1174            chain_id: 42431,
1175            nonce: 42,
1176            gas_limit: 424242,
1177            fee_token,
1178            nonce_key: U256::from(4242),
1179            valid_after: NonZeroU64::new(1800000000),
1180            ..Default::default()
1181        };
1182        let aa_signed = AASigned::new_unhashed(
1183            tempo_tx,
1184            TempoSignature::Primitive(PrimitiveSignature::Secp256k1(Signature::new(
1185                U256::ZERO,
1186                U256::ZERO,
1187                false,
1188            ))),
1189        );
1190
1191        // Build a concrete Tempo RPC transaction, serialize to JSON, deserialize as
1192        // AnyRpcTransaction.
1193        let rpc_tx = RpcTransaction::from_transaction(
1194            Recovered::new_unchecked(TempoTxEnvelope::AA(aa_signed), from),
1195            TransactionInfo::default(),
1196        );
1197        let json = serde_json::to_value(&rpc_tx).unwrap();
1198        let any_tx: AnyRpcTransaction = serde_json::from_value(json).unwrap();
1199
1200        let tx_env = TempoTxEnv::from_any_rpc_transaction(&any_tx).unwrap();
1201        assert_eq!(tx_env.inner.caller, from);
1202        assert_eq!(tx_env.inner.nonce, 42);
1203        assert_eq!(tx_env.inner.gas_limit, 424242);
1204        assert_eq!(tx_env.inner.chain_id, Some(42431));
1205        assert_eq!(tx_env.fee_token, fee_token);
1206    }
1207
1208    #[cfg(feature = "optimism")]
1209    mod optimism {
1210        use super::*;
1211        use alloy_consensus::Sealed;
1212        use alloy_op_evm::{OpEvmFactory, OpTx};
1213        use op_alloy_consensus::{OpTxEnvelope, TxDeposit, transaction::OpTransactionInfo};
1214        use op_alloy_rpc_types::Transaction as OpRpcTransaction;
1215        use op_revm::OpSpecId;
1216
1217        #[test]
1218        fn op_evm_foundry_context_ext_implementation() {
1219            let mut evm =
1220                OpEvmFactory::<OpTx>::default().create_evm(EmptyDB::default(), EvmEnv::default());
1221
1222            // Test EVM Context Block mutation
1223            evm.ctx_mut().block_mut().set_number(U256::from(123));
1224            assert_eq!(evm.ctx().block().number(), U256::from(123));
1225
1226            // Test EVM Context Tx mutation
1227            evm.ctx_mut().tx_mut().set_nonce(99);
1228            assert_eq!(evm.ctx().tx().nonce(), 99);
1229
1230            // Test EVM Context Cfg mutation
1231            evm.ctx_mut().cfg_mut().spec = OpSpecId::JOVIAN;
1232            assert_eq!(evm.ctx().cfg().spec, OpSpecId::JOVIAN);
1233
1234            // Round-trip test to ensure no issues with cloning and setting tx_env and evm_env
1235            let tx_env = evm.ctx().tx_clone();
1236            evm.ctx_mut().set_tx(tx_env);
1237            let evm_env = evm.ctx().evm_clone();
1238            evm.ctx_mut().set_evm(evm_env);
1239        }
1240
1241        #[test]
1242        fn from_any_rpc_transaction_for_op() {
1243            let from = Address::random();
1244            let signed_tx = make_signed_eip1559();
1245
1246            // Build the eth TxEnv to compare against op base
1247            let rpc_tx = RpcTransaction::from_transaction(
1248                Recovered::new_unchecked(signed_tx.into(), from),
1249                TransactionInfo::default(),
1250            );
1251            let any_tx = <AnyRpcTransaction as From<RpcTransaction>>::from(rpc_tx);
1252            let expected_base = TxEnv::from_any_rpc_transaction(&any_tx).unwrap();
1253
1254            let op_tx_env = OpTx::from_any_rpc_transaction(&any_tx).unwrap();
1255            assert_eq!(op_tx_env.base, expected_base);
1256        }
1257
1258        #[test]
1259        fn from_any_rpc_transaction_for_op_deposit() {
1260            let from = Address::random();
1261            let source_hash = B256::random();
1262            let deposit = TxDeposit {
1263                source_hash,
1264                from,
1265                to: TxKind::Call(Address::with_last_byte(0xCC)),
1266                mint: 1111,
1267                value: U256::from(200),
1268                gas_limit: 21000,
1269                is_system_transaction: true,
1270                input: Default::default(),
1271            };
1272
1273            // Build a concrete OpRpcTransaction, serialize to JSON, deserialize as
1274            // AnyRpcTransaction.
1275            let op_rpc_tx = OpRpcTransaction::from_transaction(
1276                Recovered::new_unchecked(OpTxEnvelope::Deposit(Sealed::new(deposit)), from),
1277                OpTransactionInfo::default(),
1278            );
1279            let json = serde_json::to_value(&op_rpc_tx).unwrap();
1280            let any_tx: AnyRpcTransaction = serde_json::from_value(json).unwrap();
1281
1282            let op_tx_env = OpTx::from_any_rpc_transaction(&any_tx).unwrap();
1283            assert_eq!(op_tx_env.base.caller, from);
1284            assert_eq!(op_tx_env.base.kind, TxKind::Call(Address::with_last_byte(0xCC)));
1285            assert_eq!(op_tx_env.base.value, U256::from(200));
1286            assert_eq!(op_tx_env.base.gas_limit, 21000);
1287            assert_eq!(op_tx_env.deposit.source_hash, source_hash);
1288            assert_eq!(op_tx_env.deposit.mint, Some(1111));
1289            assert!(op_tx_env.deposit.is_system_transaction);
1290        }
1291    }
1292}