1use crate::{
17 config::{ChainSpec, ForkCondition},
18 serde::RlpHeader,
19 EvmBlockHeader, EvmEnv, EvmFactory, EvmInput,
20};
21use alloy_eips::{eip4844, eip7691};
22use alloy_evm::{Database, EthEvmFactory as AlloyEthEvmFactory, EvmFactory as AlloyEvmFactory};
23use alloy_primitives::{Address, BlockNumber, Bytes, TxKind, B256, U256};
24use revm::{
25 context::{BlockEnv, CfgEnv, TxEnv},
26 context_interface::block::BlobExcessGasAndPrice,
27 inspector::NoOpInspector,
28 primitives::hardfork::SpecId,
29};
30use serde::{Deserialize, Serialize};
31use std::{collections::BTreeMap, error::Error, sync::LazyLock};
32
33pub static ETH_SEPOLIA_CHAIN_SPEC: LazyLock<EthChainSpec> = LazyLock::new(|| ChainSpec {
35 chain_id: 11155111,
36 forks: BTreeMap::from([
37 (SpecId::MERGE, ForkCondition::Block(1735371)),
38 (SpecId::SHANGHAI, ForkCondition::Timestamp(1677557088)),
39 (SpecId::CANCUN, ForkCondition::Timestamp(1706655072)),
40 (SpecId::PRAGUE, ForkCondition::Timestamp(1741159776)),
41 ]),
42});
43
44pub static ETH_HOLESKY_CHAIN_SPEC: LazyLock<EthChainSpec> = LazyLock::new(|| ChainSpec {
46 chain_id: 17000,
47 forks: BTreeMap::from([
48 (SpecId::MERGE, ForkCondition::Block(0)),
49 (SpecId::SHANGHAI, ForkCondition::Timestamp(1696000704)),
50 (SpecId::CANCUN, ForkCondition::Timestamp(1707305664)),
51 (SpecId::PRAGUE, ForkCondition::Timestamp(1740434112)),
52 ]),
53});
54
55pub static ETH_MAINNET_CHAIN_SPEC: LazyLock<EthChainSpec> = LazyLock::new(|| ChainSpec {
57 chain_id: 1,
58 forks: BTreeMap::from([
59 (SpecId::MERGE, ForkCondition::Block(15537394)),
60 (SpecId::SHANGHAI, ForkCondition::Timestamp(1681338455)),
61 (SpecId::CANCUN, ForkCondition::Timestamp(1710338135)),
62 (SpecId::PRAGUE, ForkCondition::Timestamp(1746612311)),
63 ]),
64});
65
66#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize)]
68#[non_exhaustive]
69pub struct EthEvmFactory;
70
71impl EvmFactory for EthEvmFactory {
72 type Evm<DB: Database> = <AlloyEthEvmFactory as AlloyEvmFactory>::Evm<DB, NoOpInspector>;
73 type Tx = <AlloyEthEvmFactory as AlloyEvmFactory>::Tx;
74 type Error<DBError: Error + Send + Sync + 'static> =
75 <AlloyEthEvmFactory as AlloyEvmFactory>::Error<DBError>;
76 type HaltReason = <AlloyEthEvmFactory as AlloyEvmFactory>::HaltReason;
77 type Spec = <AlloyEthEvmFactory as AlloyEvmFactory>::Spec;
78 type Header = EthBlockHeader;
79
80 fn new_tx(address: Address, data: Bytes) -> Self::Tx {
81 TxEnv {
82 caller: address,
83 kind: TxKind::Call(address),
84 data,
85 chain_id: None,
86 ..Default::default()
87 }
88 }
89
90 fn create_evm<DB: Database>(
91 db: DB,
92 chain_id: u64,
93 spec: Self::Spec,
94 header: &Self::Header,
95 ) -> Self::Evm<DB> {
96 let mut cfg_env = CfgEnv::new_with_spec(spec).with_chain_id(chain_id);
97 cfg_env.disable_nonce_check = true;
98 cfg_env.disable_balance_check = true;
99 cfg_env.disable_block_gas_limit = true;
100 cfg_env.disable_eip3607 = true;
102 cfg_env.disable_base_fee = true;
104
105 let block_env = header.to_block_env(spec);
106
107 AlloyEthEvmFactory::default().create_evm(db, (cfg_env, block_env).into())
108 }
109}
110
111pub type EthChainSpec = ChainSpec<SpecId>;
113
114pub type EthEvmEnv<D, C> = EvmEnv<D, EthEvmFactory, C>;
116
117pub type EthEvmInput = EvmInput<EthEvmFactory>;
119
120pub type EthBlockHeader = RlpHeader<alloy_consensus::Header>;
122
123impl EvmBlockHeader for EthBlockHeader {
124 type Spec = SpecId;
125
126 #[inline]
127 fn parent_hash(&self) -> &B256 {
128 &self.inner().parent_hash
129 }
130 #[inline]
131 fn number(&self) -> BlockNumber {
132 self.inner().number
133 }
134 #[inline]
135 fn timestamp(&self) -> u64 {
136 self.inner().timestamp
137 }
138 #[inline]
139 fn state_root(&self) -> &B256 {
140 &self.inner().state_root
141 }
142 #[inline]
143 fn receipts_root(&self) -> &B256 {
144 &self.inner().receipts_root
145 }
146 #[inline]
147 fn logs_bloom(&self) -> &alloy_primitives::Bloom {
148 &self.inner().logs_bloom
149 }
150
151 #[inline]
152 fn to_block_env(&self, spec: SpecId) -> BlockEnv {
153 let header = self.inner();
154
155 let blob_excess_gas_and_price = header.excess_blob_gas.map(|excess_blob_gas| match spec {
156 SpecId::CANCUN => BlobExcessGasAndPrice::new(
157 excess_blob_gas,
158 eip4844::BLOB_GASPRICE_UPDATE_FRACTION as u64,
159 ),
160 SpecId::PRAGUE => BlobExcessGasAndPrice::new(
161 excess_blob_gas,
162 eip7691::BLOB_GASPRICE_UPDATE_FRACTION_PECTRA as u64,
163 ),
164 SpecId::OSAKA => BlobExcessGasAndPrice::new(
165 excess_blob_gas,
166 eip7691::BLOB_GASPRICE_UPDATE_FRACTION_PECTRA as u64,
167 ),
168 _ => unimplemented!("unsupported spec with `excess_blob_gas`: {spec}"),
169 });
170
171 BlockEnv {
172 number: U256::from(header.number),
173 beneficiary: header.beneficiary,
174 timestamp: U256::from(header.timestamp),
175 gas_limit: header.gas_limit,
176 basefee: header.base_fee_per_gas.unwrap_or_default(),
177 difficulty: header.difficulty,
178 prevrandao: (spec >= SpecId::MERGE).then_some(header.mix_hash),
179 blob_excess_gas_and_price,
180 }
181 }
182}