#[path = "support/mock.rs"]
mod mock;
use std::sync::Arc;
use alloy_eips::BlockId;
use alloy_primitives::{Address, Bytes, U256, keccak256};
use alloy_sol_types::{SolCall, SolValue, sol};
use anyhow::Result;
use evm_fork_cache::cache::StorageBatchFetchFn;
use evm_fork_cache::events::StateView;
use evm_fork_cache::{
ColdStartCall, ColdStartConfig, ColdStartPlan, ColdStartPlanner, ColdStartResults,
ColdStartStep,
};
sol! {
interface MockERC20 {
function balanceOf(address account) returns (uint256);
}
}
fn balance_slot(owner: Address) -> U256 {
let key = keccak256((owner, U256::from(mock::MOCK_ERC20_BALANCE_SLOT)).abi_encode());
U256::from_be_bytes(key.0)
}
struct WorkingSetWarmer {
token: Address,
owner: Address,
discovered: Vec<(Address, U256)>,
verified_round: bool,
}
impl ColdStartPlanner for WorkingSetWarmer {
fn initial_plan(&mut self, _state: &dyn StateView) -> ColdStartPlan {
ColdStartPlan {
discover: vec![ColdStartCall {
from: self.owner,
to: self.token,
calldata: Bytes::from(
MockERC20::balanceOfCall {
account: self.owner,
}
.abi_encode(),
),
restrict_to: Some(vec![self.token]),
}],
..Default::default()
}
}
fn on_results(&mut self, results: &ColdStartResults, _state: &dyn StateView) -> ColdStartStep {
if self.verified_round {
return ColdStartStep::Done;
}
for call in &results.discovered {
self.discovered.extend(call.access.slots.iter().copied());
}
self.verified_round = true;
ColdStartStep::Continue(ColdStartPlan {
verify: self.discovered.clone(),
..Default::default()
})
}
}
#[tokio::main(flavor = "multi_thread")]
async fn main() -> Result<()> {
let mut cache = mock::offline_cache().await?;
let token = Address::repeat_byte(0x11);
let owner = Address::repeat_byte(0x22);
mock::install_default_account(&mut cache, Address::ZERO);
mock::install_default_account(&mut cache, owner);
mock::install_mock_erc20(&mut cache, token);
let slot = balance_slot(owner);
let fetcher: StorageBatchFetchFn =
Arc::new(move |requests: Vec<(Address, U256)>, _block: BlockId| {
requests
.into_iter()
.map(|(a, s)| {
let value = if (a, s) == (token, slot) {
U256::from(1_000_000u64)
} else {
U256::ZERO
};
(a, s, Ok(value))
})
.collect()
});
cache.set_storage_batch_fetcher(fetcher);
println!(
"before cold start: balanceOf(owner) = {} (slot uncached, reads 0)",
mock::balance_of(&mut cache, token, owner)?
);
let mut planner = WorkingSetWarmer {
token,
owner,
discovered: Vec::new(),
verified_round: false,
};
let report = cache.run_cold_start(&mut planner, ColdStartConfig::default())?;
println!("\n=== cold-start report ===");
println!(" rounds executed: {}", report.rounds);
println!(" slots discovered: {}", report.discovered_slots);
println!(
" slots verified: {} requested, {} changed + injected",
report.verified_slots, report.changed_slots
);
println!(" fetch failures: {}", report.failed_slots);
println!(
"\nafter cold start: balanceOf(owner) = {} (warmed from the fetcher; reads are now local)",
mock::balance_of(&mut cache, token, owner)?
);
Ok(())
}