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#[derive(Debug, Display, Clone, PartialEq)]
30pub enum SimulationEngineError {
31 StorageError(String),
34 OutOfGas(String, String),
37 TransactionError { data: String, gas_used: Option<u64> },
39 TraceError(String),
41}
42
43#[derive(Debug, Clone, Default)]
45pub struct SimulationResult {
46 pub result: Bytes,
48 pub state_updates: HashMap<Address, StateUpdate>,
50 pub gas_used: u64,
52 pub transient_storage: HashMap<Address, HashMap<U256, U256>>,
54}
55
56#[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 pub fn new(state: D, trace: bool) -> Self {
83 Self { state, trace }
84 }
85
86 pub fn simulate(
90 &self,
91 params: &SimulationParameters,
92 ) -> Result<SimulationResult, SimulationEngineError> {
93 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(¶ms.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) = ¶ms.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) = ¶ms.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 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 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 (
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 handle.block_on(future)
257 } else {
258 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
272fn 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
335fn 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 let mut account_updates: HashMap<Address, StateUpdate> = HashMap::new();
355 for (address, account) in state {
356 account_updates.insert(
357 address,
358 StateUpdate {
359 balance: Some(account.info.balance),
361 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)]
390pub struct SimulationParameters {
392 pub caller: Address,
394 pub to: Address,
396 pub data: Vec<u8>,
398 pub value: U256,
400 pub overrides: Option<HashMap<Address, HashMap<U256, U256>>>,
403 pub gas_limit: Option<u64>,
405 pub transient_storage: Option<HashMap<Address, HashMap<U256, U256>>>,
408 pub block_overrides: Option<BlockEnvOverrides>,
410 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#[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 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 #[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 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 (
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 (
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 let bytecode = Bytecode::new_raw(Bytes::from_static(&[
703 0x43, 0x60, 0x00, 0x52, 0x60, 0x20, 0x60, 0x00, 0xf3, ]));
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, 0x60, 0x00, 0x52, 0x60, 0x20, 0x60, 0x00, 0xf3, ]));
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 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 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 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 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 _ = 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 );
893 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 state
906 .init_account(usdt_address, contract_acc_info, Some(storage), true)
907 .expect("Failed to init account");
908
909 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 data: calldata,
946 overrides: Some(overrides),
947 ..Default::default()
948 };
949
950 let mut eng = SimulationEngine::new(state, false);
951
952 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!("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}