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