example_uniswap_get_reserves/
main.rs

1//! Example of uniswap getReserves() call emulation.
2#![cfg_attr(not(test), warn(unused_crate_dependencies))]
3
4use alloy_eips::BlockId;
5use alloy_provider::ProviderBuilder;
6use alloy_sol_types::{sol, SolCall};
7use database::{AlloyDB, CacheDB};
8use revm::{
9    context_interface::result::{ExecutionResult, Output},
10    database_interface::{DatabaseRef, EmptyDB, WrapDatabaseAsync},
11    primitives::{address, TxKind, U256},
12    Context, ExecuteEvm, MainBuilder, MainContext,
13};
14
15#[tokio::main]
16async fn main() -> anyhow::Result<()> {
17    // Initialize the Alloy provider and database
18    let rpc_url = "https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27";
19    let provider = ProviderBuilder::new().on_builtin(rpc_url).await?;
20
21    let alloy_db = WrapDatabaseAsync::new(AlloyDB::new(provider, BlockId::latest())).unwrap();
22    let cache_db = CacheDB::new(alloy_db);
23
24    // ----------------------------------------------------------- //
25    //             Storage slots of UniV2Pair contract             //
26    // =========================================================== //
27    // storage[5] = factory: address                               //
28    // storage[6] = token0: address                                //
29    // storage[7] = token1: address                                //
30    // storage[8] = (res0, res1, ts): (uint112, uint112, uint32)   //
31    // storage[9] = price0CumulativeLast: uint256                  //
32    // storage[10] = price1CumulativeLast: uint256                 //
33    // storage[11] = kLast: uint256                                //
34    // =========================================================== //
35
36    // Choose slot of storage that you would like to transact with
37    let slot = U256::from(8);
38
39    // ETH/USDT pair on Uniswap V2
40    let pool_address = address!("0d4a11d5EEaaC28EC3F61d100daF4d40471f1852");
41
42    // Generate abi for the calldata from the human readable interface
43    sol! {
44        function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
45    }
46
47    // Encode abi into Bytes
48    let encoded = getReservesCall::new(()).abi_encode();
49
50    // Query basic properties of an account incl bytecode
51    let acc_info = cache_db.basic_ref(pool_address).unwrap().unwrap();
52
53    // Query value of storage slot at account address
54    let value = cache_db.storage_ref(pool_address, slot).unwrap();
55
56    // Initialise empty in-memory-db
57    let mut cache_db = CacheDB::new(EmptyDB::default());
58
59    // Insert basic account info which was generated via Web3DB with the corresponding address
60    cache_db.insert_account_info(pool_address, acc_info);
61
62    // Insert our pre-loaded storage slot to the corresponding contract key (address) in the DB
63    cache_db
64        .insert_account_storage(pool_address, slot, value)
65        .unwrap();
66
67    // Initialise an empty (default) EVM
68    let mut evm = Context::mainnet()
69        .with_db(cache_db)
70        .modify_tx_chained(|tx| {
71            // fill in missing bits of env struct
72            // change that to whatever caller you want to be
73            tx.caller = address!("0000000000000000000000000000000000000000");
74            // account you want to transact with
75            tx.kind = TxKind::Call(pool_address);
76            // calldata formed via abigen
77            tx.data = encoded.into();
78            // transaction value in wei
79            tx.value = U256::from(0);
80        })
81        .build_mainnet();
82
83    // Execute transaction without writing to the DB
84    let ref_tx = evm.transact_previous().unwrap();
85    // Select ExecutionResult struct
86    let result = ref_tx.result;
87
88    // Unpack output call enum into raw bytes
89    let value = match result {
90        ExecutionResult::Success {
91            output: Output::Call(value),
92            ..
93        } => value,
94        _ => panic!("Execution failed: {result:?}"),
95    };
96
97    // Decode bytes to reserves + ts via alloy's abi decode
98    let return_vals = getReservesCall::abi_decode_returns(&value, true)?;
99
100    // Print emulated getReserves() call output
101    println!("Reserve0: {:#?}", return_vals.reserve0);
102    println!("Reserve1: {:#?}", return_vals.reserve1);
103    println!("Timestamp: {:#?}", return_vals.blockTimestampLast);
104
105    Ok(())
106}