#![allow(dead_code)]
use std::collections::HashMap;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, Condvar, Mutex};
use alloy_eips::BlockId;
use alloy_primitives::{Address, B256, Bytes, U256, hex};
use alloy_provider::RootProvider;
use alloy_provider::network::AnyNetwork;
use alloy_rpc_client::RpcClient;
use alloy_sol_types::{SolCall, sol};
use alloy_transport::mock::Asserter;
use anyhow::{Result, anyhow};
use evm_fork_cache::bulk_storage::AccountFieldsSample;
use evm_fork_cache::cache::{AccountFieldsFetchFn, EvmCache, StorageBatchFetchFn};
use evm_fork_cache::errors::{StorageFetchError, StorageFetchResult};
use revm::context::result::ExecutionResult;
use revm::state::{AccountInfo, Bytecode};
pub const MOCK_ERC20_RUNTIME_HEX: &str = include_str!("../../fixtures/mock_erc20_runtime.hex");
pub const MOCK_ERC20_CREATION_HEX: &str = include_str!("../../fixtures/mock_erc20_creation.hex");
pub const MOCK_ERC20_BALANCE_SLOT: u64 = 3;
sol! {
interface MockERC20 {
function balanceOf(address account) returns (uint256);
function transfer(address to, uint256 amount) returns (bool);
}
}
pub fn mock_erc20_runtime() -> Bytecode {
let bytes = hex::decode(MOCK_ERC20_RUNTIME_HEX.trim()).expect("valid runtime hex");
Bytecode::new_raw(Bytes::from(bytes))
}
pub fn mock_erc20_creation_code() -> Vec<u8> {
hex::decode(MOCK_ERC20_CREATION_HEX.trim()).expect("valid creation hex")
}
pub async fn setup_cache() -> Result<EvmCache> {
let asserter = Asserter::new();
let client = RpcClient::mocked(asserter);
let provider = RootProvider::<AnyNetwork>::new(client);
Ok(EvmCache::new(Arc::new(provider)).await)
}
pub async fn setup_cache_with_asserter() -> Result<(EvmCache, Asserter)> {
let asserter = Asserter::new();
let client = RpcClient::mocked(asserter.clone());
let provider = RootProvider::<AnyNetwork>::new(client);
Ok((EvmCache::new(Arc::new(provider)).await, asserter))
}
pub fn install_mock_erc20(cache: &mut EvmCache, token: Address) {
let bytecode = mock_erc20_runtime();
let code_hash = bytecode.hash_slow();
let info = AccountInfo {
balance: U256::ZERO,
nonce: 0,
code: Some(bytecode),
code_hash,
account_id: None,
};
cache.db_mut().insert_account_info(token, info);
cache
.db_mut()
.replace_account_storage(token, Default::default())
.expect("mark mock storage as cleared");
}
pub fn install_default_account(cache: &mut EvmCache, addr: Address) {
cache
.db_mut()
.insert_account_info(addr, AccountInfo::default());
}
pub fn balance_of(cache: &mut EvmCache, token: Address, owner: Address) -> Result<U256> {
let call = MockERC20::balanceOfCall { account: owner };
let result = cache.call_raw(owner, token, Bytes::from(call.abi_encode()), false)?;
match result {
ExecutionResult::Success { output, .. } => Ok(
MockERC20::balanceOfCall::abi_decode_returns(&output.into_data())?,
),
other => Err(anyhow!("balanceOf call failed: {other:?}")),
}
}
pub fn stub_fetcher(values: HashMap<(Address, U256), U256>) -> StorageBatchFetchFn {
Arc::new(move |requests: Vec<(Address, U256)>, _block: BlockId| {
requests
.into_iter()
.map(|(addr, slot)| {
let value = values.get(&(addr, slot)).copied().unwrap_or(U256::ZERO);
(addr, slot, Ok(value))
})
.collect()
})
}
pub fn stub_fields_fetcher(
samples: HashMap<Address, (U256, B256)>,
calls: Arc<AtomicUsize>,
) -> AccountFieldsFetchFn {
Arc::new(move |addresses: Vec<Address>, _block: BlockId| {
calls.fetch_add(1, Ordering::SeqCst);
Ok(addresses
.into_iter()
.filter_map(|address| {
samples.get(&address).map(|(balance, code_hash)| {
(
address,
AccountFieldsSample {
balance: *balance,
code_hash: *code_hash,
},
)
})
})
.collect())
})
}
pub fn failing_fields_fetcher(calls: Arc<AtomicUsize>) -> AccountFieldsFetchFn {
Arc::new(move |_addresses: Vec<Address>, _block: BlockId| {
calls.fetch_add(1, Ordering::SeqCst);
Err(StorageFetchError::custom("stub transport failure"))
})
}
pub fn failing_fetcher() -> StorageBatchFetchFn {
Arc::new(|requests: Vec<(Address, U256)>, _block: BlockId| {
requests
.into_iter()
.map(|(addr, slot)| {
(
addr,
slot,
Err(StorageFetchError::custom("stub fetcher error")),
)
})
.collect()
})
}
#[derive(Clone, Default)]
pub struct Gate {
inner: Arc<(Mutex<bool>, Condvar)>,
}
impl Gate {
pub fn new() -> Self {
Self::default()
}
pub fn wait(&self) {
let (lock, cv) = &*self.inner;
let mut released = lock.lock().unwrap_or_else(|e| e.into_inner());
while !*released {
released = cv.wait(released).unwrap_or_else(|e| e.into_inner());
}
}
pub fn release(&self) {
let (lock, cv) = &*self.inner;
*lock.lock().unwrap_or_else(|e| e.into_inner()) = true;
cv.notify_all();
}
}
pub fn gated_tracking_fetcher(
values: HashMap<(Address, U256), U256>,
gate: Gate,
) -> StorageBatchFetchFn {
Arc::new(move |requests: Vec<(Address, U256)>, _block: BlockId| {
gate.wait();
requests
.into_iter()
.map(|(addr, slot)| {
let value = values.get(&(addr, slot)).copied().unwrap_or(U256::ZERO);
(addr, slot, Ok(value))
})
.collect()
})
}
pub fn panicking_fetcher() -> StorageBatchFetchFn {
Arc::new(
|_requests: Vec<(Address, U256)>,
_block: BlockId|
-> Vec<(Address, U256, StorageFetchResult<U256>)> {
panic!("panicking fetcher: deliberate failure for the Unverified test")
},
)
}
pub fn transfer(
cache: &mut EvmCache,
token: Address,
from: Address,
to: Address,
amount: U256,
) -> Result<ExecutionResult> {
let call = MockERC20::transferCall { to, amount };
Ok(cache.call_raw(from, token, Bytes::from(call.abi_encode()), true)?)
}