#[path = "support/mock.rs"]
mod mock;
use alloy_primitives::{Address, Bytes, U256, address};
use alloy_sol_types::{SolCall, sol};
use anyhow::Result;
use evm_fork_cache::CallTracer;
use evm_fork_cache::cache::TxConfig;
use revm::context::result::ExecutionResult;
sol! {
interface IErc20 {
function balanceOf(address account) returns (uint256);
function allowance(address owner, address spender) returns (uint256);
function totalSupply() returns (uint256);
function transferFrom(address from, address to, uint256 amount) returns (bool);
}
}
#[tokio::main(flavor = "multi_thread")]
async fn main() -> Result<()> {
let mut cache = mock::offline_cache().await?;
let usdc = address!("A0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48");
let alice = address!("00000000000000000000000000000000000000A1");
let bob = address!("00000000000000000000000000000000000000B2");
let router = address!("00000000000000000000000000000000000000C3");
for a in [Address::ZERO, alice, bob, router] {
mock::install_default_account(&mut cache, a);
}
mock::install_mock_erc20(&mut cache, usdc);
cache.insert_storage_slot(usdc, U256::from(2u64), U256::from(500_000_000_000u64))?;
let one_m = U256::from(1_000_000_000_000u64);
let mut sim = cache.mock_overlay();
let ok_bal = sim.mock_balance(usdc, alice, one_m)?;
let ok_appr = sim.mock_allowance(usdc, alice, router, U256::MAX)?;
let ok_ts = sim.mock_call(
usdc,
IErc20::totalSupplyCall {},
U256::from(2_000_000_000_000u64),
)?;
println!("mocks applied → balance={ok_bal} approval={ok_appr} totalSupply={ok_ts}");
println!("\non the overlay:");
println!(
" balanceOf(alice) = {}",
sim.call_sol(usdc, IErc20::balanceOfCall { account: alice })?
);
println!(
" allowance(a→r) = {}",
sim.call_sol(
usdc,
IErc20::allowanceCall {
owner: alice,
spender: router
}
)?
);
println!(
" totalSupply() = {}",
sim.call_sol(usdc, IErc20::totalSupplyCall {})?
);
let cd = Bytes::from(
IErc20::transferFromCall {
from: alice,
to: bob,
amount: U256::from(250_000_000_000u64),
}
.abi_encode(),
);
let (res, _) = sim.call_raw_with_inspector(
router,
usdc,
cd,
&TxConfig::default(),
CallTracer::new(),
true,
)?;
let status = if matches!(res, ExecutionResult::Success { .. }) {
"Success"
} else {
"FAILED"
};
println!("\nrouter.transferFrom(alice → bob, 250k): {status}");
println!(
" alice = {}",
sim.call_sol(usdc, IErc20::balanceOfCall { account: alice })?
);
println!(
" bob = {}",
sim.call_sol(usdc, IErc20::balanceOfCall { account: bob })?
);
drop(sim);
let cache_alice: U256 = cache.call_sol(usdc, IErc20::balanceOfCall { account: alice })?;
println!("\ncache (untouched): balanceOf(alice) = {cache_alice} ← mocks never persisted");
Ok(())
}