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