revm_handler/
mainnet_builder.rs

1use crate::{frame::EthFrame, instructions::EthInstructions, EthPrecompiles};
2use context::{BlockEnv, Cfg, CfgEnv, Context, Evm, FrameStack, Journal, TxEnv};
3use context_interface::{Block, Database, JournalTr, Transaction};
4use database_interface::EmptyDB;
5use interpreter::interpreter::EthInterpreter;
6use primitives::hardfork::SpecId;
7
8/// Type alias for a mainnet EVM instance with standard Ethereum components.
9pub type MainnetEvm<CTX, INSP = ()> =
10    Evm<CTX, INSP, EthInstructions<EthInterpreter, CTX>, EthPrecompiles, EthFrame<EthInterpreter>>;
11
12/// Type alias for a mainnet context with standard Ethereum environment types.
13pub type MainnetContext<DB> = Context<BlockEnv, TxEnv, CfgEnv, DB, Journal<DB>, ()>;
14
15/// Trait for building mainnet EVM instances from contexts.
16pub trait MainBuilder: Sized {
17    /// The context type that will be used in the EVM.
18    type Context;
19
20    /// Builds a mainnet EVM instance without an inspector.
21    fn build_mainnet(self) -> MainnetEvm<Self::Context>;
22
23    /// Builds a mainnet EVM instance with the provided inspector.
24    fn build_mainnet_with_inspector<INSP>(self, inspector: INSP)
25        -> MainnetEvm<Self::Context, INSP>;
26}
27
28impl<BLOCK, TX, CFG, DB, JOURNAL, CHAIN> MainBuilder for Context<BLOCK, TX, CFG, DB, JOURNAL, CHAIN>
29where
30    BLOCK: Block,
31    TX: Transaction,
32    CFG: Cfg,
33    DB: Database,
34    JOURNAL: JournalTr<Database = DB>,
35{
36    type Context = Self;
37
38    fn build_mainnet(self) -> MainnetEvm<Self::Context> {
39        Evm {
40            ctx: self,
41            inspector: (),
42            instruction: EthInstructions::default(),
43            precompiles: EthPrecompiles::default(),
44            frame_stack: FrameStack::new(),
45        }
46    }
47
48    fn build_mainnet_with_inspector<INSP>(
49        self,
50        inspector: INSP,
51    ) -> MainnetEvm<Self::Context, INSP> {
52        Evm {
53            ctx: self,
54            inspector,
55            instruction: EthInstructions::default(),
56            precompiles: EthPrecompiles::default(),
57            frame_stack: FrameStack::new(),
58        }
59    }
60}
61
62/// Trait used to initialize Context with default mainnet types.
63pub trait MainContext {
64    /// Creates a new mainnet context with default configuration.
65    fn mainnet() -> Self;
66}
67
68impl MainContext for Context<BlockEnv, TxEnv, CfgEnv, EmptyDB, Journal<EmptyDB>, ()> {
69    fn mainnet() -> Self {
70        Context::new(EmptyDB::new(), SpecId::default())
71    }
72}
73
74#[cfg(test)]
75mod test {
76    use crate::ExecuteEvm;
77    use crate::{MainBuilder, MainContext};
78    use alloy_signer::{Either, SignerSync};
79    use alloy_signer_local::PrivateKeySigner;
80    use bytecode::{
81        opcode::{PUSH1, SSTORE},
82        Bytecode,
83    };
84    use context::{Context, TxEnv};
85    use context_interface::transaction::Authorization;
86    use database::{BenchmarkDB, EEADDRESS, FFADDRESS};
87    use primitives::{hardfork::SpecId, TxKind, U256};
88    use primitives::{StorageKey, StorageValue};
89
90    #[test]
91    fn sanity_eip7702_tx() {
92        let signer = PrivateKeySigner::random();
93        let auth = Authorization {
94            chain_id: U256::ZERO,
95            nonce: 0,
96            address: FFADDRESS,
97        };
98        let signature = signer.sign_hash_sync(&auth.signature_hash()).unwrap();
99        let auth = auth.into_signed(signature);
100
101        let bytecode = Bytecode::new_legacy([PUSH1, 0x01, PUSH1, 0x01, SSTORE].into());
102
103        let ctx = Context::mainnet()
104            .modify_cfg_chained(|cfg| cfg.spec = SpecId::PRAGUE)
105            .with_db(BenchmarkDB::new_bytecode(bytecode));
106
107        let mut evm = ctx.build_mainnet();
108
109        let state = evm
110            .transact(
111                TxEnv::builder()
112                    .gas_limit(100_000)
113                    .authorization_list(vec![Either::Left(auth)])
114                    .caller(EEADDRESS)
115                    .kind(TxKind::Call(signer.address()))
116                    .build()
117                    .unwrap(),
118            )
119            .unwrap()
120            .state;
121
122        let auth_acc = state.get(&signer.address()).unwrap();
123        assert_eq!(auth_acc.info.code, Some(Bytecode::new_eip7702(FFADDRESS)));
124        assert_eq!(auth_acc.info.nonce, 1);
125        assert_eq!(
126            auth_acc
127                .storage
128                .get(&StorageKey::from(1))
129                .unwrap()
130                .present_value,
131            StorageValue::from(1)
132        );
133    }
134}