tycho_simulation/evm/engine_db/
mod.rs

1use std::{collections::HashMap, fmt::Debug};
2
3use alloy::primitives::{Address, U160};
4use lazy_static::lazy_static;
5use revm::{primitives::KECCAK_EMPTY, state::AccountInfo, DatabaseRef};
6use tycho_client::feed::BlockHeader;
7use tycho_common::simulation::errors::SimulationError;
8
9use crate::evm::{
10    engine_db::{
11        engine_db_interface::EngineDatabaseInterface,
12        tycho_db::{PreCachedDB, PreCachedDBError},
13    },
14    simulation::SimulationEngine,
15    tycho_models::{AccountUpdate, ChangeType, ResponseAccount},
16};
17
18pub mod engine_db_interface;
19pub mod simulation_db;
20pub mod tycho_db;
21pub mod utils;
22
23lazy_static! {
24    pub static ref SHARED_TYCHO_DB: PreCachedDB =
25        PreCachedDB::new().unwrap_or_else(|err| panic!("Failed to create PreCachedDB: {err}"));
26}
27
28/// Creates a simulation engine.
29///
30/// # Parameters
31///
32/// - `trace`: Whether to trace calls. Only meant for debugging purposes, might print a lot of data
33///   to stdout.
34pub fn create_engine<D: EngineDatabaseInterface + Clone + Debug>(
35    db: D,
36    trace: bool,
37) -> Result<SimulationEngine<D>, SimulationError>
38where
39    <D as EngineDatabaseInterface>::Error: Debug,
40    <D as DatabaseRef>::Error: Debug,
41{
42    let engine = SimulationEngine::new(db.clone(), trace);
43
44    let zero_account_info =
45        AccountInfo { balance: Default::default(), nonce: 0, code_hash: KECCAK_EMPTY, code: None };
46
47    // Accounts necessary for enabling pre-compilation are initialized by default.
48    engine
49        .state
50        .init_account(Address::ZERO, zero_account_info.clone(), None, false)
51        .map_err(|e| {
52            SimulationError::FatalError(format!("Failed to init zero address: {:?}", e))
53        })?;
54
55    engine
56        .state
57        .init_account(Address::from(U160::from(4)), zero_account_info.clone(), None, false)
58        .map_err(|e| {
59            SimulationError::FatalError(format!(
60                "Failed to init ecrecover precompile address: {:?}",
61                e
62            ))
63        })?;
64
65    Ok(engine)
66}
67
68pub fn update_engine(
69    db: PreCachedDB,
70    block: Option<BlockHeader>,
71    vm_storage: Option<HashMap<Address, ResponseAccount>>,
72    account_updates: HashMap<Address, AccountUpdate>,
73) -> Result<Vec<AccountUpdate>, PreCachedDBError> {
74    if let Some(block) = block {
75        let mut vm_updates: Vec<AccountUpdate> = Vec::new();
76
77        for (_address, account_update) in account_updates.iter() {
78            vm_updates.push(account_update.clone());
79        }
80
81        if let Some(vm_storage_values) = vm_storage {
82            for (_address, vm_storage_values) in vm_storage_values.iter() {
83                // ResponseAccount objects to AccountUpdate objects as required by the update method
84                vm_updates.push(AccountUpdate {
85                    address: vm_storage_values.address,
86                    chain: vm_storage_values.chain,
87                    slots: vm_storage_values.slots.clone(),
88                    balance: Some(vm_storage_values.native_balance),
89                    code: Some(vm_storage_values.code.clone()),
90                    change: ChangeType::Creation,
91                });
92            }
93        }
94
95        if !vm_updates.is_empty() {
96            db.update(vm_updates.clone(), Some(block))?;
97        }
98
99        Ok(vm_updates)
100    } else {
101        Ok(vec![])
102    }
103}