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            flat_system_contract_specs, MegaSpecId, SEQUENCER_REGISTRY_ADDRESS,
554            SEQUENCER_REGISTRY_CODE,
555        };
556
557        // Flat predeploys (Oracle, high-precision timestamp Oracle, KeylessDeploy,
558        // MegaAccessControl, MegaLimitControl) come from the canonical registry shared
559        // with the block executor. mega-evme runs with a fixed spec, so activations are
560        // resolved via a `FixedHardfork` at timestamp 0, and the bytecode is applied as a
561        // raw state patch (no witness / storage seeding needed for local execution).
562        for contract in flat_system_contract_specs(super::FixedHardfork::new(spec), 0) {
563            self.set_account_code(contract.address, Bytecode::new_raw(contract.code));
564        }
565
566        // Rex5+: SequencerRegistry. Only the bytecode is installed here — a local run has
567        // no chain-config sequencer/admin to seed (the registry's storage is otherwise
568        // read from forked state).
569        if spec >= MegaSpecId::REX5 {
570            self.set_account_code(
571                SEQUENCER_REGISTRY_ADDRESS,
572                Bytecode::new_raw(SEQUENCER_REGISTRY_CODE),
573            );
574        }
575    }
576}
577
578// Impl block for methods that accept a generic provider
579impl<N, P> EvmeState<N, P>
580where
581    N: Network,
582    P: Provider<N>,
583{
584    /// Create a new forked state from a provider with optional prestate overrides and block hash
585    /// overrides
586    pub async fn new_forked(
587        provider: P,
588        fork_block: Option<u64>,
589        prestate: EvmState,
590        block_hashes: HashMap<u64, B256>,
591    ) -> Result<Self> {
592        // Determine block number
593        let block_num = if let Some(block_num) = fork_block {
594            BlockNumber::from(block_num)
595        } else {
596            // Fetch latest block number
597            let latest_block = provider
598                .get_block_number()
599                .await
600                .map_err(|e| EvmeError::RpcError(format!("Failed to fetch latest block: {}", e)))?;
601            BlockNumber::from(latest_block)
602        };
603
604        // Create AlloyDB with the provider and block number
605        let alloy_db = AlloyDB::new(provider, block_num.into());
606
607        // Wrap the AlloyDB for synchronous access with the runtime
608        let wrapped_db =
609            WrapDatabaseAsync::new(alloy_db).expect("Failed to create wrapped database");
610
611        // Wrap with CacheDB to enable mutable Database trait
612        let db = CacheDB::new(wrapped_db);
613
614        // Extract code hash → bytecode mappings from prestate
615        let code_map: HashMap<_, _> = prestate
616            .values()
617            .filter_map(|account| {
618                account.info.code.clone().map(|code| (account.info.code_hash, code))
619            })
620            .collect();
621
622        Ok(Self { backend: EvmeBackend::Forked(Box::new(db)), prestate, code_map, block_hashes })
623    }
624}
625
626impl<N, P> Database for EvmeState<N, P>
627where
628    N: Network,
629    P: Provider<N> + std::fmt::Debug,
630{
631    type Error = EvmeError;
632
633    fn basic(&mut self, address: Address) -> std::result::Result<Option<AccountInfo>, Self::Error> {
634        // Check prestate overrides first
635        if let Some(account) = self.prestate.get(&address) {
636            trace!(address = %address, account = ?account, "Loaded account basic from prestate");
637            return Ok(Some(account.info.clone()));
638        }
639
640        // Query backend database
641        match &mut self.backend {
642            EvmeBackend::Empty(db) => {
643                let account = db.basic(address).unwrap();
644                trace!(address = %address, account = ?account, "Loaded account basic from empty state");
645                Ok(account)
646            }
647            EvmeBackend::Forked(db) => {
648                let account = db.basic(address).map_err(|e| {
649                    EvmeError::RpcError(format!("Failed to fetch account {}: {:?}", address, e))
650                })?;
651                trace!(address = %address, account = ?account, "Loaded account basic from forked state");
652                Ok(account)
653            }
654        }
655    }
656
657    fn code_by_hash(
658        &mut self,
659        code_hash: alloy_primitives::B256,
660    ) -> std::result::Result<Bytecode, Self::Error> {
661        // Check code_map first (for prestate accounts)
662        if let Some(code) = self.code_map.get(&code_hash) {
663            trace!(code_hash = %code_hash, code = ?code, "Loaded code by hash from prestate");
664            return Ok(code.clone());
665        }
666
667        // Query backend database
668        match &mut self.backend {
669            EvmeBackend::Empty(db) => {
670                let code = db.code_by_hash(code_hash).unwrap();
671                trace!(code_hash = %code_hash, code = ?code, "Loaded code by hash from empty state");
672                Ok(code)
673            }
674            EvmeBackend::Forked(db) => {
675                let code = db.code_by_hash(code_hash).map_err(|e| {
676                    EvmeError::RpcError(format!(
677                        "Failed to fetch code by hash {}: {:?}",
678                        code_hash, e
679                    ))
680                })?;
681                trace!(code_hash = %code_hash, code = ?code, "Loaded code by hash from forked state");
682                Ok(code)
683            }
684        }
685    }
686
687    fn storage(&mut self, address: Address, index: U256) -> std::result::Result<U256, Self::Error> {
688        // Check storage overrides first
689        if let Some(account) = self.prestate.get(&address) {
690            if let Some(slot) = account.storage.get(&index) {
691                trace!(address = %address, index = %index, slot = %slot.present_value, "Loaded storage from prestate");
692                return Ok(slot.present_value);
693            }
694        }
695
696        // Query backend database
697        match &mut self.backend {
698            EvmeBackend::Empty(db) => {
699                let storage = db.storage(address, index).unwrap();
700                trace!(address = %address, index = %index, storage = %storage, "Loaded storage from empty state");
701                Ok(storage)
702            }
703            EvmeBackend::Forked(db) => {
704                let storage = db.storage(address, index).map_err(|e| {
705                    EvmeError::RpcError(format!(
706                        "Failed to fetch storage for {} at slot {}: {:?}",
707                        address, index, e
708                    ))
709                })?;
710                trace!(address = %address, index = %index, storage = %storage, "Loaded storage from forked state");
711                Ok(storage)
712            }
713        }
714    }
715
716    fn block_hash(
717        &mut self,
718        number: u64,
719    ) -> std::result::Result<alloy_primitives::B256, Self::Error> {
720        // Check block hash overrides first
721        if let Some(hash) = self.block_hashes.get(&number) {
722            trace!(number = %number, hash = %hash, "Loaded block hash from provided overrides");
723            return Ok(*hash);
724        }
725
726        // Query backend database
727        match &mut self.backend {
728            EvmeBackend::Empty(db) => {
729                let hash = db.block_hash(number).unwrap();
730                trace!(number = %number, hash = %hash, "Loaded block hash from empty state");
731                Ok(hash)
732            }
733            EvmeBackend::Forked(db) => {
734                let hash = db.block_hash(number).map_err(|e| {
735                    EvmeError::RpcError(format!(
736                        "Failed to fetch block hash for block {}: {:?}",
737                        number, e
738                    ))
739                })?;
740                trace!(number = %number, hash = %hash, "Loaded block hash from forked state");
741                Ok(hash)
742            }
743        }
744    }
745}
746
747impl<N, P> DatabaseRef for EvmeState<N, P>
748where
749    N: Network,
750    P: Provider<N> + std::fmt::Debug,
751{
752    type Error = EvmeError;
753
754    fn basic_ref(&self, address: Address) -> std::result::Result<Option<AccountInfo>, Self::Error> {
755        // Check prestate overrides first
756        if let Some(account) = self.prestate.get(&address) {
757            trace!(address = %address, account = ?account, "Loaded account basic from prestate");
758            return Ok(Some(account.info.clone()));
759        }
760
761        // Query backend database
762        match &self.backend {
763            EvmeBackend::Empty(db) => {
764                let account = db.basic_ref(address).unwrap();
765                trace!(address = %address, account = ?account, "Loaded account basic from empty state");
766                Ok(account)
767            }
768            EvmeBackend::Forked(db) => {
769                let account = db.basic_ref(address).map_err(|e| {
770                    EvmeError::RpcError(format!("Failed to fetch account {}: {:?}", address, e))
771                })?;
772                trace!(address = %address, account = ?account, "Loaded account basic from forked state");
773                Ok(account)
774            }
775        }
776    }
777
778    fn code_by_hash_ref(
779        &self,
780        code_hash: alloy_primitives::B256,
781    ) -> std::result::Result<Bytecode, Self::Error> {
782        // Check code_map first (for prestate accounts)
783        if let Some(code) = self.code_map.get(&code_hash) {
784            trace!(code_hash = %code_hash, code = ?code, "Loaded code by hash from prestate");
785            return Ok(code.clone());
786        }
787
788        // Query backend database
789        match &self.backend {
790            EvmeBackend::Empty(db) => {
791                let code = db.code_by_hash_ref(code_hash).unwrap();
792                trace!(code_hash = %code_hash, code = ?code, "Loaded code by hash from empty state");
793                Ok(code)
794            }
795            EvmeBackend::Forked(db) => {
796                let code = db.code_by_hash_ref(code_hash).map_err(|e| {
797                    EvmeError::RpcError(format!(
798                        "Failed to fetch code by hash {}: {:?}",
799                        code_hash, e
800                    ))
801                })?;
802                trace!(code_hash = %code_hash, code = ?code, "Loaded code by hash from forked state");
803                Ok(code)
804            }
805        }
806    }
807
808    fn storage_ref(&self, address: Address, index: U256) -> std::result::Result<U256, Self::Error> {
809        // Check storage overrides first
810        if let Some(account) = self.prestate.get(&address) {
811            if let Some(slot) = account.storage.get(&index) {
812                trace!(address = %address, index = %index, slot = %slot.present_value, "Loaded storage from prestate");
813                return Ok(slot.present_value);
814            }
815        }
816
817        // Query backend database
818        match &self.backend {
819            EvmeBackend::Empty(db) => {
820                let storage = db.storage_ref(address, index).unwrap();
821                trace!(address = %address, index = %index, storage = %storage, "Loaded storage from empty state");
822                Ok(storage)
823            }
824            EvmeBackend::Forked(db) => {
825                let storage = db.storage_ref(address, index).map_err(|e| {
826                    EvmeError::RpcError(format!(
827                        "Failed to fetch storage for {} at slot {}: {:?}",
828                        address, index, e
829                    ))
830                })?;
831                trace!(address = %address, index = %index, storage = %storage, "Loaded storage from forked state");
832                Ok(storage)
833            }
834        }
835    }
836
837    fn block_hash_ref(
838        &self,
839        number: u64,
840    ) -> std::result::Result<alloy_primitives::B256, Self::Error> {
841        // Check block hash overrides first
842        if let Some(hash) = self.block_hashes.get(&number) {
843            trace!(number = %number, hash = %hash, "Loaded block hash from provided overrides");
844            return Ok(*hash);
845        }
846
847        // Query backend database
848        match &self.backend {
849            EvmeBackend::Empty(db) => {
850                let hash = db.block_hash_ref(number).unwrap();
851                trace!(number = %number, hash = %hash, "Loaded block hash from empty state");
852                Ok(hash)
853            }
854            EvmeBackend::Forked(db) => {
855                let hash = db.block_hash_ref(number).map_err(|e| {
856                    EvmeError::RpcError(format!(
857                        "Failed to fetch block hash for block {}: {:?}",
858                        number, e
859                    ))
860                })?;
861                trace!(number = %number, hash = %hash, "Loaded block hash from forked state");
862                Ok(hash)
863            }
864        }
865    }
866}