Skip to main content

tycho_simulation/evm/
simulation.rs

1use std::{clone::Clone, collections::HashMap, default::Default, env, fmt::Debug};
2
3use alloy::primitives::{Address, Bytes, U256};
4use revm::{
5    context::{
6        result::{EVMError, ExecutionResult, Output, ResultAndState},
7        BlockEnv, CfgEnv, Context, TxEnv,
8    },
9    context_interface::JournalTr,
10    interpreter::{return_ok, InstructionResult},
11    primitives::{hardfork::SpecId, TxKind},
12    state::EvmState,
13    DatabaseRef, ExecuteEvm, InspectEvm, MainBuilder, MainContext,
14};
15use revm_inspectors::tracing::{TracingInspector, TracingInspectorConfig};
16use strum_macros::Display;
17use tokio::runtime::{Handle, Runtime};
18use tracing::debug;
19
20use super::{
21    account_storage::StateUpdate,
22    traces::{handle_traces, TraceResult},
23};
24use crate::evm::engine_db::{
25    engine_db_interface::EngineDatabaseInterface, simulation_db::OverriddenSimulationDB,
26};
27
28/// An error representing any transaction simulation result other than successful execution
29#[derive(Debug, Display, Clone, PartialEq)]
30pub enum SimulationEngineError {
31    /// Something went wrong while getting storage; might be caused by network issues.
32    /// Retrying may help.
33    StorageError(String),
34    /// Gas limit has been reached. Retrying while increasing gas limit or waiting for a gas price
35    /// reduction may help.
36    OutOfGas(String, String),
37    /// Simulation didn't succeed; likely not related to network or gas, so retrying won't help
38    TransactionError { data: String, gas_used: Option<u64> },
39    /// Processing traces failed.
40    TraceError(String),
41}
42
43/// A result of a successful transaction simulation
44#[derive(Debug, Clone, Default)]
45pub struct SimulationResult {
46    /// Output of transaction execution as bytes
47    pub result: Bytes,
48    /// State changes caused by the transaction
49    pub state_updates: HashMap<Address, StateUpdate>,
50    /// Gas used by the transaction (already reduced by the refunded gas)
51    pub gas_used: u64,
52    /// Transient storage changes captured during the simulation
53    pub transient_storage: HashMap<Address, HashMap<U256, U256>>,
54}
55
56/// Simulation engine
57#[derive(Debug, Clone)]
58pub struct SimulationEngine<D: EngineDatabaseInterface + Clone + Debug>
59where
60    <D as DatabaseRef>::Error: Debug,
61    <D as EngineDatabaseInterface>::Error: Debug,
62{
63    pub state: D,
64    pub trace: bool,
65}
66
67impl<D: EngineDatabaseInterface + Clone + Debug> SimulationEngine<D>
68where
69    <D as DatabaseRef>::Error: Debug,
70    <D as EngineDatabaseInterface>::Error: Debug,
71{
72    /// Create a new simulation engine
73    ///
74    /// # Arguments
75    ///
76    /// * `state` - Database reference to be used for simulation
77    /// * `trace` - Whether to print the entire execution trace
78    ///
79    /// # Notes
80    /// If you set traces to true, consider setting the ETHERSCAN_API_KEY env variable
81    /// so the tracer can pull contract metadata from etherscan.
82    pub fn new(state: D, trace: bool) -> Self {
83        Self { state, trace }
84    }
85
86    /// Simulate a transaction
87    ///
88    /// State's block will be modified to be the last block before the simulation's block.
89    pub fn simulate(
90        &self,
91        params: &SimulationParameters,
92    ) -> Result<SimulationResult, SimulationEngineError> {
93        // We allocate a new EVM so we can work with a simple referenced DB instead of a fully
94        // concurrently save shared reference and write locked object. Note that concurrently
95        // calling this method is therefore not possible.
96        // There is no need to keep an EVM on the struct as it only holds the environment and the
97        // db, the db is simply a reference wrapper. To avoid lifetimes leaking we don't let the evm
98        // struct outlive this scope.
99
100        // Borrowed, not cloned: an override map can hold every storage slot a pending block
101        // touched, and this runs once per pool.
102        let no_storage_overrides = HashMap::new();
103        let no_balance_overrides = HashMap::new();
104        let db_ref = OverriddenSimulationDB {
105            inner_db: &self.state,
106            overrides: params
107                .overrides
108                .as_ref()
109                .unwrap_or(&no_storage_overrides),
110            native_balance_overrides: params
111                .native_balance_overrides
112                .as_ref()
113                .unwrap_or(&no_balance_overrides),
114        };
115
116        let tx_env = TxEnv {
117            caller: params.caller,
118            gas_limit: params.gas_limit.unwrap_or(8_000_000),
119            kind: TxKind::Call(params.to),
120            value: params.value,
121            data: Bytes::copy_from_slice(&params.data),
122            ..Default::default()
123        };
124
125        let mut block =
126            self.state
127                .get_current_block()
128                .ok_or(SimulationEngineError::StorageError(
129                    "Current block not set in SimulationEngine.".into(),
130                ))?;
131
132        if let Some(overrides) = &params.block_overrides {
133            if let Some(number) = overrides.number {
134                block.number = number;
135            }
136            if let Some(timestamp) = overrides.timestamp {
137                block.timestamp = timestamp;
138            }
139        }
140
141        let block_env = BlockEnv {
142            number: U256::from(block.number),
143            timestamp: U256::from(block.timestamp),
144            ..Default::default()
145        };
146
147        let mut cfg_env: CfgEnv<SpecId> = CfgEnv::new_with_spec(SpecId::PRAGUE);
148        cfg_env.disable_nonce_check = true;
149        cfg_env.disable_eip3607 = true;
150
151        let context = Context::mainnet()
152            .with_cfg(cfg_env)
153            .with_ref_db(db_ref)
154            .with_block(block_env)
155            .with_tx(tx_env.clone())
156            .modify_journal_chained(|journal| {
157                if let Some(transient_storage) = params.transient_storage.clone() {
158                    for (address, slots) in transient_storage {
159                        for (slot, value) in slots {
160                            journal.tstore(address, slot, value);
161                        }
162                    }
163                }
164                if let Some(overrides) = &params.overrides {
165                    for (address, storage) in overrides {
166                        let keys = storage.keys().copied();
167                        let _ = journal.warm_account_and_storage(*address, keys);
168                    }
169                }
170            });
171
172        let evm_result = if self.trace {
173            let mut tracer = TracingInspector::new(TracingInspectorConfig::default());
174
175            let res = {
176                let mut vm = context.build_mainnet_with_inspector(&mut tracer);
177
178                debug!(
179                    "Starting simulation with tx parameters: {:#?} {:#?}",
180                    vm.ctx.tx, vm.ctx.block
181                );
182                vm.inspect_tx(tx_env.clone())
183            };
184
185            Self::print_traces(tracer, res.as_ref().ok())?;
186
187            res
188        } else {
189            let mut vm = context.build_mainnet();
190
191            debug!("Starting simulation with tx parameters: {:#?} {:#?}", vm.ctx.tx, vm.ctx.block);
192
193            vm.replay()
194        };
195
196        // TODO: update revm to 25.0.0 and get transient storage from the journaled state
197        interpret_evm_result(evm_result, HashMap::new())
198    }
199
200    pub fn clear_temp_storage(&mut self) -> Result<(), <D as EngineDatabaseInterface>::Error> {
201        self.state.clear_temp_storage()
202    }
203
204    fn print_traces(
205        tracer: TracingInspector,
206        res: Option<&ResultAndState>,
207    ) -> Result<(), SimulationEngineError> {
208        let (exit_reason, _gas_refunded, gas_used, _out, _exec_logs) = match res {
209            Some(ResultAndState { result, state: _ }) => {
210                // let ResultAndState { result, state: _ } = res;
211                match result.clone() {
212                    ExecutionResult::Success {
213                        reason,
214                        gas_used,
215                        gas_refunded,
216                        output,
217                        logs,
218                        ..
219                    } => (reason.into(), gas_refunded, gas_used, Some(output), logs),
220                    ExecutionResult::Revert { gas_used, output } => {
221                        // Need to fetch the unused gas
222                        (
223                            InstructionResult::Revert,
224                            0_u64,
225                            gas_used,
226                            Some(Output::Call(output)),
227                            vec![],
228                        )
229                    }
230                    ExecutionResult::Halt { reason, gas_used } => {
231                        (reason.into(), 0_u64, gas_used, None, vec![])
232                    }
233                }
234            }
235            _ => (InstructionResult::Stop, 0_u64, 0, None, vec![]),
236        };
237
238        let trace_res = TraceResult {
239            success: matches!(exit_reason, return_ok!()),
240            traces: Some(vec![tracer.into_traces()]),
241            gas_used,
242        };
243
244        tokio::task::block_in_place(|| -> Result<(), SimulationEngineError> {
245            let future = async {
246                handle_traces(
247                    trace_res,
248                    env::var("ETHERSCAN_API_KEY").ok(),
249                    tycho_common::models::Chain::Ethereum,
250                )
251                .await
252                .map_err(|err| SimulationEngineError::TraceError(err.to_string()))
253            };
254            if let Ok(handle) = Handle::try_current() {
255                // If successful, use the existing runtime to block on the future
256                handle.block_on(future)
257            } else {
258                // If no runtime is found, create a new one and block on the future
259                let rt = Runtime::new().map_err(|err| {
260                    SimulationEngineError::TraceError(format!(
261                        "Failed to create a new runtime: {err}"
262                    ))
263                })?;
264                rt.block_on(future)
265            }
266        })?;
267
268        Ok(())
269    }
270}
271
272/// Convert a complex EVMResult into a simpler structure
273///
274/// EVMResult is not of an error type even if the transaction was not successful.
275/// This function returns an Ok if and only if the transaction was successful.
276/// In case the transaction was reverted, halted, or another error occurred (like an error
277/// when accessing storage), this function returns an Err with a simple String description
278/// of an underlying cause.
279///
280/// # Arguments
281///
282/// * `evm_result` - output from calling `revm.transact()`
283///
284/// # Errors
285///
286/// * `SimulationError` - simulation wasn't successful for any reason. See variants for details.
287fn interpret_evm_result<DBError: Debug>(
288    evm_result: Result<ResultAndState, EVMError<DBError>>,
289    transient_storage: HashMap<Address, HashMap<U256, U256>>,
290) -> Result<SimulationResult, SimulationEngineError> {
291    match evm_result {
292        Ok(result_and_state) => match result_and_state.result {
293            ExecutionResult::Success { gas_used, gas_refunded, output, .. } => {
294                Ok(interpret_evm_success(
295                    gas_used,
296                    gas_refunded,
297                    output,
298                    result_and_state.state,
299                    transient_storage,
300                ))
301            }
302            ExecutionResult::Revert { output, gas_used } => {
303                Err(SimulationEngineError::TransactionError {
304                    data: format!("0x{encoded}", encoded = hex::encode::<Vec<u8>>(output.into())),
305                    gas_used: Some(gas_used),
306                })
307            }
308            ExecutionResult::Halt { reason, gas_used } => {
309                Err(SimulationEngineError::TransactionError {
310                    data: format!("{reason:?}"),
311                    gas_used: Some(gas_used),
312                })
313            }
314        },
315        Err(evm_error) => match evm_error {
316            EVMError::Transaction(invalid_tx) => Err(SimulationEngineError::TransactionError {
317                data: format!("EVM error: {invalid_tx:?}"),
318                gas_used: None,
319            }),
320            EVMError::Database(db_error) => {
321                Err(SimulationEngineError::StorageError(format!("Storage error: {db_error:?}")))
322            }
323            EVMError::Custom(err) => Err(SimulationEngineError::TransactionError {
324                data: format!("Unexpected error {err}"),
325                gas_used: None,
326            }),
327            EVMError::Header(err) => Err(SimulationEngineError::TransactionError {
328                data: format!("Unexpected error {err}"),
329                gas_used: None,
330            }),
331        },
332    }
333}
334
335// Helper function to extract some details from a successful transaction execution
336fn interpret_evm_success(
337    gas_used: u64,
338    gas_refunded: u64,
339    output: Output,
340    state: EvmState,
341    transient_storage: HashMap<Address, HashMap<U256, U256>>,
342) -> SimulationResult {
343    SimulationResult {
344        result: output.into_data(),
345        state_updates: {
346            // For each account mentioned in state updates in REVM output, we will have
347            // one record in our hashmap. Such record contains *new* values of account's
348            // state. This record's optional `storage` field will contain
349            // account's storage changes (as a hashmap from slot index to slot value),
350            // unless REVM output doesn't contain any storage for this account, in which case
351            // we set this field to None. If REVM did return storage, we return one record
352            // per *modified* slot (sometimes REVM returns a storage record for an account
353            // even if the slots are not modified).
354            let mut account_updates: HashMap<Address, StateUpdate> = HashMap::new();
355            for (address, account) in state {
356                account_updates.insert(
357                    address,
358                    StateUpdate {
359                        // revm doesn't say if the balance was actually changed
360                        balance: Some(account.info.balance),
361                        // revm doesn't say if the code was actually changed
362                        storage: {
363                            if account.storage.is_empty() {
364                                None
365                            } else {
366                                let mut slot_updates: HashMap<U256, U256> = HashMap::new();
367                                for (index, slot) in account.storage {
368                                    if slot.is_changed() {
369                                        slot_updates.insert(index, slot.present_value);
370                                    }
371                                }
372                                if slot_updates.is_empty() {
373                                    None
374                                } else {
375                                    Some(slot_updates)
376                                }
377                            }
378                        },
379                    },
380                );
381            }
382            account_updates
383        },
384        gas_used: gas_used - gas_refunded,
385        transient_storage,
386    }
387}
388
389#[derive(Debug, Default)]
390/// Data needed to invoke a transaction simulation
391pub struct SimulationParameters {
392    /// Address of the sending account
393    pub caller: Address,
394    /// Address of the receiving account/contract
395    pub to: Address,
396    /// Calldata
397    pub data: Vec<u8>,
398    /// Amount of native token sent
399    pub value: U256,
400    /// EVM state overrides.
401    /// Will be merged with existing state. Will take effect only for current simulation.
402    pub overrides: Option<HashMap<Address, HashMap<U256, U256>>>,
403    /// Limit of gas to be used by the transaction
404    pub gas_limit: Option<u64>,
405    /// Map of the address whose transient storage will be overwritten, to a map of storage slot
406    /// and value.
407    pub transient_storage: Option<HashMap<Address, HashMap<U256, U256>>>,
408    /// Per-call block context overrides.
409    pub block_overrides: Option<BlockEnvOverrides>,
410    /// Native balance overrides. Same per-call scoping as `overrides`.
411    pub native_balance_overrides: Option<HashMap<Address, U256>>,
412}
413
414#[derive(Debug, Clone, Default, PartialEq, Eq)]
415pub struct BlockEnvOverrides {
416    pub number: Option<u64>,
417    pub timestamp: Option<u64>,
418}
419
420/// State a view call runs against, overriding what the engine's database holds.
421///
422/// `Default` means no overrides, so a call reads the engine's confirmed state.
423#[derive(Debug, Clone, Default)]
424pub struct PendingOverrides {
425    pub storage: Option<HashMap<Address, HashMap<U256, U256>>>,
426    pub native_balances: Option<HashMap<Address, U256>>,
427    pub block: Option<BlockEnvOverrides>,
428}
429
430impl PendingOverrides {
431    /// Parameters for a `data` view call to `to` from the zero address, under these overrides.
432    ///
433    /// Clones the override maps, which `SimulationParameters` owns. Callers that issue many
434    /// calls per pending block pay that clone per call.
435    pub fn view_call(&self, to: Address, data: Vec<u8>) -> SimulationParameters {
436        SimulationParameters {
437            caller: Address::ZERO,
438            to,
439            data,
440            overrides: self.storage.clone(),
441            native_balance_overrides: self.native_balances.clone(),
442            block_overrides: self.block.clone(),
443            ..Default::default()
444        }
445    }
446}
447
448#[cfg(test)]
449mod pending_overrides_tests {
450    use std::collections::HashMap;
451
452    use alloy::primitives::{Address, U256};
453
454    use super::{BlockEnvOverrides, PendingOverrides};
455
456    /// Every override a caller sets must reach the parameters, or a pool would silently be
457    /// priced against confirmed state.
458    #[test]
459    fn test_view_call_carries_every_override() {
460        let account = Address::repeat_byte(7);
461        let overrides = PendingOverrides {
462            storage: Some(HashMap::from([(account, HashMap::from([(U256::ZERO, U256::from(1))]))])),
463            native_balances: Some(HashMap::from([(account, U256::from(2))])),
464            block: Some(BlockEnvOverrides { number: Some(3), timestamp: Some(4) }),
465        };
466
467        let params = overrides.view_call(account, vec![0xab]);
468
469        assert_eq!(params.overrides, overrides.storage);
470        assert_eq!(params.native_balance_overrides, overrides.native_balances);
471        assert_eq!(params.block_overrides, overrides.block);
472        assert_eq!(params.caller, Address::ZERO, "A view call must not impersonate an account.");
473        assert_eq!(params.to, account);
474        assert_eq!(params.data, vec![0xab]);
475        assert_eq!(params.value, U256::ZERO, "A view call must not transfer value.");
476    }
477
478    #[test]
479    fn test_default_view_call_overrides_nothing() {
480        let params = PendingOverrides::default().view_call(Address::ZERO, Vec::new());
481
482        assert!(params.overrides.is_none());
483        assert!(params
484            .native_balance_overrides
485            .is_none());
486        assert!(params.block_overrides.is_none());
487    }
488}
489
490#[cfg(test)]
491mod tests {
492    use std::{error::Error, str::FromStr, time::Instant};
493
494    use alloy::{
495        primitives::{Address, Bytes, Keccak256, B256},
496        sol_types::SolValue,
497        transports::{RpcError, TransportError, TransportErrorKind},
498    };
499    use revm::{
500        context::result::{HaltReason, InvalidTransaction, OutOfGasError, SuccessReason},
501        state::{
502            Account, AccountInfo, AccountStatus, Bytecode, EvmState as rState, EvmStorageSlot,
503        },
504    };
505    use tycho_client::feed::BlockHeader;
506    use tycho_common::simulation::errors::SimulationError;
507
508    use super::*;
509    use crate::evm::engine_db::{
510        engine_db_interface::EngineDatabaseInterface,
511        simulation_db::{EVMProvider, SimulationDB},
512        tycho_db::PreCachedDB,
513        utils::{get_client, get_runtime},
514    };
515
516    #[test]
517    fn test_interpret_result_ok_success() {
518        let evm_result: Result<ResultAndState, EVMError<TransportError>> = Ok(ResultAndState {
519            result: ExecutionResult::Success {
520                reason: SuccessReason::Return,
521                gas_used: 100_u64,
522                gas_refunded: 10_u64,
523                logs: Vec::new(),
524                output: Output::Call(Bytes::from_static(b"output")),
525            },
526            state: [(
527                // storage has changed
528                Address::ZERO,
529                Account {
530                    info: AccountInfo {
531                        balance: U256::from_limbs([1, 0, 0, 0]),
532                        nonce: 2,
533                        code_hash: B256::ZERO,
534                        code: None,
535                    },
536                    transaction_id: 0,
537                    storage: [
538                        // this slot has changed
539                        (
540                            U256::from_limbs([3, 1, 0, 0]),
541                            EvmStorageSlot {
542                                original_value: U256::from_limbs([4, 0, 0, 0]),
543                                present_value: U256::from_limbs([5, 0, 0, 0]),
544                                transaction_id: 0,
545                                is_cold: true,
546                            },
547                        ),
548                        // this slot hasn't changed
549                        (
550                            U256::from_limbs([3, 2, 0, 0]),
551                            EvmStorageSlot {
552                                original_value: U256::from_limbs([4, 0, 0, 0]),
553                                present_value: U256::from_limbs([4, 0, 0, 0]),
554                                transaction_id: 0,
555                                is_cold: true,
556                            },
557                        ),
558                    ]
559                    .iter()
560                    .cloned()
561                    .collect(),
562                    status: AccountStatus::Touched,
563                },
564            )]
565            .iter()
566            .cloned()
567            .collect(),
568        });
569
570        let transient_storage = HashMap::from([(
571            Address::from_str("0x1f98400000000000000000000000000000000004").unwrap(),
572            HashMap::from([(U256::from(0), U256::from(1))]),
573        )]);
574        let result = interpret_evm_result(evm_result, transient_storage.clone());
575        let simulation_result = result.unwrap();
576
577        assert_eq!(simulation_result.result, Bytes::from_static(b"output"));
578        let expected_state_updates = [(
579            Address::ZERO,
580            StateUpdate {
581                storage: Some(
582                    [(U256::from_limbs([3, 1, 0, 0]), U256::from_limbs([5, 0, 0, 0]))]
583                        .iter()
584                        .cloned()
585                        .collect(),
586                ),
587                balance: Some(U256::from_limbs([1, 0, 0, 0])),
588            },
589        )]
590        .iter()
591        .cloned()
592        .collect();
593        assert_eq!(simulation_result.state_updates, expected_state_updates);
594        assert_eq!(simulation_result.gas_used, 90);
595        assert_eq!(simulation_result.transient_storage, transient_storage);
596    }
597
598    #[test]
599    fn test_interpret_result_ok_revert() {
600        let evm_result: Result<ResultAndState, EVMError<TransportError>> = Ok(ResultAndState {
601            result: ExecutionResult::Revert {
602                gas_used: 100_u64,
603                output: Bytes::from_static(b"output"),
604            },
605            state: rState::default(),
606        });
607
608        let result = interpret_evm_result(evm_result, HashMap::new());
609
610        assert!(result.is_err());
611        let err = result.err().unwrap();
612        match err {
613            SimulationEngineError::TransactionError { data: _, gas_used } => {
614                assert_eq!(
615                    format!("0x{}", hex::encode::<Vec<u8>>("output".into())),
616                    "0x6f7574707574"
617                );
618                assert_eq!(gas_used, Some(100));
619            }
620            _ => panic!("Wrong type of SimulationError!"),
621        }
622    }
623
624    #[test]
625    fn test_interpret_result_ok_halt() {
626        let evm_result: Result<ResultAndState, EVMError<TransportError>> = Ok(ResultAndState {
627            result: ExecutionResult::Halt {
628                reason: HaltReason::OutOfGas(OutOfGasError::Basic),
629                gas_used: 100_u64,
630            },
631            state: rState::default(),
632        });
633
634        let result = interpret_evm_result(evm_result, HashMap::new());
635
636        assert!(result.is_err());
637        let err = result.err().unwrap();
638        match err {
639            SimulationEngineError::TransactionError { data, gas_used } => {
640                assert_eq!(data, "OutOfGas(Basic)");
641                assert_eq!(gas_used, Some(100));
642            }
643            _ => panic!("Wrong type of SimulationError!"),
644        }
645    }
646
647    #[test]
648    fn test_interpret_result_err_invalid_transaction() {
649        let evm_result: Result<ResultAndState, EVMError<TransportError>> =
650            Err(EVMError::Transaction(InvalidTransaction::PriorityFeeGreaterThanMaxFee));
651
652        let result = interpret_evm_result(evm_result, HashMap::new());
653
654        assert!(result.is_err());
655        let err = result.err().unwrap();
656        match err {
657            SimulationEngineError::TransactionError { data, gas_used } => {
658                assert_eq!(data, "EVM error: PriorityFeeGreaterThanMaxFee");
659                assert_eq!(gas_used, None);
660            }
661            _ => panic!("Wrong type of SimulationError!"),
662        }
663    }
664
665    #[test]
666    fn test_interpret_result_err_db_error() {
667        let evm_result: Result<ResultAndState, EVMError<TransportError>> = Err(EVMError::Database(
668            RpcError::Transport(TransportErrorKind::Custom(Box::from("boo".to_string()))),
669        ));
670
671        let result = interpret_evm_result(evm_result, HashMap::new());
672
673        assert!(result.is_err());
674        let err = result.err().unwrap();
675        match err {
676            SimulationEngineError::StorageError(msg) => {
677                assert_eq!(msg, "Storage error: Transport(Custom(\"boo\"))")
678            }
679            _ => panic!("Wrong type of SimulationError!"),
680        }
681    }
682    fn new_state() -> SimulationDB<EVMProvider> {
683        let runtime = get_runtime().expect("Failed to create test runtime");
684        let client = get_client(None).expect("Failed to create test client");
685        SimulationDB::new(client, runtime, None)
686    }
687
688    #[test]
689    fn test_simulate_applies_block_env_overrides() -> Result<(), Box<dyn Error>> {
690        let state = PreCachedDB::new()?;
691        let contract = Address::from_str("0x0000000000000000000000000000000000001234")?;
692        // Minimal runtime bytecode equivalent to the following Solidity contract:
693        //
694        // // SPDX-License-Identifier: UNLICENSED
695        // pragma solidity ^0.8.26;
696        //
697        // contract BlockNumberTest {
698        //     function test() external view returns (uint256) {
699        //         return block.number;
700        //     }
701        // }
702        let bytecode = Bytecode::new_raw(Bytes::from_static(&[
703            0x43, // NUMBER
704            0x60, 0x00, // PUSH1 0
705            0x52, // MSTORE
706            0x60, 0x20, // PUSH1 32
707            0x60, 0x00, // PUSH1 0
708            0xf3, // RETURN
709        ]));
710        let account = AccountInfo::new(U256::ZERO, 0, bytecode.hash_slow(), bytecode);
711        state.init_account(contract, account, None, true)?;
712        state.init_account(Address::ZERO, AccountInfo::default(), None, true)?;
713        state.update(
714            Vec::new(),
715            Some(BlockHeader { number: 1, timestamp: 2, ..Default::default() }),
716        )?;
717
718        let sim_params = SimulationParameters {
719            caller: Address::ZERO,
720            to: contract,
721            data: Vec::new(),
722            value: U256::ZERO,
723            block_overrides: Some(BlockEnvOverrides { number: Some(123), timestamp: Some(456) }),
724            ..Default::default()
725        };
726
727        let engine = SimulationEngine::new(state, false);
728        let result = engine
729            .simulate(&sim_params)
730            .expect("simulation should apply block env overrides");
731
732        assert_eq!(U256::from_be_slice(result.result.as_ref()), U256::from(123));
733        Ok(())
734    }
735
736    #[test]
737    fn test_simulate_applies_native_balance_overrides() -> Result<(), Box<dyn Error>> {
738        let state = PreCachedDB::new()?;
739        let contract = Address::from_str("0x0000000000000000000000000000000000001234")?;
740        let bytecode = Bytecode::new_raw(Bytes::from_static(&[
741            0x47, // SELFBALANCE
742            0x60, 0x00, // PUSH1 0
743            0x52, // MSTORE
744            0x60, 0x20, // PUSH1 32
745            0x60, 0x00, // PUSH1 0
746            0xf3, // RETURN
747        ]));
748        let account = AccountInfo::new(U256::ZERO, 0, bytecode.hash_slow(), bytecode);
749        state.init_account(contract, account, None, true)?;
750        state.init_account(Address::ZERO, AccountInfo::default(), None, true)?;
751        state.update(
752            Vec::new(),
753            Some(BlockHeader { number: 1, timestamp: 2, ..Default::default() }),
754        )?;
755        let expected_balance = U256::from(4_200_000_000_000_000_000u64);
756        let sim_params = SimulationParameters {
757            caller: Address::ZERO,
758            to: contract,
759            native_balance_overrides: Some(HashMap::from([(contract, expected_balance)])),
760            ..Default::default()
761        };
762
763        let result = SimulationEngine::new(state, false)
764            .simulate(&sim_params)
765            .expect("simulation should apply the native balance override");
766
767        assert_eq!(U256::from_be_slice(result.result.as_ref()), expected_balance);
768        Ok(())
769    }
770
771    #[test]
772    fn test_integration_revm_v2_swap() -> Result<(), Box<dyn Error>> {
773        let state = new_state();
774
775        // any random address will work
776        let caller = Address::from_str("0x0000000000000000000000000000000000000000")?;
777        let router_addr = Address::from_str("0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D")?;
778        let weth_addr = Address::from_str("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2")?;
779        let usdc_addr = Address::from_str("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48")?;
780
781        // Define the function selector and input arguments
782        let selector = "getAmountsOut(uint256,address[])";
783        let amount_in = U256::from(100_000_000);
784        let path = vec![usdc_addr, weth_addr];
785
786        let encoded = {
787            let args = (amount_in, path);
788            let mut hasher = Keccak256::new();
789            hasher.update(selector.as_bytes());
790            let selector_bytes = &hasher.finalize()[..4];
791            let mut data = selector_bytes.to_vec();
792            let mut encoded_args = args.abi_encode();
793            // Remove extra prefix if present (32 bytes for dynamic data)
794            if encoded_args.len() > 32 &&
795                encoded_args[..32] ==
796                    [0u8; 31]
797                        .into_iter()
798                        .chain([32].to_vec())
799                        .collect::<Vec<u8>>()
800            {
801                encoded_args = encoded_args[32..].to_vec();
802            }
803            data.extend(encoded_args);
804            data
805        };
806
807        // Simulation parameters
808        let sim_params =
809            SimulationParameters { caller, to: router_addr, data: encoded, ..Default::default() };
810        let mut eng = SimulationEngine::new(state, true);
811
812        let block = BlockHeader {
813            number: 23428552,
814            hash: tycho_common::Bytes::from_str(
815                "0x0000000000000000000000000000000000000000000000000000000000000000",
816            )
817            .unwrap(),
818            timestamp: 1758665355,
819            ..Default::default()
820        };
821        eng.state.set_block(Some(block));
822
823        let result = eng.simulate(&sim_params);
824        type BalanceReturn = Vec<U256>;
825        let amounts_out: Vec<U256> = match result {
826            Ok(SimulationResult { result, .. }) => {
827                BalanceReturn::abi_decode(&result).map_err(|e| {
828                    SimulationError::FatalError(format!("Failed to decode result: {e:?}"))
829                })?
830            }
831            _ => panic!("Execution reverted!"),
832        };
833
834        println!(
835            "Swap yielded {} WETH",
836            amounts_out
837                .last()
838                .expect("Empty decoding result")
839        );
840
841        let start = Instant::now();
842        let n_iter = 1000;
843        for _ in 0..n_iter {
844            eng.simulate(&sim_params).unwrap();
845        }
846        let duration = start.elapsed();
847
848        println!("Using revm:");
849        println!("Total Duration [n_iter={n_iter}]: {duration:?}");
850        println!("Single get_amount_out call: {per_call:?}", per_call = duration / n_iter);
851
852        Ok(())
853    }
854
855    #[test]
856    fn test_contract_deployment() -> Result<(), Box<dyn Error>> {
857        let readonly_state = new_state();
858        let state = new_state();
859
860        let selector = "balanceOf(address)";
861        let eoa_address = Address::from_str("0xDFd5293D8e347dFe59E90eFd55b2956a1343963d")?;
862        let calldata = {
863            let args = eoa_address;
864            let mut hasher = Keccak256::new();
865            hasher.update(selector.as_bytes());
866            let selector_bytes = &hasher.finalize()[..4];
867            let mut data = selector_bytes.to_vec();
868            data.extend(args.abi_encode());
869            data
870        };
871
872        let usdt_address = Address::from_str("0xdAC17F958D2ee523a2206206994597C13D831ec7").unwrap();
873        let _ = readonly_state
874            .basic_ref(usdt_address)
875            .unwrap()
876            .unwrap();
877
878        // let deploy_bytecode = std::fs::read(
879        //     "/home/mdank/repos/datarevenue/DEFI/defibot-solver/defibot/swaps/pool_state/dodo/
880        // compiled/ERC20.bin-runtime" ).unwrap();
881        // let deploy_bytecode = revm::precompile::Bytes::from(mocked_bytecode);
882        let _ = Bytes::from(hex::decode("608060405234801562000010575f80fd5b5060405162000a6b38038062000a6b83398101604081905262000033916200012c565b600362000041848262000237565b50600462000050838262000237565b506005805460ff191660ff9290921691909117905550620002ff9050565b634e487b7160e01b5f52604160045260245ffd5b5f82601f83011262000092575f80fd5b81516001600160401b0380821115620000af57620000af6200006e565b604051601f8301601f19908116603f01168101908282118183101715620000da57620000da6200006e565b81604052838152602092508683858801011115620000f6575f80fd5b5f91505b83821015620001195785820183015181830184015290820190620000fa565b5f93810190920192909252949350505050565b5f805f606084860312156200013f575f80fd5b83516001600160401b038082111562000156575f80fd5b620001648783880162000082565b945060208601519150808211156200017a575f80fd5b50620001898682870162000082565b925050604084015160ff81168114620001a0575f80fd5b809150509250925092565b600181811c90821680620001c057607f821691505b602082108103620001df57634e487b7160e01b5f52602260045260245ffd5b50919050565b601f82111562000232575f81815260208120601f850160051c810160208610156200020d5750805b601f850160051c820191505b818110156200022e5782815560010162000219565b5050505b505050565b81516001600160401b038111156200025357620002536200006e565b6200026b81620002648454620001ab565b84620001e5565b602080601f831160018114620002a1575f8415620002895750858301515b5f19600386901b1c1916600185901b1785556200022e565b5f85815260208120601f198616915b82811015620002d157888601518255948401946001909101908401620002b0565b5085821015620002ef57878501515f19600388901b60f8161c191681555b5050505050600190811b01905550565b61075e806200030d5f395ff3fe608060405234801561000f575f80fd5b50600436106100a6575f3560e01c8063395093511161006e578063395093511461011f57806370a082311461013257806395d89b411461015a578063a457c2d714610162578063a9059cbb14610175578063dd62ed3e14610188575f80fd5b806306fdde03146100aa578063095ea7b3146100c857806318160ddd146100eb57806323b872dd146100fd578063313ce56714610110575b5f80fd5b6100b261019b565b6040516100bf91906105b9565b60405180910390f35b6100db6100d636600461061f565b61022b565b60405190151581526020016100bf565b6002545b6040519081526020016100bf565b6100db61010b366004610647565b610244565b604051601281526020016100bf565b6100db61012d36600461061f565b610267565b6100ef610140366004610680565b6001600160a01b03165f9081526020819052604090205490565b6100b2610288565b6100db61017036600461061f565b610297565b6100db61018336600461061f565b6102f2565b6100ef6101963660046106a0565b6102ff565b6060600380546101aa906106d1565b80601f01602080910402602001604051908101604052809291908181526020018280546101d6906106d1565b80156102215780601f106101f857610100808354040283529160200191610221565b820191905f5260205f20905b81548152906001019060200180831161020457829003601f168201915b5050505050905090565b5f33610238818585610329565b60019150505b92915050565b5f336102518582856103dc565b61025c85858561043e565b506001949350505050565b5f3361023881858561027983836102ff565b6102839190610709565b610329565b6060600480546101aa906106d1565b5f33816102a482866102ff565b9050838110156102e557604051632983c0c360e21b81526001600160a01b038616600482015260248101829052604481018590526064015b60405180910390fd5b61025c8286868403610329565b5f3361023881858561043e565b6001600160a01b039182165f90815260016020908152604080832093909416825291909152205490565b6001600160a01b0383166103525760405163e602df0560e01b81525f60048201526024016102dc565b6001600160a01b03821661037b57604051634a1406b160e11b81525f60048201526024016102dc565b6001600160a01b038381165f8181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b5f6103e784846102ff565b90505f198114610438578181101561042b57604051637dc7a0d960e11b81526001600160a01b038416600482015260248101829052604481018390526064016102dc565b6104388484848403610329565b50505050565b6001600160a01b03831661046757604051634b637e8f60e11b81525f60048201526024016102dc565b6001600160a01b0382166104905760405163ec442f0560e01b81525f60048201526024016102dc565b61049b8383836104a0565b505050565b6001600160a01b0383166104ca578060025f8282546104bf9190610709565b9091555061053a9050565b6001600160a01b0383165f908152602081905260409020548181101561051c5760405163391434e360e21b81526001600160a01b038516600482015260248101829052604481018390526064016102dc565b6001600160a01b0384165f9081526020819052604090209082900390555b6001600160a01b03821661055657600280548290039055610574565b6001600160a01b0382165f9081526020819052604090208054820190555b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040516103cf91815260200190565b5f6020808352835180828501525f5b818110156105e4578581018301518582016040015282016105c8565b505f604082860101526040601f19601f8301168501019250505092915050565b80356001600160a01b038116811461061a575f80fd5b919050565b5f8060408385031215610630575f80fd5b61063983610604565b946020939093013593505050565b5f805f60608486031215610659575f80fd5b61066284610604565b925061067060208501610604565b9150604084013590509250925092565b5f60208284031215610690575f80fd5b61069982610604565b9392505050565b5f80604083850312156106b1575f80fd5b6106ba83610604565b91506106c860208401610604565b90509250929050565b600181811c90821680620001c057607f821691505b602082108103620001df57634e487b7160e01b5f52602260045260245ffd5b50919050565b8082018082111561023e57634e487b7160e01b5f52601160045260245ffdfea2646970667358221220dfc123d5852c9246ea16b645b377b4436e2f778438195cc6d6c435e8c73a20e764736f6c63430008140033000000000000000000000000000000000000000000000000000000000000000000")?);
883
884        let onchain_bytecode = Bytes::from(hex::decode("608060405234801561000f575f80fd5b50600436106100a6575f3560e01c8063395093511161006e578063395093511461011f57806370a082311461013257806395d89b411461015a578063a457c2d714610162578063a9059cbb14610175578063dd62ed3e14610188575f80fd5b806306fdde03146100aa578063095ea7b3146100c857806318160ddd146100eb57806323b872dd146100fd578063313ce56714610110575b5f80fd5b6100b261019b565b6040516100bf91906105b9565b60405180910390f35b6100db6100d636600461061f565b61022b565b60405190151581526020016100bf565b6002545b6040519081526020016100bf565b6100db61010b366004610647565b610244565b604051601281526020016100bf565b6100db61012d36600461061f565b610267565b6100ef610140366004610680565b6001600160a01b03165f9081526020819052604090205490565b6100b2610288565b6100db61017036600461061f565b610297565b6100db61018336600461061f565b6102f2565b6100ef6101963660046106a0565b6102ff565b6060600380546101aa906106d1565b80601f01602080910402602001604051908101604052809291908181526020018280546101d6906106d1565b80156102215780601f106101f857610100808354040283529160200191610221565b820191905f5260205f20905b81548152906001019060200180831161020457829003601f168201915b5050505050905090565b5f33610238818585610329565b60019150505b92915050565b5f336102518582856103dc565b61025c85858561043e565b506001949350505050565b5f3361023881858561027983836102ff565b6102839190610709565b610329565b6060600480546101aa906106d1565b5f33816102a482866102ff565b9050838110156102e557604051632983c0c360e21b81526001600160a01b038616600482015260248101829052604481018590526064015b60405180910390fd5b61025c8286868403610329565b5f3361023881858561043e565b6001600160a01b039182165f90815260016020908152604080832093909416825291909152205490565b6001600160a01b0383166103525760405163e602df0560e01b81525f60048201526024016102dc565b6001600160a01b03821661037b57604051634a1406b160e11b81525f60048201526024016102dc565b6001600160a01b038381165f8181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b5f6103e784846102ff565b90505f198114610438578181101561042b57604051637dc7a0d960e11b81526001600160a01b038416600482015260248101829052604481018390526064016102dc565b6104388484848403610329565b50505050565b6001600160a01b03831661046757604051634b637e8f60e11b81525f60048201526024016102dc565b6001600160a01b0382166104905760405163ec442f0560e01b81525f60048201526024016102dc565b61049b8383836104a0565b505050565b6001600160a01b0383166104ca578060025f8282546104bf9190610709565b9091555061053a9050565b6001600160a01b0383165f908152602081905260409020548181101561051c5760405163391434e360e21b81526001600160a01b038516600482015260248101829052604481018390526064016102dc565b6001600160a01b0384165f9081526020819052604090209082900390555b6001600160a01b03821661055657600280548290039055610574565b6001600160a01b0382165f9081526020819052604090208054820190555b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040516103cf91815260200190565b5f6020808352835180828501525f5b818110156105e4578581018301518582016040015282016105c8565b505f604082860101526040601f19601f8301168501019250505092915050565b80356001600160a01b038116811461061a575f80fd5b919050565b5f8060408385031215610630575f80fd5b61063983610604565b946020939093013593505050565b5f805f60608486031215610659575f80fd5b61066284610604565b925061067060208501610604565b9150604084013590509250925092565b5f60208284031215610690575f80fd5b61069982610604565b9392505050565b5f80604083850312156106b1575f80fd5b6106ba83610604565b91506106c860208401610604565b90509250929050565b600181811c908216806106e557607f821691505b60208210810361070357634e487b7160e01b5f52602260045260245ffd5b50919050565b8082018082111561023e57634e487b7160e01b5f52601160045260245ffdfea2646970667358221220dfc123d5852c9246ea16b645b377b4436e2f778438195cc6d6c435e8c73a20e764736f6c63430008140033000000000000000000000000000000000000000000000000000000000000000000")?);
885        let code = Bytecode::new_raw(onchain_bytecode);
886        let contract_acc_info = AccountInfo::new(
887            U256::from(0),
888            0,
889            code.hash_slow(),
890            code,
891            // true_usdt.code.unwrap(),
892        );
893        // Adding permanent storage for balance
894        let mut storage = HashMap::default();
895        storage.insert(
896            U256::from_str(
897                "25842306973167774731510882590667189188844731550465818811072464953030320818263",
898            )
899            .unwrap(),
900            U256::from_str("25").unwrap(),
901        );
902        // MOCK A BALANCE AND APPROVAL
903        // let mut permanent_storage = HashMap::new();
904        // permanent_storage.insert(s)
905        state
906            .init_account(usdt_address, contract_acc_info, Some(storage), true)
907            .expect("Failed to init account");
908
909        // DEPLOY A CONTRACT TO GET ON-CHAIN BYTECODE
910        // let deployment_account = B160::from_str("0x0000000000000000000000000000000000000123")?;
911        // state.init_account(
912        //     deployment_account,
913        //     AccountInfo::new(U256::MAX, 0, Bytecode::default()),
914        //     None,
915        //     true,
916        // );
917        // let deployment_params = SimulationParameters {
918        //     caller: Address::from(deployment_account),
919        //     to: Address::zero(),
920        //     data: Bytes::from(deploy_bytecode),
921        //     value: U256::from(0u64),
922        //     overrides: None,
923        //     gas_limit: None,
924        // };
925
926        // prepare balanceOf
927        // let deployed_contract_address =
928        // B160::from_str("0x5450b634edf901a95af959c99c058086a51836a8")?; Adding overwrite
929        // for balance
930        let mut overrides = HashMap::default();
931        let mut storage_overwrite = HashMap::default();
932        storage_overwrite.insert(
933            U256::from_str(
934                "25842306973167774731510882590667189188844731550465818811072464953030320818263",
935            )
936            .unwrap(),
937            U256::from_str("80").unwrap(),
938        );
939        overrides.insert(usdt_address, storage_overwrite);
940
941        let sim_params = SimulationParameters {
942            caller: Address::from_str("0x0000000000000000000000000000000000000000")?,
943            to: usdt_address,
944            // to: Address::from(deployed_contract_address),
945            data: calldata,
946            overrides: Some(overrides),
947            ..Default::default()
948        };
949
950        let mut eng = SimulationEngine::new(state, false);
951
952        // Dummy block (irrelevant for this test)
953        let block = BlockHeader {
954            number: 1,
955            hash: tycho_common::Bytes::from_str(
956                "0x0000000000000000000000000000000000000000000000000000000000000000",
957            )
958            .unwrap(),
959            timestamp: 1748397011,
960            ..Default::default()
961        };
962        eng.state.set_block(Some(block));
963
964        // println!("Deploying a mocked contract!");
965        // let deployment_result = eng.simulate(&deployment_params);
966        // match deployment_result {
967        //     Ok(SimulationResult { result, state_updates, gas_used }) => {
968        //         println!("Deployment result: {:?}", result);
969        //         println!("Used gas: {:?}", gas_used);
970        //         println!("{:?}", state_updates);
971        //     }
972        //     Err(error) => panic!("{:?}", error),
973        // };
974
975        println!("Executing balanceOf");
976        let result = eng.simulate(&sim_params);
977        let balance = match result {
978            Ok(SimulationResult { result, .. }) => U256::abi_decode(&result)?,
979            Err(error) => panic!("{error:?}"),
980        };
981        println!("Balance: {balance}");
982
983        Ok(())
984    }
985}