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
66pub static STEEL_TEST_PRAGUE_CHAIN_SPEC: LazyLock<ChainSpec<SpecId>> =
68 LazyLock::new(|| ChainSpec::new_single(5733100018, SpecId::PRAGUE));
69
70#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize)]
72#[non_exhaustive]
73pub struct EthEvmFactory;
74
75impl EvmFactory for EthEvmFactory {
76 type Evm<DB: Database> = <AlloyEthEvmFactory as AlloyEvmFactory>::Evm<DB, NoOpInspector>;
77 type Tx = <AlloyEthEvmFactory as AlloyEvmFactory>::Tx;
78 type Error<DBError: Error + Send + Sync + 'static> =
79 <AlloyEthEvmFactory as AlloyEvmFactory>::Error<DBError>;
80 type HaltReason = <AlloyEthEvmFactory as AlloyEvmFactory>::HaltReason;
81 type Spec = <AlloyEthEvmFactory as AlloyEvmFactory>::Spec;
82 type Header = EthBlockHeader;
83
84 fn new_tx(address: Address, data: Bytes) -> Self::Tx {
85 TxEnv {
86 caller: address,
87 kind: TxKind::Call(address),
88 data,
89 chain_id: None,
90 ..Default::default()
91 }
92 }
93
94 fn create_evm<DB: Database>(
95 db: DB,
96 chain_id: u64,
97 spec: Self::Spec,
98 header: &Self::Header,
99 ) -> Self::Evm<DB> {
100 let mut cfg_env = CfgEnv::new_with_spec(spec).with_chain_id(chain_id);
101 cfg_env.disable_nonce_check = true;
102 cfg_env.disable_balance_check = true;
103 cfg_env.disable_block_gas_limit = true;
104 cfg_env.disable_eip3607 = true;
106 cfg_env.disable_base_fee = true;
108
109 let block_env = header.to_block_env(spec);
110
111 AlloyEthEvmFactory::default().create_evm(db, (cfg_env, block_env).into())
112 }
113}
114
115pub type EthChainSpec = ChainSpec<SpecId>;
117
118pub type EthEvmEnv<D, C> = EvmEnv<D, EthEvmFactory, C>;
120
121pub type EthEvmInput = EvmInput<EthEvmFactory>;
123
124pub type EthBlockHeader = RlpHeader<alloy_consensus::Header>;
126
127impl EvmBlockHeader for EthBlockHeader {
128 type Spec = SpecId;
129
130 #[inline]
131 fn parent_hash(&self) -> &B256 {
132 &self.inner().parent_hash
133 }
134 #[inline]
135 fn number(&self) -> BlockNumber {
136 self.inner().number
137 }
138 #[inline]
139 fn timestamp(&self) -> u64 {
140 self.inner().timestamp
141 }
142 #[inline]
143 fn state_root(&self) -> &B256 {
144 &self.inner().state_root
145 }
146 #[inline]
147 fn receipts_root(&self) -> &B256 {
148 &self.inner().receipts_root
149 }
150 #[inline]
151 fn logs_bloom(&self) -> &alloy_primitives::Bloom {
152 &self.inner().logs_bloom
153 }
154
155 #[inline]
156 fn to_block_env(&self, spec: SpecId) -> BlockEnv {
157 let header = self.inner();
158
159 let blob_excess_gas_and_price = header.excess_blob_gas.map(|excess_blob_gas| match spec {
160 SpecId::CANCUN => BlobExcessGasAndPrice::new(
161 excess_blob_gas,
162 eip4844::BLOB_GASPRICE_UPDATE_FRACTION as u64,
163 ),
164 SpecId::PRAGUE => BlobExcessGasAndPrice::new(
165 excess_blob_gas,
166 eip7691::BLOB_GASPRICE_UPDATE_FRACTION_PECTRA as u64,
167 ),
168 SpecId::OSAKA => BlobExcessGasAndPrice::new(
169 excess_blob_gas,
170 eip7691::BLOB_GASPRICE_UPDATE_FRACTION_PECTRA as u64,
171 ),
172 _ => unimplemented!("unsupported spec with `excess_blob_gas`: {spec}"),
173 });
174
175 BlockEnv {
176 number: U256::from(header.number),
177 beneficiary: header.beneficiary,
178 timestamp: U256::from(header.timestamp),
179 gas_limit: header.gas_limit,
180 basefee: header.base_fee_per_gas.unwrap_or_default(),
181 difficulty: header.difficulty,
182 prevrandao: (spec >= SpecId::MERGE).then_some(header.mix_hash),
183 blob_excess_gas_and_price,
184 }
185 }
186}
187
188#[cfg(test)]
189mod tests {
190 use alloy::primitives::b256;
191
192 use super::{
193 ETH_HOLESKY_CHAIN_SPEC, ETH_MAINNET_CHAIN_SPEC, ETH_SEPOLIA_CHAIN_SPEC,
194 STEEL_TEST_PRAGUE_CHAIN_SPEC,
195 };
196
197 #[test]
200 fn mainnet_spec_digest() {
201 assert_eq!(
202 ETH_MAINNET_CHAIN_SPEC.digest(),
203 b256!("0x9a223c7ca04c969f1cacbe5b8db44c308b2c53390505d3d48c834ed4469fc839")
204 );
205 }
206
207 #[test]
208 fn sepolia_spec_digest() {
209 assert_eq!(
210 ETH_SEPOLIA_CHAIN_SPEC.digest(),
211 b256!("0x5c9552dc9bfad8572ded4f818bb35b0f4260660c1554236986b768ae999b4b60")
212 );
213 }
214
215 #[test]
216 fn holesky_spec_digest() {
217 assert_eq!(
218 ETH_HOLESKY_CHAIN_SPEC.digest(),
219 b256!("0x8eae1ba5f877e6ad7007bf6985f5245be7d758457fb4eb7e6a72d47f49bea389")
220 );
221 }
222
223 #[test]
224 fn testnet_spec_digest() {
225 assert_eq!(
226 STEEL_TEST_PRAGUE_CHAIN_SPEC.digest(),
227 b256!("0x33e32d9590cd4b168773ca27de65d535f2e744274b1437acb712dd4264f2eb87")
228 );
229 }
230}