1use crate::{
17 config::{ChainSpec, ForkCondition},
18 serde::RlpHeader,
19 EvmBlockHeader, EvmEnv, EvmFactory, EvmInput, EvmSpecId,
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 (SpecId::OSAKA, ForkCondition::Timestamp(1760427360)),
42 ]),
43});
44
45pub static ETH_HOODI_CHAIN_SPEC: LazyLock<EthChainSpec> = LazyLock::new(|| ChainSpec {
47 chain_id: 560048,
48 forks: BTreeMap::from([
49 (SpecId::CANCUN, ForkCondition::Block(0)),
50 (SpecId::PRAGUE, ForkCondition::Timestamp(1742999832)),
51 (SpecId::OSAKA, ForkCondition::Timestamp(1761677592)),
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 (SpecId::OSAKA, ForkCondition::Timestamp(1764798551)),
64 ]),
65});
66
67pub static STEEL_TEST_PRAGUE_CHAIN_SPEC: LazyLock<ChainSpec<SpecId>> =
69 LazyLock::new(|| ChainSpec::new_single(5733100018, SpecId::PRAGUE));
70
71#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize)]
73#[non_exhaustive]
74pub struct EthEvmFactory;
75
76impl EvmFactory for EthEvmFactory {
77 type Evm<DB: Database> = <AlloyEthEvmFactory as AlloyEvmFactory>::Evm<DB, NoOpInspector>;
78 type Tx = <AlloyEthEvmFactory as AlloyEvmFactory>::Tx;
79 type Error<DBError: Error + Send + Sync + 'static> =
80 <AlloyEthEvmFactory as AlloyEvmFactory>::Error<DBError>;
81 type HaltReason = <AlloyEthEvmFactory as AlloyEvmFactory>::HaltReason;
82 type Spec = <AlloyEthEvmFactory as AlloyEvmFactory>::Spec;
83 type SpecId = SpecId;
84 type Header = EthBlockHeader;
85
86 fn new_tx(address: Address, data: Bytes) -> Self::Tx {
87 TxEnv {
88 caller: address,
89 kind: TxKind::Call(address),
90 data,
91 chain_id: None,
92 ..Default::default()
93 }
94 }
95
96 fn create_evm<DB: Database>(
97 db: DB,
98 chain_id: u64,
99 spec_id: Self::SpecId,
100 header: &Self::Header,
101 ) -> Self::Evm<DB> {
102 let mut cfg_env = CfgEnv::new_with_spec(spec_id).with_chain_id(chain_id);
103 cfg_env.disable_nonce_check = true;
104 cfg_env.disable_balance_check = true;
105 cfg_env.disable_block_gas_limit = true;
106 cfg_env.disable_eip3607 = true;
108 cfg_env.disable_base_fee = true;
110
111 let block_env = header.to_block_env(spec_id);
112
113 AlloyEthEvmFactory::default().create_evm(db, (cfg_env, block_env).into())
114 }
115}
116
117pub type EthChainSpec = ChainSpec<SpecId>;
119
120pub type EthEvmEnv<D, C> = EvmEnv<D, EthEvmFactory, C>;
122
123pub type EthEvmInput = EvmInput<EthEvmFactory>;
125
126pub type EthBlockHeader = RlpHeader<alloy_consensus::Header>;
128
129impl EvmSpecId for SpecId {
130 #[inline]
131 fn has_eip4788(&self) -> bool {
132 self >= &SpecId::CANCUN
133 }
134 #[inline]
135 fn has_eip2935(&self) -> bool {
136 self >= &SpecId::PRAGUE
137 }
138 #[inline]
139 fn to_u32(&self) -> u32 {
140 *self as u32
141 }
142}
143
144impl EvmBlockHeader for EthBlockHeader {
145 type SpecId = SpecId;
146
147 #[inline]
148 fn parent_hash(&self) -> &B256 {
149 &self.inner().parent_hash
150 }
151 #[inline]
152 fn number(&self) -> BlockNumber {
153 self.inner().number
154 }
155 #[inline]
156 fn timestamp(&self) -> u64 {
157 self.inner().timestamp
158 }
159 #[inline]
160 fn state_root(&self) -> &B256 {
161 &self.inner().state_root
162 }
163 #[inline]
164 fn receipts_root(&self) -> &B256 {
165 &self.inner().receipts_root
166 }
167 #[inline]
168 fn logs_bloom(&self) -> &alloy_primitives::Bloom {
169 &self.inner().logs_bloom
170 }
171
172 #[inline]
173 fn to_block_env(&self, spec: SpecId) -> BlockEnv {
174 let header = self.inner();
175
176 let blob_excess_gas_and_price = header.excess_blob_gas.map(|excess_blob_gas| match spec {
177 SpecId::CANCUN => BlobExcessGasAndPrice::new(
178 excess_blob_gas,
179 eip4844::BLOB_GASPRICE_UPDATE_FRACTION as u64,
180 ),
181 SpecId::PRAGUE => BlobExcessGasAndPrice::new(
182 excess_blob_gas,
183 eip7691::BLOB_GASPRICE_UPDATE_FRACTION_PECTRA as u64,
184 ),
185 SpecId::OSAKA => BlobExcessGasAndPrice::new(
186 excess_blob_gas,
187 eip7691::BLOB_GASPRICE_UPDATE_FRACTION_PECTRA as u64,
188 ),
189 _ => unimplemented!("unsupported spec with `excess_blob_gas`: {spec}"),
190 });
191
192 BlockEnv {
193 number: U256::from(header.number),
194 beneficiary: header.beneficiary,
195 timestamp: U256::from(header.timestamp),
196 gas_limit: header.gas_limit,
197 basefee: header.base_fee_per_gas.unwrap_or_default(),
198 difficulty: header.difficulty,
199 prevrandao: (spec >= SpecId::MERGE).then_some(header.mix_hash),
200 blob_excess_gas_and_price,
201 slot_num: header.slot_number.unwrap_or_default(),
202 }
203 }
204}
205
206#[cfg(test)]
207mod tests {
208 use alloy::primitives::b256;
209
210 use super::{
211 ETH_HOODI_CHAIN_SPEC, ETH_MAINNET_CHAIN_SPEC, ETH_SEPOLIA_CHAIN_SPEC,
212 STEEL_TEST_PRAGUE_CHAIN_SPEC,
213 };
214
215 #[test]
218 fn mainnet_spec_digest() {
219 assert_eq!(
220 ETH_MAINNET_CHAIN_SPEC.digest(),
221 b256!("0x47dc59f84afd2e9e7a48c4012004ab7c77fbd9acf822bf1143b8442c6c8851d4")
222 );
223 }
224
225 #[test]
226 fn sepolia_spec_digest() {
227 assert_eq!(
228 ETH_SEPOLIA_CHAIN_SPEC.digest(),
229 b256!("0x90c1e882b1f0fda4dc7f1c66c07ed3d2a74e443834905faa9f32f583b71f459d")
230 );
231 }
232
233 #[test]
234 fn hoodi_spec_digest() {
235 assert_eq!(
236 ETH_HOODI_CHAIN_SPEC.digest(),
237 b256!("0x34cb1defd939572b00439d2c13f93c033b82227067371c910ad104d527c78860")
238 );
239 }
240
241 #[test]
242 fn testnet_spec_digest() {
243 assert_eq!(
244 STEEL_TEST_PRAGUE_CHAIN_SPEC.digest(),
245 b256!("0x33e32d9590cd4b168773ca27de65d535f2e744274b1437acb712dd4264f2eb87")
246 );
247 }
248}