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 overrides = params
102 .overrides
103 .clone()
104 .unwrap_or_default();
105
106 let db_ref = OverriddenSimulationDB { inner_db: &self.state, overrides: &overrides };
107
108 let tx_env = TxEnv {
109 caller: params.caller,
110 gas_limit: params.gas_limit.unwrap_or(8_000_000),
111 kind: TxKind::Call(params.to),
112 value: params.value,
113 data: Bytes::copy_from_slice(¶ms.data),
114 ..Default::default()
115 };
116
117 let mut block =
118 self.state
119 .get_current_block()
120 .ok_or(SimulationEngineError::StorageError(
121 "Current block not set in SimulationEngine.".into(),
122 ))?;
123
124 if let Some(overrides) = ¶ms.block_overrides {
125 if let Some(number) = overrides.number {
126 block.number = number;
127 }
128 if let Some(timestamp) = overrides.timestamp {
129 block.timestamp = timestamp;
130 }
131 }
132
133 let block_env = BlockEnv {
134 number: U256::from(block.number),
135 timestamp: U256::from(block.timestamp),
136 ..Default::default()
137 };
138
139 let mut cfg_env: CfgEnv<SpecId> = CfgEnv::new_with_spec(SpecId::PRAGUE);
140 cfg_env.disable_nonce_check = true;
141 cfg_env.disable_eip3607 = true;
142
143 let context = Context::mainnet()
144 .with_cfg(cfg_env)
145 .with_ref_db(db_ref)
146 .with_block(block_env)
147 .with_tx(tx_env.clone())
148 .modify_journal_chained(|journal| {
149 if let Some(transient_storage) = params.transient_storage.clone() {
150 for (address, slots) in transient_storage {
151 for (slot, value) in slots {
152 journal.tstore(address, slot, value);
153 }
154 }
155 }
156 if let Some(overrides) = ¶ms.overrides {
157 for (address, storage) in overrides {
158 let keys = storage.keys().copied();
159 let _ = journal.warm_account_and_storage(*address, keys);
160 }
161 }
162 });
163
164 let evm_result = if self.trace {
165 let mut tracer = TracingInspector::new(TracingInspectorConfig::default());
166
167 let res = {
168 let mut vm = context.build_mainnet_with_inspector(&mut tracer);
169
170 debug!(
171 "Starting simulation with tx parameters: {:#?} {:#?}",
172 vm.ctx.tx, vm.ctx.block
173 );
174 vm.inspect_tx(tx_env.clone())
175 };
176
177 Self::print_traces(tracer, res.as_ref().ok())?;
178
179 res
180 } else {
181 let mut vm = context.build_mainnet();
182
183 debug!("Starting simulation with tx parameters: {:#?} {:#?}", vm.ctx.tx, vm.ctx.block);
184
185 vm.replay()
186 };
187
188 interpret_evm_result(evm_result, HashMap::new())
190 }
191
192 pub fn clear_temp_storage(&mut self) -> Result<(), <D as EngineDatabaseInterface>::Error> {
193 self.state.clear_temp_storage()
194 }
195
196 fn print_traces(
197 tracer: TracingInspector,
198 res: Option<&ResultAndState>,
199 ) -> Result<(), SimulationEngineError> {
200 let (exit_reason, _gas_refunded, gas_used, _out, _exec_logs) = match res {
201 Some(ResultAndState { result, state: _ }) => {
202 match result.clone() {
204 ExecutionResult::Success {
205 reason,
206 gas_used,
207 gas_refunded,
208 output,
209 logs,
210 ..
211 } => (reason.into(), gas_refunded, gas_used, Some(output), logs),
212 ExecutionResult::Revert { gas_used, output } => {
213 (
215 InstructionResult::Revert,
216 0_u64,
217 gas_used,
218 Some(Output::Call(output)),
219 vec![],
220 )
221 }
222 ExecutionResult::Halt { reason, gas_used } => {
223 (reason.into(), 0_u64, gas_used, None, vec![])
224 }
225 }
226 }
227 _ => (InstructionResult::Stop, 0_u64, 0, None, vec![]),
228 };
229
230 let trace_res = TraceResult {
231 success: matches!(exit_reason, return_ok!()),
232 traces: Some(vec![tracer.into_traces()]),
233 gas_used,
234 };
235
236 tokio::task::block_in_place(|| -> Result<(), SimulationEngineError> {
237 let future = async {
238 handle_traces(
239 trace_res,
240 env::var("ETHERSCAN_API_KEY").ok(),
241 tycho_common::models::Chain::Ethereum,
242 )
243 .await
244 .map_err(|err| SimulationEngineError::TraceError(err.to_string()))
245 };
246 if let Ok(handle) = Handle::try_current() {
247 handle.block_on(future)
249 } else {
250 let rt = Runtime::new().map_err(|err| {
252 SimulationEngineError::TraceError(format!(
253 "Failed to create a new runtime: {err}"
254 ))
255 })?;
256 rt.block_on(future)
257 }
258 })?;
259
260 Ok(())
261 }
262}
263
264fn interpret_evm_result<DBError: Debug>(
280 evm_result: Result<ResultAndState, EVMError<DBError>>,
281 transient_storage: HashMap<Address, HashMap<U256, U256>>,
282) -> Result<SimulationResult, SimulationEngineError> {
283 match evm_result {
284 Ok(result_and_state) => match result_and_state.result {
285 ExecutionResult::Success { gas_used, gas_refunded, output, .. } => {
286 Ok(interpret_evm_success(
287 gas_used,
288 gas_refunded,
289 output,
290 result_and_state.state,
291 transient_storage,
292 ))
293 }
294 ExecutionResult::Revert { output, gas_used } => {
295 Err(SimulationEngineError::TransactionError {
296 data: format!("0x{encoded}", encoded = hex::encode::<Vec<u8>>(output.into())),
297 gas_used: Some(gas_used),
298 })
299 }
300 ExecutionResult::Halt { reason, gas_used } => {
301 Err(SimulationEngineError::TransactionError {
302 data: format!("{reason:?}"),
303 gas_used: Some(gas_used),
304 })
305 }
306 },
307 Err(evm_error) => match evm_error {
308 EVMError::Transaction(invalid_tx) => Err(SimulationEngineError::TransactionError {
309 data: format!("EVM error: {invalid_tx:?}"),
310 gas_used: None,
311 }),
312 EVMError::Database(db_error) => {
313 Err(SimulationEngineError::StorageError(format!("Storage error: {db_error:?}")))
314 }
315 EVMError::Custom(err) => Err(SimulationEngineError::TransactionError {
316 data: format!("Unexpected error {err}"),
317 gas_used: None,
318 }),
319 EVMError::Header(err) => Err(SimulationEngineError::TransactionError {
320 data: format!("Unexpected error {err}"),
321 gas_used: None,
322 }),
323 },
324 }
325}
326
327fn interpret_evm_success(
329 gas_used: u64,
330 gas_refunded: u64,
331 output: Output,
332 state: EvmState,
333 transient_storage: HashMap<Address, HashMap<U256, U256>>,
334) -> SimulationResult {
335 SimulationResult {
336 result: output.into_data(),
337 state_updates: {
338 let mut account_updates: HashMap<Address, StateUpdate> = HashMap::new();
347 for (address, account) in state {
348 account_updates.insert(
349 address,
350 StateUpdate {
351 balance: Some(account.info.balance),
353 storage: {
355 if account.storage.is_empty() {
356 None
357 } else {
358 let mut slot_updates: HashMap<U256, U256> = HashMap::new();
359 for (index, slot) in account.storage {
360 if slot.is_changed() {
361 slot_updates.insert(index, slot.present_value);
362 }
363 }
364 if slot_updates.is_empty() {
365 None
366 } else {
367 Some(slot_updates)
368 }
369 }
370 },
371 },
372 );
373 }
374 account_updates
375 },
376 gas_used: gas_used - gas_refunded,
377 transient_storage,
378 }
379}
380
381#[derive(Debug)]
382pub struct SimulationParameters {
384 pub caller: Address,
386 pub to: Address,
388 pub data: Vec<u8>,
390 pub value: U256,
392 pub overrides: Option<HashMap<Address, HashMap<U256, U256>>>,
395 pub gas_limit: Option<u64>,
397 pub transient_storage: Option<HashMap<Address, HashMap<U256, U256>>>,
400 pub block_overrides: Option<BlockEnvOverrides>,
402}
403
404#[derive(Debug, Clone, Default, PartialEq, Eq)]
405pub struct BlockEnvOverrides {
406 pub number: Option<u64>,
407 pub timestamp: Option<u64>,
408}
409
410#[cfg(test)]
411mod tests {
412 use std::{error::Error, str::FromStr, time::Instant};
413
414 use alloy::{
415 primitives::{Address, Bytes, Keccak256, B256},
416 sol_types::SolValue,
417 transports::{RpcError, TransportError, TransportErrorKind},
418 };
419 use revm::{
420 context::result::{HaltReason, InvalidTransaction, OutOfGasError, SuccessReason},
421 state::{
422 Account, AccountInfo, AccountStatus, Bytecode, EvmState as rState, EvmStorageSlot,
423 },
424 };
425 use tycho_client::feed::BlockHeader;
426 use tycho_common::simulation::errors::SimulationError;
427
428 use super::*;
429 use crate::evm::engine_db::{
430 engine_db_interface::EngineDatabaseInterface,
431 simulation_db::{EVMProvider, SimulationDB},
432 utils::{get_client, get_runtime},
433 };
434
435 #[test]
436 fn test_interpret_result_ok_success() {
437 let evm_result: Result<ResultAndState, EVMError<TransportError>> = Ok(ResultAndState {
438 result: ExecutionResult::Success {
439 reason: SuccessReason::Return,
440 gas_used: 100_u64,
441 gas_refunded: 10_u64,
442 logs: Vec::new(),
443 output: Output::Call(Bytes::from_static(b"output")),
444 },
445 state: [(
446 Address::ZERO,
448 Account {
449 info: AccountInfo {
450 balance: U256::from_limbs([1, 0, 0, 0]),
451 nonce: 2,
452 code_hash: B256::ZERO,
453 code: None,
454 },
455 transaction_id: 0,
456 storage: [
457 (
459 U256::from_limbs([3, 1, 0, 0]),
460 EvmStorageSlot {
461 original_value: U256::from_limbs([4, 0, 0, 0]),
462 present_value: U256::from_limbs([5, 0, 0, 0]),
463 transaction_id: 0,
464 is_cold: true,
465 },
466 ),
467 (
469 U256::from_limbs([3, 2, 0, 0]),
470 EvmStorageSlot {
471 original_value: U256::from_limbs([4, 0, 0, 0]),
472 present_value: U256::from_limbs([4, 0, 0, 0]),
473 transaction_id: 0,
474 is_cold: true,
475 },
476 ),
477 ]
478 .iter()
479 .cloned()
480 .collect(),
481 status: AccountStatus::Touched,
482 },
483 )]
484 .iter()
485 .cloned()
486 .collect(),
487 });
488
489 let transient_storage = HashMap::from([(
490 Address::from_str("0x1f98400000000000000000000000000000000004").unwrap(),
491 HashMap::from([(U256::from(0), U256::from(1))]),
492 )]);
493 let result = interpret_evm_result(evm_result, transient_storage.clone());
494 let simulation_result = result.unwrap();
495
496 assert_eq!(simulation_result.result, Bytes::from_static(b"output"));
497 let expected_state_updates = [(
498 Address::ZERO,
499 StateUpdate {
500 storage: Some(
501 [(U256::from_limbs([3, 1, 0, 0]), U256::from_limbs([5, 0, 0, 0]))]
502 .iter()
503 .cloned()
504 .collect(),
505 ),
506 balance: Some(U256::from_limbs([1, 0, 0, 0])),
507 },
508 )]
509 .iter()
510 .cloned()
511 .collect();
512 assert_eq!(simulation_result.state_updates, expected_state_updates);
513 assert_eq!(simulation_result.gas_used, 90);
514 assert_eq!(simulation_result.transient_storage, transient_storage);
515 }
516
517 #[test]
518 fn test_interpret_result_ok_revert() {
519 let evm_result: Result<ResultAndState, EVMError<TransportError>> = Ok(ResultAndState {
520 result: ExecutionResult::Revert {
521 gas_used: 100_u64,
522 output: Bytes::from_static(b"output"),
523 },
524 state: rState::default(),
525 });
526
527 let result = interpret_evm_result(evm_result, HashMap::new());
528
529 assert!(result.is_err());
530 let err = result.err().unwrap();
531 match err {
532 SimulationEngineError::TransactionError { data: _, gas_used } => {
533 assert_eq!(
534 format!("0x{}", hex::encode::<Vec<u8>>("output".into())),
535 "0x6f7574707574"
536 );
537 assert_eq!(gas_used, Some(100));
538 }
539 _ => panic!("Wrong type of SimulationError!"),
540 }
541 }
542
543 #[test]
544 fn test_interpret_result_ok_halt() {
545 let evm_result: Result<ResultAndState, EVMError<TransportError>> = Ok(ResultAndState {
546 result: ExecutionResult::Halt {
547 reason: HaltReason::OutOfGas(OutOfGasError::Basic),
548 gas_used: 100_u64,
549 },
550 state: rState::default(),
551 });
552
553 let result = interpret_evm_result(evm_result, HashMap::new());
554
555 assert!(result.is_err());
556 let err = result.err().unwrap();
557 match err {
558 SimulationEngineError::TransactionError { data, gas_used } => {
559 assert_eq!(data, "OutOfGas(Basic)");
560 assert_eq!(gas_used, Some(100));
561 }
562 _ => panic!("Wrong type of SimulationError!"),
563 }
564 }
565
566 #[test]
567 fn test_interpret_result_err_invalid_transaction() {
568 let evm_result: Result<ResultAndState, EVMError<TransportError>> =
569 Err(EVMError::Transaction(InvalidTransaction::PriorityFeeGreaterThanMaxFee));
570
571 let result = interpret_evm_result(evm_result, HashMap::new());
572
573 assert!(result.is_err());
574 let err = result.err().unwrap();
575 match err {
576 SimulationEngineError::TransactionError { data, gas_used } => {
577 assert_eq!(data, "EVM error: PriorityFeeGreaterThanMaxFee");
578 assert_eq!(gas_used, None);
579 }
580 _ => panic!("Wrong type of SimulationError!"),
581 }
582 }
583
584 #[test]
585 fn test_interpret_result_err_db_error() {
586 let evm_result: Result<ResultAndState, EVMError<TransportError>> = Err(EVMError::Database(
587 RpcError::Transport(TransportErrorKind::Custom(Box::from("boo".to_string()))),
588 ));
589
590 let result = interpret_evm_result(evm_result, HashMap::new());
591
592 assert!(result.is_err());
593 let err = result.err().unwrap();
594 match err {
595 SimulationEngineError::StorageError(msg) => {
596 assert_eq!(msg, "Storage error: Transport(Custom(\"boo\"))")
597 }
598 _ => panic!("Wrong type of SimulationError!"),
599 }
600 }
601 fn new_state() -> SimulationDB<EVMProvider> {
602 let runtime = get_runtime().expect("Failed to create test runtime");
603 let client = get_client(None).expect("Failed to create test client");
604 SimulationDB::new(client, runtime, None)
605 }
606
607 #[test]
608 fn test_simulate_applies_block_env_overrides() -> Result<(), Box<dyn Error>> {
609 let mut state = new_state();
610 let contract = Address::from_str("0x0000000000000000000000000000000000001234")?;
611 let bytecode = Bytecode::new_raw(Bytes::from_static(&[
622 0x43, 0x60, 0x00, 0x52, 0x60, 0x20, 0x60, 0x00, 0xf3, ]));
629 let account = AccountInfo::new(U256::ZERO, 0, bytecode.hash_slow(), bytecode);
630 state.init_account(contract, account, None, true)?;
631 state.init_account(Address::ZERO, AccountInfo::default(), None, true)?;
632 state.set_block(Some(BlockHeader { number: 1, timestamp: 2, ..Default::default() }));
633
634 let sim_params = SimulationParameters {
635 caller: Address::ZERO,
636 to: contract,
637 data: Vec::new(),
638 value: U256::ZERO,
639 overrides: None,
640 gas_limit: None,
641 transient_storage: None,
642 block_overrides: Some(BlockEnvOverrides { number: Some(123), timestamp: Some(456) }),
643 };
644
645 let engine = SimulationEngine::new(state, false);
646 let result = engine
647 .simulate(&sim_params)
648 .expect("simulation should apply block env overrides");
649
650 assert_eq!(U256::from_be_slice(result.result.as_ref()), U256::from(123));
651 Ok(())
652 }
653
654 #[test]
655 fn test_integration_revm_v2_swap() -> Result<(), Box<dyn Error>> {
656 let state = new_state();
657
658 let caller = Address::from_str("0x0000000000000000000000000000000000000000")?;
660 let router_addr = Address::from_str("0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D")?;
661 let weth_addr = Address::from_str("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2")?;
662 let usdc_addr = Address::from_str("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48")?;
663
664 let selector = "getAmountsOut(uint256,address[])";
666 let amount_in = U256::from(100_000_000);
667 let path = vec![usdc_addr, weth_addr];
668
669 let encoded = {
670 let args = (amount_in, path);
671 let mut hasher = Keccak256::new();
672 hasher.update(selector.as_bytes());
673 let selector_bytes = &hasher.finalize()[..4];
674 let mut data = selector_bytes.to_vec();
675 let mut encoded_args = args.abi_encode();
676 if encoded_args.len() > 32 &&
678 encoded_args[..32] ==
679 [0u8; 31]
680 .into_iter()
681 .chain([32].to_vec())
682 .collect::<Vec<u8>>()
683 {
684 encoded_args = encoded_args[32..].to_vec();
685 }
686 data.extend(encoded_args);
687 data
688 };
689
690 let sim_params = SimulationParameters {
692 caller,
693 to: router_addr,
694 data: encoded,
695 value: U256::from(0u64),
696 overrides: None,
697 gas_limit: None,
698 transient_storage: None,
699 block_overrides: None,
700 };
701 let mut eng = SimulationEngine::new(state, true);
702
703 let block = BlockHeader {
704 number: 23428552,
705 hash: tycho_common::Bytes::from_str(
706 "0x0000000000000000000000000000000000000000000000000000000000000000",
707 )
708 .unwrap(),
709 timestamp: 1758665355,
710 ..Default::default()
711 };
712 eng.state.set_block(Some(block));
713
714 let result = eng.simulate(&sim_params);
715 type BalanceReturn = Vec<U256>;
716 let amounts_out: Vec<U256> = match result {
717 Ok(SimulationResult { result, .. }) => {
718 BalanceReturn::abi_decode(&result).map_err(|e| {
719 SimulationError::FatalError(format!("Failed to decode result: {e:?}"))
720 })?
721 }
722 _ => panic!("Execution reverted!"),
723 };
724
725 println!(
726 "Swap yielded {} WETH",
727 amounts_out
728 .last()
729 .expect("Empty decoding result")
730 );
731
732 let start = Instant::now();
733 let n_iter = 1000;
734 for _ in 0..n_iter {
735 eng.simulate(&sim_params).unwrap();
736 }
737 let duration = start.elapsed();
738
739 println!("Using revm:");
740 println!("Total Duration [n_iter={n_iter}]: {duration:?}");
741 println!("Single get_amount_out call: {per_call:?}", per_call = duration / n_iter);
742
743 Ok(())
744 }
745
746 #[test]
747 fn test_contract_deployment() -> Result<(), Box<dyn Error>> {
748 let readonly_state = new_state();
749 let state = new_state();
750
751 let selector = "balanceOf(address)";
752 let eoa_address = Address::from_str("0xDFd5293D8e347dFe59E90eFd55b2956a1343963d")?;
753 let calldata = {
754 let args = eoa_address;
755 let mut hasher = Keccak256::new();
756 hasher.update(selector.as_bytes());
757 let selector_bytes = &hasher.finalize()[..4];
758 let mut data = selector_bytes.to_vec();
759 data.extend(args.abi_encode());
760 data
761 };
762
763 let usdt_address = Address::from_str("0xdAC17F958D2ee523a2206206994597C13D831ec7").unwrap();
764 let _ = readonly_state
765 .basic_ref(usdt_address)
766 .unwrap()
767 .unwrap();
768
769 let _ = Bytes::from(hex::decode("608060405234801562000010575f80fd5b5060405162000a6b38038062000a6b83398101604081905262000033916200012c565b600362000041848262000237565b50600462000050838262000237565b506005805460ff191660ff9290921691909117905550620002ff9050565b634e487b7160e01b5f52604160045260245ffd5b5f82601f83011262000092575f80fd5b81516001600160401b0380821115620000af57620000af6200006e565b604051601f8301601f19908116603f01168101908282118183101715620000da57620000da6200006e565b81604052838152602092508683858801011115620000f6575f80fd5b5f91505b83821015620001195785820183015181830184015290820190620000fa565b5f93810190920192909252949350505050565b5f805f606084860312156200013f575f80fd5b83516001600160401b038082111562000156575f80fd5b620001648783880162000082565b945060208601519150808211156200017a575f80fd5b50620001898682870162000082565b925050604084015160ff81168114620001a0575f80fd5b809150509250925092565b600181811c90821680620001c057607f821691505b602082108103620001df57634e487b7160e01b5f52602260045260245ffd5b50919050565b601f82111562000232575f81815260208120601f850160051c810160208610156200020d5750805b601f850160051c820191505b818110156200022e5782815560010162000219565b5050505b505050565b81516001600160401b038111156200025357620002536200006e565b6200026b81620002648454620001ab565b84620001e5565b602080601f831160018114620002a1575f8415620002895750858301515b5f19600386901b1c1916600185901b1785556200022e565b5f85815260208120601f198616915b82811015620002d157888601518255948401946001909101908401620002b0565b5085821015620002ef57878501515f19600388901b60f8161c191681555b5050505050600190811b01905550565b61075e806200030d5f395ff3fe608060405234801561000f575f80fd5b50600436106100a6575f3560e01c8063395093511161006e578063395093511461011f57806370a082311461013257806395d89b411461015a578063a457c2d714610162578063a9059cbb14610175578063dd62ed3e14610188575f80fd5b806306fdde03146100aa578063095ea7b3146100c857806318160ddd146100eb57806323b872dd146100fd578063313ce56714610110575b5f80fd5b6100b261019b565b6040516100bf91906105b9565b60405180910390f35b6100db6100d636600461061f565b61022b565b60405190151581526020016100bf565b6002545b6040519081526020016100bf565b6100db61010b366004610647565b610244565b604051601281526020016100bf565b6100db61012d36600461061f565b610267565b6100ef610140366004610680565b6001600160a01b03165f9081526020819052604090205490565b6100b2610288565b6100db61017036600461061f565b610297565b6100db61018336600461061f565b6102f2565b6100ef6101963660046106a0565b6102ff565b6060600380546101aa906106d1565b80601f01602080910402602001604051908101604052809291908181526020018280546101d6906106d1565b80156102215780601f106101f857610100808354040283529160200191610221565b820191905f5260205f20905b81548152906001019060200180831161020457829003601f168201915b5050505050905090565b5f33610238818585610329565b60019150505b92915050565b5f336102518582856103dc565b61025c85858561043e565b506001949350505050565b5f3361023881858561027983836102ff565b6102839190610709565b610329565b6060600480546101aa906106d1565b5f33816102a482866102ff565b9050838110156102e557604051632983c0c360e21b81526001600160a01b038616600482015260248101829052604481018590526064015b60405180910390fd5b61025c8286868403610329565b5f3361023881858561043e565b6001600160a01b039182165f90815260016020908152604080832093909416825291909152205490565b6001600160a01b0383166103525760405163e602df0560e01b81525f60048201526024016102dc565b6001600160a01b03821661037b57604051634a1406b160e11b81525f60048201526024016102dc565b6001600160a01b038381165f8181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b5f6103e784846102ff565b90505f198114610438578181101561042b57604051637dc7a0d960e11b81526001600160a01b038416600482015260248101829052604481018390526064016102dc565b6104388484848403610329565b50505050565b6001600160a01b03831661046757604051634b637e8f60e11b81525f60048201526024016102dc565b6001600160a01b0382166104905760405163ec442f0560e01b81525f60048201526024016102dc565b61049b8383836104a0565b505050565b6001600160a01b0383166104ca578060025f8282546104bf9190610709565b9091555061053a9050565b6001600160a01b0383165f908152602081905260409020548181101561051c5760405163391434e360e21b81526001600160a01b038516600482015260248101829052604481018390526064016102dc565b6001600160a01b0384165f9081526020819052604090209082900390555b6001600160a01b03821661055657600280548290039055610574565b6001600160a01b0382165f9081526020819052604090208054820190555b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040516103cf91815260200190565b5f6020808352835180828501525f5b818110156105e4578581018301518582016040015282016105c8565b505f604082860101526040601f19601f8301168501019250505092915050565b80356001600160a01b038116811461061a575f80fd5b919050565b5f8060408385031215610630575f80fd5b61063983610604565b946020939093013593505050565b5f805f60608486031215610659575f80fd5b61066284610604565b925061067060208501610604565b9150604084013590509250925092565b5f60208284031215610690575f80fd5b61069982610604565b9392505050565b5f80604083850312156106b1575f80fd5b6106ba83610604565b91506106c860208401610604565b90509250929050565b600181811c90821680620001c057607f821691505b602082108103620001df57634e487b7160e01b5f52602260045260245ffd5b50919050565b8082018082111561023e57634e487b7160e01b5f52601160045260245ffdfea2646970667358221220dfc123d5852c9246ea16b645b377b4436e2f778438195cc6d6c435e8c73a20e764736f6c63430008140033000000000000000000000000000000000000000000000000000000000000000000")?);
774
775 let onchain_bytecode = Bytes::from(hex::decode("608060405234801561000f575f80fd5b50600436106100a6575f3560e01c8063395093511161006e578063395093511461011f57806370a082311461013257806395d89b411461015a578063a457c2d714610162578063a9059cbb14610175578063dd62ed3e14610188575f80fd5b806306fdde03146100aa578063095ea7b3146100c857806318160ddd146100eb57806323b872dd146100fd578063313ce56714610110575b5f80fd5b6100b261019b565b6040516100bf91906105b9565b60405180910390f35b6100db6100d636600461061f565b61022b565b60405190151581526020016100bf565b6002545b6040519081526020016100bf565b6100db61010b366004610647565b610244565b604051601281526020016100bf565b6100db61012d36600461061f565b610267565b6100ef610140366004610680565b6001600160a01b03165f9081526020819052604090205490565b6100b2610288565b6100db61017036600461061f565b610297565b6100db61018336600461061f565b6102f2565b6100ef6101963660046106a0565b6102ff565b6060600380546101aa906106d1565b80601f01602080910402602001604051908101604052809291908181526020018280546101d6906106d1565b80156102215780601f106101f857610100808354040283529160200191610221565b820191905f5260205f20905b81548152906001019060200180831161020457829003601f168201915b5050505050905090565b5f33610238818585610329565b60019150505b92915050565b5f336102518582856103dc565b61025c85858561043e565b506001949350505050565b5f3361023881858561027983836102ff565b6102839190610709565b610329565b6060600480546101aa906106d1565b5f33816102a482866102ff565b9050838110156102e557604051632983c0c360e21b81526001600160a01b038616600482015260248101829052604481018590526064015b60405180910390fd5b61025c8286868403610329565b5f3361023881858561043e565b6001600160a01b039182165f90815260016020908152604080832093909416825291909152205490565b6001600160a01b0383166103525760405163e602df0560e01b81525f60048201526024016102dc565b6001600160a01b03821661037b57604051634a1406b160e11b81525f60048201526024016102dc565b6001600160a01b038381165f8181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b5f6103e784846102ff565b90505f198114610438578181101561042b57604051637dc7a0d960e11b81526001600160a01b038416600482015260248101829052604481018390526064016102dc565b6104388484848403610329565b50505050565b6001600160a01b03831661046757604051634b637e8f60e11b81525f60048201526024016102dc565b6001600160a01b0382166104905760405163ec442f0560e01b81525f60048201526024016102dc565b61049b8383836104a0565b505050565b6001600160a01b0383166104ca578060025f8282546104bf9190610709565b9091555061053a9050565b6001600160a01b0383165f908152602081905260409020548181101561051c5760405163391434e360e21b81526001600160a01b038516600482015260248101829052604481018390526064016102dc565b6001600160a01b0384165f9081526020819052604090209082900390555b6001600160a01b03821661055657600280548290039055610574565b6001600160a01b0382165f9081526020819052604090208054820190555b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040516103cf91815260200190565b5f6020808352835180828501525f5b818110156105e4578581018301518582016040015282016105c8565b505f604082860101526040601f19601f8301168501019250505092915050565b80356001600160a01b038116811461061a575f80fd5b919050565b5f8060408385031215610630575f80fd5b61063983610604565b946020939093013593505050565b5f805f60608486031215610659575f80fd5b61066284610604565b925061067060208501610604565b9150604084013590509250925092565b5f60208284031215610690575f80fd5b61069982610604565b9392505050565b5f80604083850312156106b1575f80fd5b6106ba83610604565b91506106c860208401610604565b90509250929050565b600181811c908216806106e557607f821691505b60208210810361070357634e487b7160e01b5f52602260045260245ffd5b50919050565b8082018082111561023e57634e487b7160e01b5f52601160045260245ffdfea2646970667358221220dfc123d5852c9246ea16b645b377b4436e2f778438195cc6d6c435e8c73a20e764736f6c63430008140033000000000000000000000000000000000000000000000000000000000000000000")?);
776 let code = Bytecode::new_raw(onchain_bytecode);
777 let contract_acc_info = AccountInfo::new(
778 U256::from(0),
779 0,
780 code.hash_slow(),
781 code,
782 );
784 let mut storage = HashMap::default();
786 storage.insert(
787 U256::from_str(
788 "25842306973167774731510882590667189188844731550465818811072464953030320818263",
789 )
790 .unwrap(),
791 U256::from_str("25").unwrap(),
792 );
793 state
797 .init_account(usdt_address, contract_acc_info, Some(storage), true)
798 .expect("Failed to init account");
799
800 let mut overrides = HashMap::default();
822 let mut storage_overwrite = HashMap::default();
823 storage_overwrite.insert(
824 U256::from_str(
825 "25842306973167774731510882590667189188844731550465818811072464953030320818263",
826 )
827 .unwrap(),
828 U256::from_str("80").unwrap(),
829 );
830 overrides.insert(usdt_address, storage_overwrite);
831
832 let sim_params = SimulationParameters {
833 caller: Address::from_str("0x0000000000000000000000000000000000000000")?,
834 to: usdt_address,
835 data: calldata,
837 value: U256::from(0u64),
838 overrides: Some(overrides),
839 gas_limit: None,
840 transient_storage: None,
841 block_overrides: None,
842 };
843
844 let mut eng = SimulationEngine::new(state, false);
845
846 let block = BlockHeader {
848 number: 1,
849 hash: tycho_common::Bytes::from_str(
850 "0x0000000000000000000000000000000000000000000000000000000000000000",
851 )
852 .unwrap(),
853 timestamp: 1748397011,
854 ..Default::default()
855 };
856 eng.state.set_block(Some(block));
857
858 println!("Executing balanceOf");
870 let result = eng.simulate(&sim_params);
871 let balance = match result {
872 Ok(SimulationResult { result, .. }) => U256::abi_decode(&result)?,
873 Err(error) => panic!("{error:?}"),
874 };
875 println!("Balance: {balance}");
876
877 Ok(())
878 }
879}