Skip to main content

mega_evme/common/
state.rs

1//! State management for mega-evme with optional RPC forking support
2
3use std::{collections::BTreeMap, path::PathBuf, str::FromStr};
4
5use alloy_network::Network;
6use alloy_primitives::{map::DefaultHashBuilder, Address, BlockNumber, Bytes, B256, U256};
7use alloy_provider::Provider;
8use clap::Parser;
9use op_alloy_network::Optimism;
10
11use mega_evm::revm::{
12    database::{AlloyDB, CacheDB, EmptyDB, WrapDatabaseAsync},
13    primitives::HashMap,
14    state::{Account, AccountInfo, Bytecode, EvmState, EvmStorageSlot},
15    Database, DatabaseRef,
16};
17use tracing::{debug, info, trace};
18
19use super::{EvmeError, Result, RpcCacheStore};
20
21/// Pre-execution state configuration arguments
22#[derive(Parser, Debug, Clone)]
23#[command(next_help_heading = "State Options")]
24pub struct PreStateArgs {
25    /// Fork state from a remote RPC endpoint.
26    #[arg(long = "fork")]
27    pub fork: bool,
28
29    /// Block number of the state (post-block state) to fork from. If not specified, the latest
30    /// block is used. Only used if `fork` is true.
31    #[arg(long = "fork.block")]
32    pub fork_block: Option<u64>,
33
34    /// JSON file with prestate (genesis) config. This overrides the state in the
35    /// forked remote state (if applicable).
36    #[arg(long = "prestate", visible_aliases = ["pre-state"])]
37    pub prestate: Option<PathBuf>,
38
39    /// History block hashes to serve `BLOCKHASH` opcode. This overrides the block hashes in the
40    /// forked remote state (if applicable). Each entry should be in the format
41    /// `block_number:block_hash` (can be repeated).
42    #[arg(long = "block-hash", visible_aliases = ["blockhash", "block-hashes", "blockhashes"])]
43    pub block_hashes: Vec<String>,
44
45    /// Balance to allocate to the sender account.
46    /// VALUE can be: plain number (wei), or number with suffix (ether, gwei, wei).
47    /// Examples: `--sender.balance 1ether`, `--sender.balance 1000000000000000000`
48    /// If not specified, sender balance is not set (fallback to `prestate` if specified,
49    /// otherwise 0)
50    #[arg(long = "sender.balance", visible_aliases = ["from.balance"])]
51    pub sender_balance: Option<String>,
52
53    /// Add ether to specified addresses. Each entry format: `ADDRESS+=VALUE`
54    /// VALUE can be: plain number (wei), or number with suffix (ether, gwei, wei).
55    /// Examples: `--faucet 0x1234+=100ether`, `--faucet 0x5678+=1000000gwei`
56    /// Can be repeated for multiple addresses.
57    #[arg(long = "faucet")]
58    pub faucet: Vec<String>,
59
60    /// Override balance for specified addresses. Each entry format: `ADDRESS=VALUE`
61    /// VALUE can be: plain number (wei), or number with suffix (ether, gwei, wei).
62    /// Examples: `--balance 0x1234=100ether`
63    #[arg(long = "balance")]
64    pub balance: Vec<String>,
65
66    /// Override storage slots. Each entry format: `ADDRESS:SLOT=VALUE`
67    /// SLOT and VALUE are U256 (hex or decimal).
68    /// Examples: `--storage 0x1234:0x0=0x1`
69    #[arg(long = "storage")]
70    pub storage: Vec<String>,
71}
72
73/// Parse ether value string into wei (U256).
74/// Supports: plain number (wei), or number with suffix (ether, gwei, wei, etc).
75/// Examples: "1000000000000000000", "1ether", "100gwei", "1000wei"
76pub fn parse_ether_value(s: &str) -> Result<U256> {
77    use alloy_primitives::utils::parse_units;
78
79    let s = s.trim();
80
81    // Find where digits/decimal end and unit begins
82    let split_pos = s.find(|c: char| !c.is_ascii_digit() && c != '.').unwrap_or(s.len());
83
84    let (num_str, unit) = s.split_at(split_pos);
85    let unit = if unit.is_empty() { "wei" } else { unit };
86
87    let parsed = parse_units(num_str, unit)
88        .map_err(|e| EvmeError::InvalidInput(format!("Invalid ether value '{}': {}", s, e)))?;
89
90    Ok(parsed.into())
91}
92
93impl PreStateArgs {
94    /// Parse block hashes from CLI arguments.
95    ///
96    /// Each entry should be in the format `block_number:block_hash`.
97    pub fn parse_block_hashes(&self) -> Result<HashMap<u64, B256>> {
98        debug!("Parsing block hashes");
99        let mut map = HashMap::default();
100        for entry in &self.block_hashes {
101            let (num_str, hash_str) = entry.split_once(':').ok_or_else(|| {
102                EvmeError::InvalidInput(format!(
103                    "Invalid block hash entry '{}': expected format 'block_number:block_hash'",
104                    entry
105                ))
106            })?;
107            let block_num: u64 = num_str.trim().parse().map_err(|e| {
108                EvmeError::InvalidInput(format!(
109                    "Invalid block number '{}' in entry '{}': {}",
110                    num_str, entry, e
111                ))
112            })?;
113            let block_hash = B256::from_str(hash_str.trim()).map_err(|e| {
114                EvmeError::InvalidInput(format!(
115                    "Invalid block hash '{}' in entry '{}': {}",
116                    hash_str, entry, e
117                ))
118            })?;
119            map.insert(block_num, block_hash);
120        }
121        trace!(block_hashes = ?map, "Block hashes parsed");
122        Ok(map)
123    }
124
125    /// Parse faucet entries from CLI arguments.
126    ///
127    /// Each entry should be in the format `ADDRESS+=VALUE`.
128    /// VALUE can be: plain number (wei), or number with suffix (ether, gwei, wei).
129    pub fn parse_faucet(&self) -> Result<Vec<(Address, U256)>> {
130        let mut entries = Vec::new();
131        for entry in &self.faucet {
132            let (addr_str, value_str) = entry.split_once("+=").ok_or_else(|| {
133                EvmeError::InvalidInput(format!(
134                    "Invalid faucet entry '{}': expected format 'ADDRESS+=VALUE'",
135                    entry
136                ))
137            })?;
138            let address = Address::from_str(addr_str.trim()).map_err(|e| {
139                EvmeError::InvalidInput(format!(
140                    "Invalid address '{}' in faucet entry '{}': {}",
141                    addr_str, entry, e
142                ))
143            })?;
144            let wei = parse_ether_value(value_str)?;
145            entries.push((address, wei));
146        }
147        Ok(entries)
148    }
149
150    /// Parse balance override entries from CLI arguments.
151    ///
152    /// Each entry should be in the format `ADDRESS=VALUE`.
153    /// VALUE can be: plain number (wei), or number with suffix (ether, gwei, wei).
154    pub fn parse_balance(&self) -> Result<Vec<(Address, U256)>> {
155        let mut entries = Vec::new();
156        for entry in &self.balance {
157            let (addr_str, value_str) = entry.split_once('=').ok_or_else(|| {
158                EvmeError::InvalidInput(format!(
159                    "Invalid balance entry '{}': expected format 'ADDRESS=VALUE'",
160                    entry
161                ))
162            })?;
163            let address = Address::from_str(addr_str.trim()).map_err(|e| {
164                EvmeError::InvalidInput(format!(
165                    "Invalid address '{}' in balance entry '{}': {}",
166                    addr_str, entry, e
167                ))
168            })?;
169            let wei = parse_ether_value(value_str)?;
170            entries.push((address, wei));
171        }
172        Ok(entries)
173    }
174
175    /// Parse storage override entries from CLI arguments.
176    ///
177    /// Each entry should be in the format `ADDRESS:SLOT=VALUE`.
178    /// SLOT and VALUE are U256 (hex or decimal).
179    pub fn parse_storage(&self) -> Result<Vec<(Address, U256, U256)>> {
180        let mut entries = Vec::new();
181        for entry in &self.storage {
182            let (addr_str, rest) = entry.split_once(':').ok_or_else(|| {
183                EvmeError::InvalidInput(format!(
184                    "Invalid storage entry '{}': expected format 'ADDRESS:SLOT=VALUE'",
185                    entry
186                ))
187            })?;
188            let (slot_str, value_str) = rest.split_once('=').ok_or_else(|| {
189                EvmeError::InvalidInput(format!(
190                    "Invalid storage entry '{}': expected format 'ADDRESS:SLOT=VALUE'",
191                    entry
192                ))
193            })?;
194            let address = Address::from_str(addr_str.trim()).map_err(|e| {
195                EvmeError::InvalidInput(format!(
196                    "Invalid address '{}' in storage entry '{}': {}",
197                    addr_str, entry, e
198                ))
199            })?;
200            let slot = U256::from_str(slot_str.trim()).map_err(|e| {
201                EvmeError::InvalidInput(format!(
202                    "Invalid slot '{}' in storage entry '{}': {}",
203                    slot_str, entry, e
204                ))
205            })?;
206            let value = U256::from_str(value_str.trim()).map_err(|e| {
207                EvmeError::InvalidInput(format!(
208                    "Invalid value '{}' in storage entry '{}': {}",
209                    value_str, entry, e
210                ))
211            })?;
212            entries.push((address, slot, value));
213        }
214        Ok(entries)
215    }
216
217    /// Load prestate as [`EvmState`] from file if provided
218    pub fn load_prestate(&self, sender: &Address) -> Result<EvmState> {
219        let mut prestate = if let Some(pre_state_path) = &self.prestate {
220            info!(prestate_path = ?pre_state_path, "Loading prestate from file");
221            let prestate_content = std::fs::read_to_string(pre_state_path)?;
222            let loaded_prestate: HashMap<Address, AccountState> =
223                serde_json::from_str(&prestate_content).map_err(|e| {
224                    EvmeError::InvalidInput(format!("Failed to parse prestate JSON: {}", e))
225                })?;
226            trace!(loaded_prestate = ?loaded_prestate, "Prestate loaded from file");
227            let mut prestate = EvmState::with_capacity_and_hasher(
228                loaded_prestate.len(),
229                DefaultHashBuilder::default(),
230            );
231            for (address, account_state) in loaded_prestate {
232                let account = account_state.into_account()?;
233                prestate.insert(address, account);
234            }
235            trace!(prestate = ?prestate, "Prestate loaded");
236            prestate
237        } else {
238            debug!("No prestate file provided");
239            HashMap::default()
240        };
241
242        // Apply balance overrides
243        for (address, balance) in self.parse_balance()? {
244            info!(address = %address, balance = %balance, "Overriding balance");
245            prestate.entry(address).or_default().info.balance = balance;
246        }
247
248        // Apply storage overrides
249        for (address, slot, value) in self.parse_storage()? {
250            info!(address = %address, slot = %slot, value = %value, "Overriding storage");
251            prestate
252                .entry(address)
253                .or_default()
254                .storage
255                .insert(slot, EvmStorageSlot::new(value, 0));
256        }
257
258        // Set balance for the sender if specified (overrides prestate)
259        if let Some(sender_balance_str) = &self.sender_balance {
260            let sender_balance = parse_ether_value(sender_balance_str)?;
261            info!(sender = %sender, sender_balance = %sender_balance, "Overriding sender balance");
262            prestate.entry(*sender).or_default().info.set_balance(sender_balance);
263        }
264
265        // Apply faucet balances
266        for (address, balance) in self.parse_faucet()? {
267            info!(address = %address, balance = %balance, "Faucet: adding balance");
268            prestate.entry(address).or_default().info.balance += balance;
269        }
270
271        Ok(prestate)
272    }
273
274    /// Build the initial execution state and the clean-exit RPC cache store.
275    ///
276    /// Fork mode (`self.fork == true`) builds a forked state at `self.fork_block` via
277    /// `rpc_args.build_provider()` and returns the caller-owned [`RpcCacheStore`] for
278    /// persist-on-exit. Non-fork mode builds an empty local state, ignores `rpc_args`,
279    /// and returns a no-op store. Either way the call site persists unconditionally.
280    pub async fn create_initial_state(
281        &self,
282        sender: &Address,
283        rpc_args: &super::RpcArgs,
284    ) -> Result<(EvmeState<Optimism, super::OpProvider>, RpcCacheStore)> {
285        let prestate = self.load_prestate(sender)?;
286        let block_hashes = self.parse_block_hashes()?;
287
288        if self.fork {
289            debug!("Creating forked state");
290            if rpc_args.rpc_url.is_none() {
291                return Err(EvmeError::InvalidInput("'--fork' requires '--rpc <URL>'".to_string()));
292            }
293            if rpc_args.capture_file.is_some() || rpc_args.replay_file.is_some() {
294                return Err(EvmeError::InvalidInput(
295                    "'--rpc.capture-file' and '--rpc.replay-file' are not supported with '--fork' \
296                     in this version"
297                        .to_string(),
298                ));
299            }
300            let super::BuildProviderOutput { provider, cache_store, .. } =
301                rpc_args.build_provider().await?;
302            let state =
303                EvmeState::new_forked(provider, self.fork_block, prestate, block_hashes).await?;
304            Ok((state, cache_store))
305        } else {
306            debug!("Creating local state");
307            Ok((EvmeState::new_empty(prestate, block_hashes), RpcCacheStore::noop()))
308        }
309    }
310}
311
312/// State dump configuration arguments
313#[derive(Parser, Debug, Clone)]
314#[command(next_help_heading = "State Dump Options")]
315pub struct StateDumpArgs {
316    /// Dumps the state after the run
317    #[arg(long = "dump")]
318    pub dump: bool,
319
320    /// Output file for state dump (if not specified, prints to console)
321    #[arg(long = "dump.output")]
322    pub dump_output_file: Option<PathBuf>,
323}
324
325impl StateDumpArgs {
326    /// Serializes [`EvmState`] as JSON string with deterministic key ordering.
327    pub fn serialize_evm_state(&self, evm_state: &EvmState) -> Result<String> {
328        trace!(evm_state = ?evm_state, "Serializing EVM state");
329        let account_states: BTreeMap<_, _> = evm_state
330            .iter()
331            .map(|(address, account)| (address, AccountState::from_account(account.clone())))
332            .collect();
333        let state_json = serde_json::to_string_pretty(&account_states)
334            .map_err(|e| EvmeError::ExecutionError(format!("Failed to serialize state: {}", e)))?;
335        Ok(state_json)
336    }
337
338    /// Dumps [`EvmState`] as JSON string to file or console.
339    pub fn dump_evm_state(&self, evm_state: &EvmState) -> Result<()> {
340        debug!("Dumping EVM state");
341        let state_json = self.serialize_evm_state(evm_state)?;
342
343        // Output to file or console
344        println!();
345        println!("=== State Dump ===");
346        if let Some(ref output_file) = self.dump_output_file {
347            debug!(output_file = ?output_file, "Writing dumped state to file");
348            std::fs::write(output_file, state_json).map_err(|e| {
349                EvmeError::ExecutionError(format!("Failed to write state to file: {}", e))
350            })?;
351            println!("State dump written to: {}", output_file.display());
352        } else {
353            debug!("Printing dumped state to console");
354            println!("{}", state_json);
355        }
356
357        Ok(())
358    }
359}
360
361/// Account state information
362#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
363#[serde(rename_all = "camelCase")]
364pub struct AccountState {
365    /// Account balance
366    /// U256 from ruint already uses quantity format (0x-prefixed hex without leading zeros)
367    pub balance: Option<U256>,
368    /// Account nonce (uses `alloy_serde::quantity` for standard Ethereum format)
369    #[serde(default, with = "alloy_serde::quantity::opt")]
370    pub nonce: Option<u64>,
371    /// Account code (hex string with 0x prefix)
372    pub code: Option<Bytes>,
373    /// Code hash
374    /// B256 already uses hex format with 0x prefix (always 32 bytes)
375    pub code_hash: Option<B256>,
376    /// Storage slots (sorted by key for deterministic output)
377    pub storage: Option<BTreeMap<U256, U256>>,
378}
379
380impl AccountState {
381    /// Creates a new [`AccountState`] from [`Account`].
382    ///
383    /// When `AccountInfo.code` is `Some`, `code_hash` is recomputed from the actual
384    /// bytes (guards against stale hashes from direct `info.code` assignment).
385    /// When `code` is `None` (lazy-loaded, e.g. forked accounts whose bytecode hasn't
386    /// been fetched), the original `code_hash` is preserved so the account is not
387    /// silently downgraded to an EOA.
388    pub fn from_account(account: Account) -> Self {
389        let (code, code_hash) = match &account.info.code {
390            Some(bytecode) => {
391                let bytes: Bytes = bytecode.original_byte_slice().to_vec().into();
392                let hash = if bytes.is_empty() {
393                    B256::from(alloy_primitives::KECCAK256_EMPTY)
394                } else {
395                    alloy_primitives::keccak256(&bytes)
396                };
397                (Some(bytes), hash)
398            }
399            // Code not materialized (lazy loading) — preserve original hash.
400            None => (None, account.info.code_hash),
401        };
402        let storage: BTreeMap<U256, U256> =
403            account.storage.into_iter().map(|(slot, value)| (slot, value.present_value)).collect();
404        Self {
405            balance: Some(account.info.balance),
406            nonce: Some(account.info.nonce),
407            code,
408            code_hash: Some(code_hash),
409            storage: Some(storage),
410        }
411    }
412
413    /// Converts into [`Account`].
414    pub fn into_account(self) -> Result<Account> {
415        let code = self.code.unwrap_or_default();
416        let bytecode = if code.is_empty() {
417            Bytecode::default()
418        } else {
419            Bytecode::new_raw_checked(code).map_err(EvmeError::InvalidBytecode)?
420        };
421        let computed_hash = bytecode.hash_slow();
422        if let Some(code_hash) = self.code_hash {
423            if computed_hash != code_hash {
424                return Err(EvmeError::CodeHashMismatch {
425                    expected: code_hash,
426                    computed: computed_hash,
427                });
428            }
429        }
430
431        let info = AccountInfo::new(
432            self.balance.unwrap_or_default(),
433            self.nonce.unwrap_or_default(),
434            computed_hash,
435            bytecode,
436        );
437        let storage = self
438            .storage
439            .unwrap_or_default()
440            .into_iter()
441            .map(|(slot, value)| (slot, EvmStorageSlot::new(value, 0)));
442        Ok(Account::from(info).with_storage(storage))
443    }
444}
445
446/// Backend database type with generic provider and network
447#[derive(Debug)]
448enum EvmeBackend<N, P>
449where
450    N: Network,
451    P: Provider<N>,
452{
453    /// Local state with no RPC backend
454    Empty(EmptyDB),
455    /// Forked state from RPC
456    Forked(Box<CacheDB<WrapDatabaseAsync<AlloyDB<N, P>>>>),
457}
458
459/// State database that can be backed by either [`EmptyDB`] or [`AlloyDB`] (forked from RPC)
460#[derive(Debug)]
461pub struct EvmeState<N, P>
462where
463    N: Network,
464    P: Provider<N>,
465{
466    /// The backend database
467    backend: EvmeBackend<N, P>,
468    /// Prestate overrides (accounts that override the database)
469    prestate: EvmState,
470    /// Code hash to bytecode map (extracted from prestate accounts)
471    code_map: HashMap<alloy_primitives::B256, Bytecode>,
472    /// Block hash overrides (block number -> block hash)
473    block_hashes: HashMap<u64, B256>,
474}
475
476impl<N, P> EvmeState<N, P>
477where
478    N: Network,
479    P: Provider<N>,
480{
481    /// Creates a new empty state with optional prestate overrides and block hash overrides
482    pub fn new_empty(prestate: EvmState, block_hashes: HashMap<u64, B256>) -> Self {
483        // Extract code hash → bytecode mappings from prestate
484        let code_map: HashMap<_, _> = prestate
485            .values()
486            .filter_map(|account| {
487                account.info.code.clone().map(|code| (account.info.code_hash, code))
488            })
489            .collect();
490
491        Self { backend: EvmeBackend::Empty(EmptyDB::default()), prestate, code_map, block_hashes }
492    }
493
494    /// Inserts an account override
495    /// This will override the existing account if it exists.
496    pub fn insert_account(&mut self, address: Address, account: Account) {
497        // Add code to code_map if present
498        if let Some(ref code) = account.info.code {
499            self.code_map.insert(account.info.code_hash, code.clone());
500        }
501        self.prestate.insert(address, account);
502    }
503
504    /// Inserts storage overrides for an account
505    pub fn insert_storage(&mut self, address: Address, storage: HashMap<U256, EvmStorageSlot>) {
506        self.prestate.entry(address).or_default().storage.extend(storage);
507    }
508
509    /// Inserts an account with storage.
510    /// This will override the existing account if it exists.
511    pub fn insert_account_with_storage(
512        &mut self,
513        address: Address,
514        info: AccountInfo,
515        storage: HashMap<U256, EvmStorageSlot>,
516    ) {
517        // Add code to code_map if present
518        if let Some(ref code) = info.code {
519            self.code_map.insert(info.code_hash, code.clone());
520        }
521        let account = Account::from(info).with_storage(storage.into_iter());
522        self.prestate.insert(address, account);
523    }
524
525    /// Set the balance for an account.
526    pub fn set_account_balance(&mut self, address: Address, balance: U256) {
527        self.prestate.entry(address).or_default().info.balance = balance;
528    }
529
530    /// Set the nonce for an account.
531    pub fn set_account_nonce(&mut self, address: Address, nonce: u64) {
532        self.prestate.entry(address).or_default().info.nonce = nonce;
533    }
534
535    /// Set the code for an account.
536    pub fn set_account_code(&mut self, address: Address, code: Bytecode) {
537        self.code_map.insert(code.hash_slow(), code.clone());
538        self.prestate.entry(address).or_default().info.set_code(code);
539    }
540
541    /// Set the storage for an account.
542    pub fn set_account_storage(&mut self, address: Address, storage: HashMap<U256, U256>) {
543        self.prestate
544            .entry(address)
545            .or_default()
546            .storage
547            .extend(storage.into_iter().map(|(slot, value)| (slot, EvmStorageSlot::new(value, 0))));
548    }
549
550    /// Deploys system contracts based on the given spec.
551    pub fn deploy_system_contracts(&mut self, spec: mega_evm::MegaSpecId) {
552        use mega_evm::{
553            MegaSpecId, ACCESS_CONTROL_ADDRESS, ACCESS_CONTROL_CODE,
554            HIGH_PRECISION_TIMESTAMP_ORACLE_ADDRESS, HIGH_PRECISION_TIMESTAMP_ORACLE_CODE,
555            KEYLESS_DEPLOY_ADDRESS, KEYLESS_DEPLOY_CODE, LIMIT_CONTROL_ADDRESS, LIMIT_CONTROL_CODE,
556            ORACLE_CONTRACT_ADDRESS, ORACLE_CONTRACT_CODE, ORACLE_CONTRACT_CODE_REX2,
557            ORACLE_CONTRACT_CODE_REX5, SEQUENCER_REGISTRY_ADDRESS, SEQUENCER_REGISTRY_CODE,
558        };
559
560        // MiniRex+: Oracle Contract (v1.0.0, v1.1.0, or v2.0.0 based on hardfork)
561        if spec >= MegaSpecId::MINI_REX {
562            let code = if spec >= MegaSpecId::REX5 {
563                ORACLE_CONTRACT_CODE_REX5
564            } else if spec >= MegaSpecId::REX2 {
565                ORACLE_CONTRACT_CODE_REX2
566            } else {
567                ORACLE_CONTRACT_CODE
568            };
569            self.set_account_code(ORACLE_CONTRACT_ADDRESS, Bytecode::new_raw(code));
570        }
571
572        // MiniRex+: High Precision Timestamp Oracle
573        if spec >= MegaSpecId::MINI_REX {
574            self.set_account_code(
575                HIGH_PRECISION_TIMESTAMP_ORACLE_ADDRESS,
576                Bytecode::new_raw(HIGH_PRECISION_TIMESTAMP_ORACLE_CODE),
577            );
578        }
579
580        // Rex2+: Keyless Deploy Contract
581        if spec >= MegaSpecId::REX2 {
582            self.set_account_code(KEYLESS_DEPLOY_ADDRESS, Bytecode::new_raw(KEYLESS_DEPLOY_CODE));
583        }
584
585        // Rex4+: Access Control Contract
586        if spec >= MegaSpecId::REX4 {
587            self.set_account_code(ACCESS_CONTROL_ADDRESS, Bytecode::new_raw(ACCESS_CONTROL_CODE));
588        }
589
590        // Rex4+: Limit Control Contract
591        if spec >= MegaSpecId::REX4 {
592            self.set_account_code(LIMIT_CONTROL_ADDRESS, Bytecode::new_raw(LIMIT_CONTROL_CODE));
593        }
594
595        // Rex5+: SequencerRegistry Contract
596        if spec >= MegaSpecId::REX5 {
597            self.set_account_code(
598                SEQUENCER_REGISTRY_ADDRESS,
599                Bytecode::new_raw(SEQUENCER_REGISTRY_CODE),
600            );
601        }
602    }
603}
604
605// Impl block for methods that accept a generic provider
606impl<N, P> EvmeState<N, P>
607where
608    N: Network,
609    P: Provider<N>,
610{
611    /// Create a new forked state from a provider with optional prestate overrides and block hash
612    /// overrides
613    pub async fn new_forked(
614        provider: P,
615        fork_block: Option<u64>,
616        prestate: EvmState,
617        block_hashes: HashMap<u64, B256>,
618    ) -> Result<Self> {
619        // Determine block number
620        let block_num = if let Some(block_num) = fork_block {
621            BlockNumber::from(block_num)
622        } else {
623            // Fetch latest block number
624            let latest_block = provider
625                .get_block_number()
626                .await
627                .map_err(|e| EvmeError::RpcError(format!("Failed to fetch latest block: {}", e)))?;
628            BlockNumber::from(latest_block)
629        };
630
631        // Create AlloyDB with the provider and block number
632        let alloy_db = AlloyDB::new(provider, block_num.into());
633
634        // Wrap the AlloyDB for synchronous access with the runtime
635        let wrapped_db =
636            WrapDatabaseAsync::new(alloy_db).expect("Failed to create wrapped database");
637
638        // Wrap with CacheDB to enable mutable Database trait
639        let db = CacheDB::new(wrapped_db);
640
641        // Extract code hash → bytecode mappings from prestate
642        let code_map: HashMap<_, _> = prestate
643            .values()
644            .filter_map(|account| {
645                account.info.code.clone().map(|code| (account.info.code_hash, code))
646            })
647            .collect();
648
649        Ok(Self { backend: EvmeBackend::Forked(Box::new(db)), prestate, code_map, block_hashes })
650    }
651}
652
653impl<N, P> Database for EvmeState<N, P>
654where
655    N: Network,
656    P: Provider<N> + std::fmt::Debug,
657{
658    type Error = EvmeError;
659
660    fn basic(&mut self, address: Address) -> std::result::Result<Option<AccountInfo>, Self::Error> {
661        // Check prestate overrides first
662        if let Some(account) = self.prestate.get(&address) {
663            trace!(address = %address, account = ?account, "Loaded account basic from prestate");
664            return Ok(Some(account.info.clone()));
665        }
666
667        // Query backend database
668        match &mut self.backend {
669            EvmeBackend::Empty(db) => {
670                let account = db.basic(address).unwrap();
671                trace!(address = %address, account = ?account, "Loaded account basic from empty state");
672                Ok(account)
673            }
674            EvmeBackend::Forked(db) => {
675                let account = db.basic(address).map_err(|e| {
676                    EvmeError::RpcError(format!("Failed to fetch account {}: {:?}", address, e))
677                })?;
678                trace!(address = %address, account = ?account, "Loaded account basic from forked state");
679                Ok(account)
680            }
681        }
682    }
683
684    fn code_by_hash(
685        &mut self,
686        code_hash: alloy_primitives::B256,
687    ) -> std::result::Result<Bytecode, Self::Error> {
688        // Check code_map first (for prestate accounts)
689        if let Some(code) = self.code_map.get(&code_hash) {
690            trace!(code_hash = %code_hash, code = ?code, "Loaded code by hash from prestate");
691            return Ok(code.clone());
692        }
693
694        // Query backend database
695        match &mut self.backend {
696            EvmeBackend::Empty(db) => {
697                let code = db.code_by_hash(code_hash).unwrap();
698                trace!(code_hash = %code_hash, code = ?code, "Loaded code by hash from empty state");
699                Ok(code)
700            }
701            EvmeBackend::Forked(db) => {
702                let code = db.code_by_hash(code_hash).map_err(|e| {
703                    EvmeError::RpcError(format!(
704                        "Failed to fetch code by hash {}: {:?}",
705                        code_hash, e
706                    ))
707                })?;
708                trace!(code_hash = %code_hash, code = ?code, "Loaded code by hash from forked state");
709                Ok(code)
710            }
711        }
712    }
713
714    fn storage(&mut self, address: Address, index: U256) -> std::result::Result<U256, Self::Error> {
715        // Check storage overrides first
716        if let Some(account) = self.prestate.get(&address) {
717            if let Some(slot) = account.storage.get(&index) {
718                trace!(address = %address, index = %index, slot = %slot.present_value, "Loaded storage from prestate");
719                return Ok(slot.present_value);
720            }
721        }
722
723        // Query backend database
724        match &mut self.backend {
725            EvmeBackend::Empty(db) => {
726                let storage = db.storage(address, index).unwrap();
727                trace!(address = %address, index = %index, storage = %storage, "Loaded storage from empty state");
728                Ok(storage)
729            }
730            EvmeBackend::Forked(db) => {
731                let storage = db.storage(address, index).map_err(|e| {
732                    EvmeError::RpcError(format!(
733                        "Failed to fetch storage for {} at slot {}: {:?}",
734                        address, index, e
735                    ))
736                })?;
737                trace!(address = %address, index = %index, storage = %storage, "Loaded storage from forked state");
738                Ok(storage)
739            }
740        }
741    }
742
743    fn block_hash(
744        &mut self,
745        number: u64,
746    ) -> std::result::Result<alloy_primitives::B256, Self::Error> {
747        // Check block hash overrides first
748        if let Some(hash) = self.block_hashes.get(&number) {
749            trace!(number = %number, hash = %hash, "Loaded block hash from provided overrides");
750            return Ok(*hash);
751        }
752
753        // Query backend database
754        match &mut self.backend {
755            EvmeBackend::Empty(db) => {
756                let hash = db.block_hash(number).unwrap();
757                trace!(number = %number, hash = %hash, "Loaded block hash from empty state");
758                Ok(hash)
759            }
760            EvmeBackend::Forked(db) => {
761                let hash = db.block_hash(number).map_err(|e| {
762                    EvmeError::RpcError(format!(
763                        "Failed to fetch block hash for block {}: {:?}",
764                        number, e
765                    ))
766                })?;
767                trace!(number = %number, hash = %hash, "Loaded block hash from forked state");
768                Ok(hash)
769            }
770        }
771    }
772}
773
774impl<N, P> DatabaseRef for EvmeState<N, P>
775where
776    N: Network,
777    P: Provider<N> + std::fmt::Debug,
778{
779    type Error = EvmeError;
780
781    fn basic_ref(&self, address: Address) -> std::result::Result<Option<AccountInfo>, Self::Error> {
782        // Check prestate overrides first
783        if let Some(account) = self.prestate.get(&address) {
784            trace!(address = %address, account = ?account, "Loaded account basic from prestate");
785            return Ok(Some(account.info.clone()));
786        }
787
788        // Query backend database
789        match &self.backend {
790            EvmeBackend::Empty(db) => {
791                let account = db.basic_ref(address).unwrap();
792                trace!(address = %address, account = ?account, "Loaded account basic from empty state");
793                Ok(account)
794            }
795            EvmeBackend::Forked(db) => {
796                let account = db.basic_ref(address).map_err(|e| {
797                    EvmeError::RpcError(format!("Failed to fetch account {}: {:?}", address, e))
798                })?;
799                trace!(address = %address, account = ?account, "Loaded account basic from forked state");
800                Ok(account)
801            }
802        }
803    }
804
805    fn code_by_hash_ref(
806        &self,
807        code_hash: alloy_primitives::B256,
808    ) -> std::result::Result<Bytecode, Self::Error> {
809        // Check code_map first (for prestate accounts)
810        if let Some(code) = self.code_map.get(&code_hash) {
811            trace!(code_hash = %code_hash, code = ?code, "Loaded code by hash from prestate");
812            return Ok(code.clone());
813        }
814
815        // Query backend database
816        match &self.backend {
817            EvmeBackend::Empty(db) => {
818                let code = db.code_by_hash_ref(code_hash).unwrap();
819                trace!(code_hash = %code_hash, code = ?code, "Loaded code by hash from empty state");
820                Ok(code)
821            }
822            EvmeBackend::Forked(db) => {
823                let code = db.code_by_hash_ref(code_hash).map_err(|e| {
824                    EvmeError::RpcError(format!(
825                        "Failed to fetch code by hash {}: {:?}",
826                        code_hash, e
827                    ))
828                })?;
829                trace!(code_hash = %code_hash, code = ?code, "Loaded code by hash from forked state");
830                Ok(code)
831            }
832        }
833    }
834
835    fn storage_ref(&self, address: Address, index: U256) -> std::result::Result<U256, Self::Error> {
836        // Check storage overrides first
837        if let Some(account) = self.prestate.get(&address) {
838            if let Some(slot) = account.storage.get(&index) {
839                trace!(address = %address, index = %index, slot = %slot.present_value, "Loaded storage from prestate");
840                return Ok(slot.present_value);
841            }
842        }
843
844        // Query backend database
845        match &self.backend {
846            EvmeBackend::Empty(db) => {
847                let storage = db.storage_ref(address, index).unwrap();
848                trace!(address = %address, index = %index, storage = %storage, "Loaded storage from empty state");
849                Ok(storage)
850            }
851            EvmeBackend::Forked(db) => {
852                let storage = db.storage_ref(address, index).map_err(|e| {
853                    EvmeError::RpcError(format!(
854                        "Failed to fetch storage for {} at slot {}: {:?}",
855                        address, index, e
856                    ))
857                })?;
858                trace!(address = %address, index = %index, storage = %storage, "Loaded storage from forked state");
859                Ok(storage)
860            }
861        }
862    }
863
864    fn block_hash_ref(
865        &self,
866        number: u64,
867    ) -> std::result::Result<alloy_primitives::B256, Self::Error> {
868        // Check block hash overrides first
869        if let Some(hash) = self.block_hashes.get(&number) {
870            trace!(number = %number, hash = %hash, "Loaded block hash from provided overrides");
871            return Ok(*hash);
872        }
873
874        // Query backend database
875        match &self.backend {
876            EvmeBackend::Empty(db) => {
877                let hash = db.block_hash_ref(number).unwrap();
878                trace!(number = %number, hash = %hash, "Loaded block hash from empty state");
879                Ok(hash)
880            }
881            EvmeBackend::Forked(db) => {
882                let hash = db.block_hash_ref(number).map_err(|e| {
883                    EvmeError::RpcError(format!(
884                        "Failed to fetch block hash for block {}: {:?}",
885                        number, e
886                    ))
887                })?;
888                trace!(number = %number, hash = %hash, "Loaded block hash from forked state");
889                Ok(hash)
890            }
891        }
892    }
893}