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 block = self
117 .state
118 .get_current_block()
119 .ok_or(SimulationEngineError::StorageError(
120 "Current block not set in SimulationEngine.".into(),
121 ))?;
122
123 let block_env = BlockEnv {
124 number: U256::from(block.number),
125 timestamp: U256::from(block.timestamp),
126 ..Default::default()
127 };
128
129 let mut cfg_env: CfgEnv<SpecId> = CfgEnv::new_with_spec(SpecId::PRAGUE);
130 cfg_env.disable_nonce_check = true;
131 cfg_env.disable_eip3607 = true;
132
133 let context = Context::mainnet()
134 .with_cfg(cfg_env)
135 .with_ref_db(db_ref)
136 .with_block(block_env)
137 .with_tx(tx_env.clone())
138 .modify_journal_chained(|journal| {
139 if let Some(transient_storage) = params.transient_storage.clone() {
140 for (address, slots) in transient_storage {
141 for (slot, value) in slots {
142 journal.tstore(address, slot, value);
143 }
144 }
145 }
146 });
147
148 let evm_result = if self.trace {
149 let mut tracer = TracingInspector::new(TracingInspectorConfig::default());
150
151 let res = {
152 let mut vm = context.build_mainnet_with_inspector(&mut tracer);
153
154 debug!(
155 "Starting simulation with tx parameters: {:#?} {:#?}",
156 vm.ctx.tx, vm.ctx.block
157 );
158 vm.inspect_tx(tx_env.clone())
159 };
160
161 Self::print_traces(tracer, res.as_ref().ok())?;
162
163 res
164 } else {
165 let mut vm = context.build_mainnet();
166
167 debug!("Starting simulation with tx parameters: {:#?} {:#?}", vm.ctx.tx, vm.ctx.block);
168
169 vm.replay()
170 };
171
172 interpret_evm_result(evm_result, HashMap::new())
174 }
175
176 pub fn clear_temp_storage(&mut self) -> Result<(), <D as EngineDatabaseInterface>::Error> {
177 self.state.clear_temp_storage()
178 }
179
180 fn print_traces(
181 tracer: TracingInspector,
182 res: Option<&ResultAndState>,
183 ) -> Result<(), SimulationEngineError> {
184 let (exit_reason, _gas_refunded, gas_used, _out, _exec_logs) = match res {
185 Some(ResultAndState { result, state: _ }) => {
186 match result.clone() {
188 ExecutionResult::Success {
189 reason,
190 gas_used,
191 gas_refunded,
192 output,
193 logs,
194 ..
195 } => (reason.into(), gas_refunded, gas_used, Some(output), logs),
196 ExecutionResult::Revert { gas_used, output } => {
197 (
199 InstructionResult::Revert,
200 0_u64,
201 gas_used,
202 Some(Output::Call(output)),
203 vec![],
204 )
205 }
206 ExecutionResult::Halt { reason, gas_used } => {
207 (reason.into(), 0_u64, gas_used, None, vec![])
208 }
209 }
210 }
211 _ => (InstructionResult::Stop, 0_u64, 0, None, vec![]),
212 };
213
214 let trace_res = TraceResult {
215 success: matches!(exit_reason, return_ok!()),
216 traces: Some(vec![tracer.into_traces()]),
217 gas_used,
218 };
219
220 tokio::task::block_in_place(|| -> Result<(), SimulationEngineError> {
221 let future = async {
222 handle_traces(
223 trace_res,
224 env::var("ETHERSCAN_API_KEY").ok(),
225 tycho_common::models::Chain::Ethereum,
226 )
227 .await
228 .map_err(|err| SimulationEngineError::TraceError(err.to_string()))
229 };
230 if let Ok(handle) = Handle::try_current() {
231 handle.block_on(future)
233 } else {
234 let rt = Runtime::new().map_err(|err| {
236 SimulationEngineError::TraceError(format!(
237 "Failed to create a new runtime: {err}"
238 ))
239 })?;
240 rt.block_on(future)
241 }
242 })?;
243
244 Ok(())
245 }
246}
247
248fn interpret_evm_result<DBError: Debug>(
264 evm_result: Result<ResultAndState, EVMError<DBError>>,
265 transient_storage: HashMap<Address, HashMap<U256, U256>>,
266) -> Result<SimulationResult, SimulationEngineError> {
267 match evm_result {
268 Ok(result_and_state) => match result_and_state.result {
269 ExecutionResult::Success { gas_used, gas_refunded, output, .. } => {
270 Ok(interpret_evm_success(
271 gas_used,
272 gas_refunded,
273 output,
274 result_and_state.state,
275 transient_storage,
276 ))
277 }
278 ExecutionResult::Revert { output, gas_used } => {
279 Err(SimulationEngineError::TransactionError {
280 data: format!("0x{encoded}", encoded = hex::encode::<Vec<u8>>(output.into())),
281 gas_used: Some(gas_used),
282 })
283 }
284 ExecutionResult::Halt { reason, gas_used } => {
285 Err(SimulationEngineError::TransactionError {
286 data: format!("{reason:?}"),
287 gas_used: Some(gas_used),
288 })
289 }
290 },
291 Err(evm_error) => match evm_error {
292 EVMError::Transaction(invalid_tx) => Err(SimulationEngineError::TransactionError {
293 data: format!("EVM error: {invalid_tx:?}"),
294 gas_used: None,
295 }),
296 EVMError::Database(db_error) => {
297 Err(SimulationEngineError::StorageError(format!("Storage error: {db_error:?}")))
298 }
299 EVMError::Custom(err) => Err(SimulationEngineError::TransactionError {
300 data: format!("Unexpected error {err}"),
301 gas_used: None,
302 }),
303 EVMError::Header(err) => Err(SimulationEngineError::TransactionError {
304 data: format!("Unexpected error {err}"),
305 gas_used: None,
306 }),
307 },
308 }
309}
310
311fn interpret_evm_success(
313 gas_used: u64,
314 gas_refunded: u64,
315 output: Output,
316 state: EvmState,
317 transient_storage: HashMap<Address, HashMap<U256, U256>>,
318) -> SimulationResult {
319 SimulationResult {
320 result: output.into_data(),
321 state_updates: {
322 let mut account_updates: HashMap<Address, StateUpdate> = HashMap::new();
331 for (address, account) in state {
332 account_updates.insert(
333 address,
334 StateUpdate {
335 balance: Some(account.info.balance),
337 storage: {
339 if account.storage.is_empty() {
340 None
341 } else {
342 let mut slot_updates: HashMap<U256, U256> = HashMap::new();
343 for (index, slot) in account.storage {
344 if slot.is_changed() {
345 slot_updates.insert(index, slot.present_value);
346 }
347 }
348 if slot_updates.is_empty() {
349 None
350 } else {
351 Some(slot_updates)
352 }
353 }
354 },
355 },
356 );
357 }
358 account_updates
359 },
360 gas_used: gas_used - gas_refunded,
361 transient_storage,
362 }
363}
364
365#[derive(Debug)]
366pub struct SimulationParameters {
368 pub caller: Address,
370 pub to: Address,
372 pub data: Vec<u8>,
374 pub value: U256,
376 pub overrides: Option<HashMap<Address, HashMap<U256, U256>>>,
379 pub gas_limit: Option<u64>,
381 pub transient_storage: Option<HashMap<Address, HashMap<U256, U256>>>,
384}
385
386#[cfg(test)]
387mod tests {
388 use std::{error::Error, str::FromStr, time::Instant};
389
390 use alloy::{
391 primitives::{Address, Bytes, Keccak256, B256},
392 sol_types::SolValue,
393 transports::{RpcError, TransportError, TransportErrorKind},
394 };
395 use revm::{
396 context::result::{HaltReason, InvalidTransaction, OutOfGasError, SuccessReason},
397 state::{
398 Account, AccountInfo, AccountStatus, Bytecode, EvmState as rState, EvmStorageSlot,
399 },
400 };
401 use tycho_client::feed::BlockHeader;
402 use tycho_common::simulation::errors::SimulationError;
403
404 use super::*;
405 use crate::evm::engine_db::{
406 engine_db_interface::EngineDatabaseInterface,
407 simulation_db::{EVMProvider, SimulationDB},
408 utils::{get_client, get_runtime},
409 };
410
411 #[test]
412 fn test_interpret_result_ok_success() {
413 let evm_result: Result<ResultAndState, EVMError<TransportError>> = Ok(ResultAndState {
414 result: ExecutionResult::Success {
415 reason: SuccessReason::Return,
416 gas_used: 100_u64,
417 gas_refunded: 10_u64,
418 logs: Vec::new(),
419 output: Output::Call(Bytes::from_static(b"output")),
420 },
421 state: [(
422 Address::ZERO,
424 Account {
425 info: AccountInfo {
426 balance: U256::from_limbs([1, 0, 0, 0]),
427 nonce: 2,
428 code_hash: B256::ZERO,
429 code: None,
430 },
431 transaction_id: 0,
432 storage: [
433 (
435 U256::from_limbs([3, 1, 0, 0]),
436 EvmStorageSlot {
437 original_value: U256::from_limbs([4, 0, 0, 0]),
438 present_value: U256::from_limbs([5, 0, 0, 0]),
439 transaction_id: 0,
440 is_cold: true,
441 },
442 ),
443 (
445 U256::from_limbs([3, 2, 0, 0]),
446 EvmStorageSlot {
447 original_value: U256::from_limbs([4, 0, 0, 0]),
448 present_value: U256::from_limbs([4, 0, 0, 0]),
449 transaction_id: 0,
450 is_cold: true,
451 },
452 ),
453 ]
454 .iter()
455 .cloned()
456 .collect(),
457 status: AccountStatus::Touched,
458 },
459 )]
460 .iter()
461 .cloned()
462 .collect(),
463 });
464
465 let transient_storage = HashMap::from([(
466 Address::from_str("0x1f98400000000000000000000000000000000004").unwrap(),
467 HashMap::from([(U256::from(0), U256::from(1))]),
468 )]);
469 let result = interpret_evm_result(evm_result, transient_storage.clone());
470 let simulation_result = result.unwrap();
471
472 assert_eq!(simulation_result.result, Bytes::from_static(b"output"));
473 let expected_state_updates = [(
474 Address::ZERO,
475 StateUpdate {
476 storage: Some(
477 [(U256::from_limbs([3, 1, 0, 0]), U256::from_limbs([5, 0, 0, 0]))]
478 .iter()
479 .cloned()
480 .collect(),
481 ),
482 balance: Some(U256::from_limbs([1, 0, 0, 0])),
483 },
484 )]
485 .iter()
486 .cloned()
487 .collect();
488 assert_eq!(simulation_result.state_updates, expected_state_updates);
489 assert_eq!(simulation_result.gas_used, 90);
490 assert_eq!(simulation_result.transient_storage, transient_storage);
491 }
492
493 #[test]
494 fn test_interpret_result_ok_revert() {
495 let evm_result: Result<ResultAndState, EVMError<TransportError>> = Ok(ResultAndState {
496 result: ExecutionResult::Revert {
497 gas_used: 100_u64,
498 output: Bytes::from_static(b"output"),
499 },
500 state: rState::default(),
501 });
502
503 let result = interpret_evm_result(evm_result, HashMap::new());
504
505 assert!(result.is_err());
506 let err = result.err().unwrap();
507 match err {
508 SimulationEngineError::TransactionError { data: _, gas_used } => {
509 assert_eq!(
510 format!("0x{}", hex::encode::<Vec<u8>>("output".into())),
511 "0x6f7574707574"
512 );
513 assert_eq!(gas_used, Some(100));
514 }
515 _ => panic!("Wrong type of SimulationError!"),
516 }
517 }
518
519 #[test]
520 fn test_interpret_result_ok_halt() {
521 let evm_result: Result<ResultAndState, EVMError<TransportError>> = Ok(ResultAndState {
522 result: ExecutionResult::Halt {
523 reason: HaltReason::OutOfGas(OutOfGasError::Basic),
524 gas_used: 100_u64,
525 },
526 state: rState::default(),
527 });
528
529 let result = interpret_evm_result(evm_result, HashMap::new());
530
531 assert!(result.is_err());
532 let err = result.err().unwrap();
533 match err {
534 SimulationEngineError::TransactionError { data, gas_used } => {
535 assert_eq!(data, "OutOfGas(Basic)");
536 assert_eq!(gas_used, Some(100));
537 }
538 _ => panic!("Wrong type of SimulationError!"),
539 }
540 }
541
542 #[test]
543 fn test_interpret_result_err_invalid_transaction() {
544 let evm_result: Result<ResultAndState, EVMError<TransportError>> =
545 Err(EVMError::Transaction(InvalidTransaction::PriorityFeeGreaterThanMaxFee));
546
547 let result = interpret_evm_result(evm_result, HashMap::new());
548
549 assert!(result.is_err());
550 let err = result.err().unwrap();
551 match err {
552 SimulationEngineError::TransactionError { data, gas_used } => {
553 assert_eq!(data, "EVM error: PriorityFeeGreaterThanMaxFee");
554 assert_eq!(gas_used, None);
555 }
556 _ => panic!("Wrong type of SimulationError!"),
557 }
558 }
559
560 #[test]
561 fn test_interpret_result_err_db_error() {
562 let evm_result: Result<ResultAndState, EVMError<TransportError>> = Err(EVMError::Database(
563 RpcError::Transport(TransportErrorKind::Custom(Box::from("boo".to_string()))),
564 ));
565
566 let result = interpret_evm_result(evm_result, HashMap::new());
567
568 assert!(result.is_err());
569 let err = result.err().unwrap();
570 match err {
571 SimulationEngineError::StorageError(msg) => {
572 assert_eq!(msg, "Storage error: Transport(Custom(\"boo\"))")
573 }
574 _ => panic!("Wrong type of SimulationError!"),
575 }
576 }
577 fn new_state() -> SimulationDB<EVMProvider> {
578 let runtime = get_runtime().expect("Failed to create test runtime");
579 let client = get_client(None).expect("Failed to create test client");
580 SimulationDB::new(client, runtime, None)
581 }
582
583 #[test]
584 fn test_integration_revm_v2_swap() -> Result<(), Box<dyn Error>> {
585 let state = new_state();
586
587 let caller = Address::from_str("0x0000000000000000000000000000000000000000")?;
589 let router_addr = Address::from_str("0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D")?;
590 let weth_addr = Address::from_str("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2")?;
591 let usdc_addr = Address::from_str("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48")?;
592
593 let selector = "getAmountsOut(uint256,address[])";
595 let amount_in = U256::from(100_000_000);
596 let path = vec![usdc_addr, weth_addr];
597
598 let encoded = {
599 let args = (amount_in, path);
600 let mut hasher = Keccak256::new();
601 hasher.update(selector.as_bytes());
602 let selector_bytes = &hasher.finalize()[..4];
603 let mut data = selector_bytes.to_vec();
604 let mut encoded_args = args.abi_encode();
605 if encoded_args.len() > 32 &&
607 encoded_args[..32] ==
608 [0u8; 31]
609 .into_iter()
610 .chain([32].to_vec())
611 .collect::<Vec<u8>>()
612 {
613 encoded_args = encoded_args[32..].to_vec();
614 }
615 data.extend(encoded_args);
616 data
617 };
618
619 let sim_params = SimulationParameters {
621 caller,
622 to: router_addr,
623 data: encoded,
624 value: U256::from(0u64),
625 overrides: None,
626 gas_limit: None,
627 transient_storage: None,
628 };
629 let mut eng = SimulationEngine::new(state, true);
630
631 let block = BlockHeader {
632 number: 23428552,
633 hash: tycho_common::Bytes::from_str(
634 "0x0000000000000000000000000000000000000000000000000000000000000000",
635 )
636 .unwrap(),
637 timestamp: 1758665355,
638 ..Default::default()
639 };
640 eng.state.set_block(Some(block));
641
642 let result = eng.simulate(&sim_params);
643 type BalanceReturn = Vec<U256>;
644 let amounts_out: Vec<U256> = match result {
645 Ok(SimulationResult { result, .. }) => {
646 BalanceReturn::abi_decode(&result).map_err(|e| {
647 SimulationError::FatalError(format!("Failed to decode result: {e:?}"))
648 })?
649 }
650 _ => panic!("Execution reverted!"),
651 };
652
653 println!(
654 "Swap yielded {} WETH",
655 amounts_out
656 .last()
657 .expect("Empty decoding result")
658 );
659
660 let start = Instant::now();
661 let n_iter = 1000;
662 for _ in 0..n_iter {
663 eng.simulate(&sim_params).unwrap();
664 }
665 let duration = start.elapsed();
666
667 println!("Using revm:");
668 println!("Total Duration [n_iter={n_iter}]: {duration:?}");
669 println!("Single get_amount_out call: {per_call:?}", per_call = duration / n_iter);
670
671 Ok(())
672 }
673
674 #[test]
675 fn test_contract_deployment() -> Result<(), Box<dyn Error>> {
676 let readonly_state = new_state();
677 let state = new_state();
678
679 let selector = "balanceOf(address)";
680 let eoa_address = Address::from_str("0xDFd5293D8e347dFe59E90eFd55b2956a1343963d")?;
681 let calldata = {
682 let args = eoa_address;
683 let mut hasher = Keccak256::new();
684 hasher.update(selector.as_bytes());
685 let selector_bytes = &hasher.finalize()[..4];
686 let mut data = selector_bytes.to_vec();
687 data.extend(args.abi_encode());
688 data
689 };
690
691 let usdt_address = Address::from_str("0xdAC17F958D2ee523a2206206994597C13D831ec7").unwrap();
692 let _ = readonly_state
693 .basic_ref(usdt_address)
694 .unwrap()
695 .unwrap();
696
697 let _ = Bytes::from(hex::decode("608060405234801562000010575f80fd5b5060405162000a6b38038062000a6b83398101604081905262000033916200012c565b600362000041848262000237565b50600462000050838262000237565b506005805460ff191660ff9290921691909117905550620002ff9050565b634e487b7160e01b5f52604160045260245ffd5b5f82601f83011262000092575f80fd5b81516001600160401b0380821115620000af57620000af6200006e565b604051601f8301601f19908116603f01168101908282118183101715620000da57620000da6200006e565b81604052838152602092508683858801011115620000f6575f80fd5b5f91505b83821015620001195785820183015181830184015290820190620000fa565b5f93810190920192909252949350505050565b5f805f606084860312156200013f575f80fd5b83516001600160401b038082111562000156575f80fd5b620001648783880162000082565b945060208601519150808211156200017a575f80fd5b50620001898682870162000082565b925050604084015160ff81168114620001a0575f80fd5b809150509250925092565b600181811c90821680620001c057607f821691505b602082108103620001df57634e487b7160e01b5f52602260045260245ffd5b50919050565b601f82111562000232575f81815260208120601f850160051c810160208610156200020d5750805b601f850160051c820191505b818110156200022e5782815560010162000219565b5050505b505050565b81516001600160401b038111156200025357620002536200006e565b6200026b81620002648454620001ab565b84620001e5565b602080601f831160018114620002a1575f8415620002895750858301515b5f19600386901b1c1916600185901b1785556200022e565b5f85815260208120601f198616915b82811015620002d157888601518255948401946001909101908401620002b0565b5085821015620002ef57878501515f19600388901b60f8161c191681555b5050505050600190811b01905550565b61075e806200030d5f395ff3fe608060405234801561000f575f80fd5b50600436106100a6575f3560e01c8063395093511161006e578063395093511461011f57806370a082311461013257806395d89b411461015a578063a457c2d714610162578063a9059cbb14610175578063dd62ed3e14610188575f80fd5b806306fdde03146100aa578063095ea7b3146100c857806318160ddd146100eb57806323b872dd146100fd578063313ce56714610110575b5f80fd5b6100b261019b565b6040516100bf91906105b9565b60405180910390f35b6100db6100d636600461061f565b61022b565b60405190151581526020016100bf565b6002545b6040519081526020016100bf565b6100db61010b366004610647565b610244565b604051601281526020016100bf565b6100db61012d36600461061f565b610267565b6100ef610140366004610680565b6001600160a01b03165f9081526020819052604090205490565b6100b2610288565b6100db61017036600461061f565b610297565b6100db61018336600461061f565b6102f2565b6100ef6101963660046106a0565b6102ff565b6060600380546101aa906106d1565b80601f01602080910402602001604051908101604052809291908181526020018280546101d6906106d1565b80156102215780601f106101f857610100808354040283529160200191610221565b820191905f5260205f20905b81548152906001019060200180831161020457829003601f168201915b5050505050905090565b5f33610238818585610329565b60019150505b92915050565b5f336102518582856103dc565b61025c85858561043e565b506001949350505050565b5f3361023881858561027983836102ff565b6102839190610709565b610329565b6060600480546101aa906106d1565b5f33816102a482866102ff565b9050838110156102e557604051632983c0c360e21b81526001600160a01b038616600482015260248101829052604481018590526064015b60405180910390fd5b61025c8286868403610329565b5f3361023881858561043e565b6001600160a01b039182165f90815260016020908152604080832093909416825291909152205490565b6001600160a01b0383166103525760405163e602df0560e01b81525f60048201526024016102dc565b6001600160a01b03821661037b57604051634a1406b160e11b81525f60048201526024016102dc565b6001600160a01b038381165f8181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b5f6103e784846102ff565b90505f198114610438578181101561042b57604051637dc7a0d960e11b81526001600160a01b038416600482015260248101829052604481018390526064016102dc565b6104388484848403610329565b50505050565b6001600160a01b03831661046757604051634b637e8f60e11b81525f60048201526024016102dc565b6001600160a01b0382166104905760405163ec442f0560e01b81525f60048201526024016102dc565b61049b8383836104a0565b505050565b6001600160a01b0383166104ca578060025f8282546104bf9190610709565b9091555061053a9050565b6001600160a01b0383165f908152602081905260409020548181101561051c5760405163391434e360e21b81526001600160a01b038516600482015260248101829052604481018390526064016102dc565b6001600160a01b0384165f9081526020819052604090209082900390555b6001600160a01b03821661055657600280548290039055610574565b6001600160a01b0382165f9081526020819052604090208054820190555b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040516103cf91815260200190565b5f6020808352835180828501525f5b818110156105e4578581018301518582016040015282016105c8565b505f604082860101526040601f19601f8301168501019250505092915050565b80356001600160a01b038116811461061a575f80fd5b919050565b5f8060408385031215610630575f80fd5b61063983610604565b946020939093013593505050565b5f805f60608486031215610659575f80fd5b61066284610604565b925061067060208501610604565b9150604084013590509250925092565b5f60208284031215610690575f80fd5b61069982610604565b9392505050565b5f80604083850312156106b1575f80fd5b6106ba83610604565b91506106c860208401610604565b90509250929050565b600181811c90821680620001c057607f821691505b602082108103620001df57634e487b7160e01b5f52602260045260245ffd5b50919050565b8082018082111561023e57634e487b7160e01b5f52601160045260245ffdfea2646970667358221220dfc123d5852c9246ea16b645b377b4436e2f778438195cc6d6c435e8c73a20e764736f6c63430008140033000000000000000000000000000000000000000000000000000000000000000000")?);
702
703 let onchain_bytecode = Bytes::from(hex::decode("608060405234801561000f575f80fd5b50600436106100a6575f3560e01c8063395093511161006e578063395093511461011f57806370a082311461013257806395d89b411461015a578063a457c2d714610162578063a9059cbb14610175578063dd62ed3e14610188575f80fd5b806306fdde03146100aa578063095ea7b3146100c857806318160ddd146100eb57806323b872dd146100fd578063313ce56714610110575b5f80fd5b6100b261019b565b6040516100bf91906105b9565b60405180910390f35b6100db6100d636600461061f565b61022b565b60405190151581526020016100bf565b6002545b6040519081526020016100bf565b6100db61010b366004610647565b610244565b604051601281526020016100bf565b6100db61012d36600461061f565b610267565b6100ef610140366004610680565b6001600160a01b03165f9081526020819052604090205490565b6100b2610288565b6100db61017036600461061f565b610297565b6100db61018336600461061f565b6102f2565b6100ef6101963660046106a0565b6102ff565b6060600380546101aa906106d1565b80601f01602080910402602001604051908101604052809291908181526020018280546101d6906106d1565b80156102215780601f106101f857610100808354040283529160200191610221565b820191905f5260205f20905b81548152906001019060200180831161020457829003601f168201915b5050505050905090565b5f33610238818585610329565b60019150505b92915050565b5f336102518582856103dc565b61025c85858561043e565b506001949350505050565b5f3361023881858561027983836102ff565b6102839190610709565b610329565b6060600480546101aa906106d1565b5f33816102a482866102ff565b9050838110156102e557604051632983c0c360e21b81526001600160a01b038616600482015260248101829052604481018590526064015b60405180910390fd5b61025c8286868403610329565b5f3361023881858561043e565b6001600160a01b039182165f90815260016020908152604080832093909416825291909152205490565b6001600160a01b0383166103525760405163e602df0560e01b81525f60048201526024016102dc565b6001600160a01b03821661037b57604051634a1406b160e11b81525f60048201526024016102dc565b6001600160a01b038381165f8181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b5f6103e784846102ff565b90505f198114610438578181101561042b57604051637dc7a0d960e11b81526001600160a01b038416600482015260248101829052604481018390526064016102dc565b6104388484848403610329565b50505050565b6001600160a01b03831661046757604051634b637e8f60e11b81525f60048201526024016102dc565b6001600160a01b0382166104905760405163ec442f0560e01b81525f60048201526024016102dc565b61049b8383836104a0565b505050565b6001600160a01b0383166104ca578060025f8282546104bf9190610709565b9091555061053a9050565b6001600160a01b0383165f908152602081905260409020548181101561051c5760405163391434e360e21b81526001600160a01b038516600482015260248101829052604481018390526064016102dc565b6001600160a01b0384165f9081526020819052604090209082900390555b6001600160a01b03821661055657600280548290039055610574565b6001600160a01b0382165f9081526020819052604090208054820190555b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040516103cf91815260200190565b5f6020808352835180828501525f5b818110156105e4578581018301518582016040015282016105c8565b505f604082860101526040601f19601f8301168501019250505092915050565b80356001600160a01b038116811461061a575f80fd5b919050565b5f8060408385031215610630575f80fd5b61063983610604565b946020939093013593505050565b5f805f60608486031215610659575f80fd5b61066284610604565b925061067060208501610604565b9150604084013590509250925092565b5f60208284031215610690575f80fd5b61069982610604565b9392505050565b5f80604083850312156106b1575f80fd5b6106ba83610604565b91506106c860208401610604565b90509250929050565b600181811c908216806106e557607f821691505b60208210810361070357634e487b7160e01b5f52602260045260245ffd5b50919050565b8082018082111561023e57634e487b7160e01b5f52601160045260245ffdfea2646970667358221220dfc123d5852c9246ea16b645b377b4436e2f778438195cc6d6c435e8c73a20e764736f6c63430008140033000000000000000000000000000000000000000000000000000000000000000000")?);
704 let code = Bytecode::new_raw(onchain_bytecode);
705 let contract_acc_info = AccountInfo::new(
706 U256::from(0),
707 0,
708 code.hash_slow(),
709 code,
710 );
712 let mut storage = HashMap::default();
714 storage.insert(
715 U256::from_str(
716 "25842306973167774731510882590667189188844731550465818811072464953030320818263",
717 )
718 .unwrap(),
719 U256::from_str("25").unwrap(),
720 );
721 state
725 .init_account(usdt_address, contract_acc_info, Some(storage), true)
726 .expect("Failed to init account");
727
728 let mut overrides = HashMap::default();
750 let mut storage_overwrite = HashMap::default();
751 storage_overwrite.insert(
752 U256::from_str(
753 "25842306973167774731510882590667189188844731550465818811072464953030320818263",
754 )
755 .unwrap(),
756 U256::from_str("80").unwrap(),
757 );
758 overrides.insert(usdt_address, storage_overwrite);
759
760 let sim_params = SimulationParameters {
761 caller: Address::from_str("0x0000000000000000000000000000000000000000")?,
762 to: usdt_address,
763 data: calldata,
765 value: U256::from(0u64),
766 overrides: Some(overrides),
767 gas_limit: None,
768 transient_storage: None,
769 };
770
771 let mut eng = SimulationEngine::new(state, false);
772
773 let block = BlockHeader {
775 number: 1,
776 hash: tycho_common::Bytes::from_str(
777 "0x0000000000000000000000000000000000000000000000000000000000000000",
778 )
779 .unwrap(),
780 timestamp: 1748397011,
781 ..Default::default()
782 };
783 eng.state.set_block(Some(block));
784
785 println!("Executing balanceOf");
797 let result = eng.simulate(&sim_params);
798 let balance = match result {
799 Ok(SimulationResult { result, .. }) => U256::abi_decode(&result)?,
800 Err(error) => panic!("{error:?}"),
801 };
802 println!("Balance: {balance}");
803
804 Ok(())
805 }
806}