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