use degenbot_executor::WarmupSlots;
use revm::database::CacheDB;
use revm::database_interface::DatabaseRef;
use revm::primitives::{Address, Bytes, U256};
use revm::state::AccountInfo;
pub const OWNER_FUND_ETH: u64 = 100;
pub const EXECUTOR_FUND_ETH: u64 = 10;
pub const ONE_WEI: U256 = U256::from_limbs([1, 0, 0, 0]);
#[derive(Debug, Clone)]
pub struct SimulationOverrideParams {
pub owner: Address,
pub inject_code: bool,
pub injected_address: Option<Address>,
pub runtime_bytecode: Bytes,
pub warmup: WarmupSlots,
pub weth_address: Address,
pub pool_manager_address: Address,
}
pub fn apply_simulation_overrides<ExtDb>(
cache_db: &mut CacheDB<ExtDb>,
params: &SimulationOverrideParams,
) -> Result<(), OverrideError>
where
ExtDb: DatabaseRef,
<ExtDb as DatabaseRef>::Error: std::fmt::Display,
{
cache_db.insert_account_info(
params.owner,
AccountInfo {
balance: eth_amount(OWNER_FUND_ETH),
..AccountInfo::default()
},
);
if params.inject_code {
#[expect(clippy::expect_used)] let injected = params.injected_address.expect(
"inject_code is true but injected_address is None — supply the injection address",
);
cache_db.insert_account_info(
injected,
AccountInfo {
balance: eth_amount(EXECUTOR_FUND_ETH),
code: Some(revm::bytecode::Bytecode::new_raw(
params.runtime_bytecode.clone(),
)),
..AccountInfo::default()
},
);
merge_warmup_slots(
cache_db,
¶ms.warmup,
params.weth_address,
params.pool_manager_address,
)?;
insert_weth_balance_override(
cache_db,
params.weth_address,
params.warmup.weth_balance,
eth_amount(EXECUTOR_FUND_ETH),
)?;
}
Ok(())
}
fn merge_warmup_slots<ExtDb>(
cache_db: &mut CacheDB<ExtDb>,
warmup: &WarmupSlots,
weth_address: Address,
pool_manager_address: Address,
) -> Result<(), OverrideError>
where
ExtDb: DatabaseRef,
<ExtDb as DatabaseRef>::Error: std::fmt::Display,
{
cache_db
.insert_account_storage(weth_address, warmup.weth_balance, ONE_WEI)
.map_err(|e| OverrideError::Insertion(e.to_string()))?;
cache_db
.insert_account_storage(pool_manager_address, warmup.erc6909_weth, ONE_WEI)
.map_err(|e| OverrideError::Insertion(e.to_string()))?;
cache_db
.insert_account_storage(pool_manager_address, warmup.erc6909_native, ONE_WEI)
.map_err(|e| OverrideError::Insertion(e.to_string()))?;
Ok(())
}
fn insert_weth_balance_override<ExtDb>(
cache_db: &mut CacheDB<ExtDb>,
weth_address: Address,
weth_balance_slot: U256,
balance: U256,
) -> Result<(), OverrideError>
where
ExtDb: DatabaseRef,
<ExtDb as DatabaseRef>::Error: std::fmt::Display,
{
cache_db
.insert_account_storage(weth_address, weth_balance_slot, balance)
.map_err(|e| OverrideError::Insertion(e.to_string()))
}
fn eth_amount(eth: u64) -> U256 {
U256::from(eth) * U256::from(10u128.pow(18))
}
#[derive(Debug, thiserror::Error)]
pub enum OverrideError {
#[error("CacheDB insertion backing fetch failed: {0}")]
Insertion(String),
}
#[expect(clippy::expect_used)]
#[cfg(test)]
mod tests {
use super::*;
use alloy::primitives::address;
use degenbot_executor::compute_simulation_warmup_slots;
use revm::database::CacheDB;
use revm::database_interface::EmptyDB;
use revm::primitives::Address;
const WETH: Address = address!("c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2");
const PM: Address = address!("000000000004444c5dc75cb358380d2e3de08a90");
const OWNER: Address = address!("9c56a29c7231974c269e24f9fb3c29203039089e");
const INJECTED: Address = address!("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
const RUNTIME_BYTECODE: &[u8] = &0xDEAD_BEEF_u32.to_be_bytes();
fn read_storage(db: &CacheDB<EmptyDB>, addr: Address, slot: U256) -> U256 {
db.storage_ref(addr, slot).unwrap_or_default()
}
#[test]
fn apply_simulation_overrides_writes_match_oracle_values() {
for inject_code in [false, true] {
let warmup = compute_simulation_warmup_slots(INJECTED, WETH);
let runtime = Bytes::from_static(RUNTIME_BYTECODE);
let mut cache_db: CacheDB<EmptyDB> = CacheDB::new(EmptyDB::default());
apply_simulation_overrides(
&mut cache_db,
&SimulationOverrideParams {
owner: OWNER,
inject_code,
injected_address: (inject_code).then_some(INJECTED),
runtime_bytecode: runtime.clone(),
warmup,
weth_address: WETH,
pool_manager_address: PM,
},
)
.expect("apply_simulation_overrides with EmptyDB backing cannot fail");
let owner_info = cache_db.basic_ref(OWNER).expect("owner loaded");
assert_eq!(
owner_info.as_ref().map(|i| i.balance),
Some(eth_amount(OWNER_FUND_ETH)),
"owner balance: inject_code={inject_code}",
);
if inject_code {
let injected_info = cache_db
.basic_ref(INJECTED)
.expect("injected loaded")
.expect("injected account present");
assert_eq!(
injected_info.balance,
eth_amount(EXECUTOR_FUND_ETH),
"injected balance: inject_code={inject_code}",
);
assert_eq!(
injected_info
.code
.expect("injected code present")
.original_bytes(),
runtime,
"injected runtime bytecode carried verbatim: inject_code={inject_code}",
);
assert_eq!(
read_storage(&cache_db, WETH, warmup.weth_balance),
eth_amount(EXECUTOR_FUND_ETH),
"WETH9 balanceOf slot raised to 10 ETH: inject_code={inject_code}",
);
assert_eq!(
read_storage(&cache_db, PM, warmup.erc6909_weth),
ONE_WEI,
"PM ERC6909 (weth-id) warmed to 1 wei: inject_code={inject_code}",
);
assert_eq!(
read_storage(&cache_db, PM, warmup.erc6909_native),
ONE_WEI,
"PM ERC6909 (native-id) warmed to 1 wei: inject_code={inject_code}",
);
}
}
}
#[test]
fn apply_simulation_overrides_skip_when_no_code_injection() {
let warmup = compute_simulation_warmup_slots(INJECTED, WETH);
let mut cache_db: CacheDB<EmptyDB> = CacheDB::new(EmptyDB::default());
apply_simulation_overrides(
&mut cache_db,
&SimulationOverrideParams {
owner: OWNER,
inject_code: false,
injected_address: None,
runtime_bytecode: Bytes::new(),
warmup,
weth_address: WETH,
pool_manager_address: PM,
},
)
.expect("EmptyDB backing cannot fail");
let owner_info = cache_db.basic_ref(OWNER).expect("owner loaded");
assert_eq!(
owner_info.as_ref().map(|i| i.balance),
Some(eth_amount(OWNER_FUND_ETH)),
);
assert_eq!(
read_storage(&cache_db, WETH, warmup.weth_balance),
U256::ZERO,
"WETH9 slot untouched when inject_code=false",
);
}
}