#![cfg_attr(test, allow(clippy::unreadable_literal))]
use std::sync::OnceLock;
use alloy::primitives::{Address, U256};
use degenbot_bot::bot_core::SimAnchorState;
pub const SIM_SERVE_ENGINE_STATE_ENV: &str = "DEGENBOT_SIM_SERVE_ENGINE_STATE";
const BOT_STATE_DB_LOG_PREFIX: &str = "[bot-state-db]";
static SERVE_ENABLED: OnceLock<bool> = OnceLock::new();
#[cfg(test)]
static TEST_FORCE: std::sync::atomic::AtomicI8 = std::sync::atomic::AtomicI8::new(-1);
fn serve_enabled() -> bool {
#[cfg(test)]
{
let forced = TEST_FORCE.load(std::sync::atomic::Ordering::Acquire);
if forced != -1 {
return forced != 0;
}
}
*SERVE_ENABLED
.get_or_init(|| std::env::var_os(SIM_SERVE_ENGINE_STATE_ENV).is_some_and(|v| v == "1"))
}
#[cfg(test)]
pub fn force_serve_enabled_for_tests(on: Option<bool>) {
match on {
Some(true) => TEST_FORCE.store(1, std::sync::atomic::Ordering::Release),
Some(false) => TEST_FORCE.store(0, std::sync::atomic::Ordering::Release),
None => TEST_FORCE.store(-1, std::sync::atomic::Ordering::Release),
}
}
#[must_use]
pub fn serve_tracked_slot(
anchor: &SimAnchorState,
address: Address,
index: U256,
rpc_value: U256,
) -> Option<U256> {
if !serve_enabled() {
return None;
}
let probe = anchor.probe_tracked_storage_slot(address, index)?;
let served = U256::from_be_bytes(probe.engine_word.0);
let delta_xor = served ^ rpc_value;
tracing::info!(
pool_addr = %format!("{address:?}"),
slot = %index,
kind = ?probe.kind,
served = %hex_padded_u256(served),
rpc = %hex_padded_u256(rpc_value),
delta_xor = %hex_padded_u256(delta_xor),
update_block = probe.update_block,
"{BOT_STATE_DB_LOG_PREFIX}"
);
Some(served)
}
fn hex_padded_u256(word: U256) -> String {
use alloy::hex;
hex::encode(word.to_be_bytes::<32>())
}
#[expect(clippy::unwrap_used, clippy::expect_used)]
#[cfg(test)]
mod tests {
use super::*;
use crate::sim::evm::BotStateDb;
use alloy::primitives::{address, aliases::U112, Address, U256};
use degenbot_bot::bot_core::{BotState, RegisterV2PoolParams};
use revm::database_interface::DatabaseRef;
use revm::primitives::{StorageKey, StorageValue, B256 as RevmB256};
use revm::state::AccountInfo;
const V2_RESERVES_SLOT: u64 = 8;
const V2_ADDR: Address = address!("b4e16d0168e52d35cacd2c6185b44281ec28c9dc");
#[derive(Default)]
struct FixedStorageDb {
slots: std::collections::HashMap<(Address, U256), U256>,
}
impl DatabaseRef for FixedStorageDb {
type Error = std::convert::Infallible;
fn basic_ref(&self, _address: Address) -> Result<Option<AccountInfo>, Self::Error> {
Ok(None)
}
fn storage_ref(
&self,
address: Address,
index: StorageKey,
) -> Result<StorageValue, Self::Error> {
Ok(self
.slots
.get(&(address, index))
.copied()
.unwrap_or(StorageValue::ZERO))
}
fn code_by_hash_ref(
&self,
_code_hash: RevmB256,
) -> Result<revm::bytecode::Bytecode, Self::Error> {
Ok(revm::bytecode::Bytecode::default())
}
fn block_hash_ref(&self, _number: u64) -> Result<RevmB256, Self::Error> {
Ok(RevmB256::ZERO)
}
}
fn v2_bot_state(reserve0: u128, reserve1: u128, update_block: u64) -> BotState {
let mut core = BotState::new();
let params = RegisterV2PoolParams {
address: V2_ADDR,
token0: Address::ZERO,
token1: Address::from([0xa0; 20]),
reserve0: U112::from(reserve0),
reserve1: U112::from(reserve1),
fee_token0: (997, 1000),
fee_token1: (997, 1000),
factory: Address::ZERO,
update_block,
stable_swap: false,
fee_denominator: None,
..Default::default()
};
core.register_v2_pool(¶ms).expect("V2 registration");
core
}
fn pack_v2_reserves(reserve0: u128, reserve1: u128) -> U256 {
let r0 = U256::from(reserve0);
let r1 = U256::from(reserve1);
r0 | (r1 << 112u32)
}
static SERVE_TEST_GUARD: std::sync::Mutex<()> = std::sync::Mutex::new(());
#[test]
fn serve_returns_engine_v2_reserves_when_gate_on() {
let _g = SERVE_TEST_GUARD.lock().unwrap();
let core = v2_bot_state(1_000_000, 2_000_000, 18_012_345);
let anchor = SimAnchorState::snapshot(&core);
let mut db = FixedStorageDb::default();
let rpc_word = pack_v2_reserves(999_999, 1_999_999);
db.slots
.insert((V2_ADDR, U256::from(V2_RESERVES_SLOT)), rpc_word);
force_serve_enabled_for_tests(Some(true));
let bot_db = BotStateDb::new(&anchor, db);
let got = bot_db
.storage_ref(V2_ADDR, U256::from(V2_RESERVES_SLOT))
.expect("storage_ref ok");
force_serve_enabled_for_tests(None);
let expected = pack_v2_reserves(1_000_000, 2_000_000);
assert_eq!(
got, expected,
"serving returns the engine's packed reserves, not the RPC fallback's"
);
assert_ne!(
got, rpc_word,
"the served value must differ from the divergent RPC value"
);
}
#[test]
fn serve_falls_through_to_rpc_when_gate_off() {
let _g = SERVE_TEST_GUARD.lock().unwrap();
let core = v2_bot_state(1_000_000, 2_000_000, 18_012_345);
let anchor = SimAnchorState::snapshot(&core);
let rpc_word = pack_v2_reserves(999_999, 1_999_999);
let mut db = FixedStorageDb::default();
db.slots
.insert((V2_ADDR, U256::from(V2_RESERVES_SLOT)), rpc_word);
force_serve_enabled_for_tests(Some(false));
let bot_db = BotStateDb::new(&anchor, db);
let got = bot_db
.storage_ref(V2_ADDR, U256::from(V2_RESERVES_SLOT))
.expect("storage_ref ok");
force_serve_enabled_for_tests(None);
assert_eq!(got, rpc_word, "gate off → RPC fallback value returned");
}
#[test]
fn serve_falls_through_for_untracked_slot() {
let _g = SERVE_TEST_GUARD.lock().unwrap();
let core = v2_bot_state(1_000_000, 2_000_000, 18_012_345);
let anchor = SimAnchorState::snapshot(&core);
let rpc_word = U256::from(0xdeadbeefu64);
let mut db = FixedStorageDb::default();
db.slots.insert((V2_ADDR, U256::from(6u64)), rpc_word);
force_serve_enabled_for_tests(Some(true));
let bot_db = BotStateDb::new(&anchor, db);
let got = bot_db
.storage_ref(V2_ADDR, U256::from(6u64))
.expect("storage_ref ok");
force_serve_enabled_for_tests(None);
assert_eq!(
got, rpc_word,
"untracked slot → RPC fallback (no engine word to serve)"
);
}
#[test]
fn serve_falls_through_for_unregistered_address() {
let _g = SERVE_TEST_GUARD.lock().unwrap();
let core = v2_bot_state(1_000_000, 2_000_000, 18_012_345);
let anchor = SimAnchorState::snapshot(&core);
let unknown = address!("1111111111111111111111111111111111111111");
let rpc_word = U256::from(0xcafeu64);
let mut db = FixedStorageDb::default();
db.slots.insert((unknown, U256::ZERO), rpc_word);
force_serve_enabled_for_tests(Some(true));
let bot_db = BotStateDb::new(&anchor, db);
let got = bot_db
.storage_ref(unknown, U256::ZERO)
.expect("storage_ref ok");
force_serve_enabled_for_tests(None);
assert_eq!(
got, rpc_word,
"non-pool contract → RPC fallback (no engine state to serve)"
);
}
#[test]
fn serve_v2_reserves_zeroes_timestamp_bits() {
let _g = SERVE_TEST_GUARD.lock().unwrap();
let core = v2_bot_state(1_000_000, 2_000_000, 18_012_345);
let anchor = SimAnchorState::snapshot(&core);
let rpc_with_ts =
pack_v2_reserves(999_999, 1_999_999) | (U256::from(0x6543_2101u32) << 224u32);
let mut db = FixedStorageDb::default();
db.slots
.insert((V2_ADDR, U256::from(V2_RESERVES_SLOT)), rpc_with_ts);
force_serve_enabled_for_tests(Some(true));
let bot_db = BotStateDb::new(&anchor, db);
let got = bot_db
.storage_ref(V2_ADDR, U256::from(V2_RESERVES_SLOT))
.expect("storage_ref ok");
force_serve_enabled_for_tests(None);
assert_eq!(
got >> 224u32,
U256::ZERO,
"served word's timestamp bits must be zeroed (engine doesn't track ts)"
);
assert_eq!(
got & U256::from_limbs([u64::MAX, u64::MAX, u64::MAX, 0x0000_0000_ffff_ffff]),
pack_v2_reserves(1_000_000, 2_000_000),
"served reserves (low 224) match the engine"
);
}
}