Expand description
Cross-block warm cache for immutable/long-TTL account data (bytecode +
account existence) — the persistent layer underneath the per-block
CacheDB. Caches basic_ref + code_by_hash_ref with a per-entry TTL;
forwards storage_ref + block_hash_ref untouched.
Cross-block warm cache for immutable/long-TTL account data — bytecode +
account existence (the basic_ref + code_by_hash_ref rows). Sits
underneath the per-block CacheDB in the BlockSimHandle DB stack so the
contract code-load RPCs stop repeating every block while the per-block
CacheDB (overrides + mutable storage, fresh each block, drops at EOB) is
preserved.
§What this caches (the staleness line)
Only the immutable rows:
basic_ref—AccountInfoFOR CONTRACTS ONLY. An EOA’s nonce/balance mutate every block, so EOA rows (empty code hash) are never cached — they fall through to the per-blockCacheDB+ RPC path fresh at each block build. (A cached contract’s AccountInfo byte-RARELY mutates: contract nonces advance only viaCREATE*; a stale contract nonce does not gate frame-replay validation the way a sender nonce does, and the TTL refresh bounds that edge.)code_by_hash_ref—Bytecodekeyed bycode_hash(immutable by hash).
storage_ref + block_hash_ref are forwarded UNCHECKED to the inner Db
(the mutable-per-block row — V2 reserves slot 8 / V3 slot0+ticks change
every block there’s a swap; the chain block hash is per-chain, not
per-block-snapshot). Caching these would re-introduce the K-invariant /
LOK stale-state-divergence reverts the option-B path hit.
§The TTL (a safety net, not the primary correctness mechanism)
Each entry records the block it was loaded at; an entry read at block B
is a miss when B - loaded_block > WARM_CODE_CACHE_TTL_BLOCKS. A
re-fetch on miss re-caches with loaded_block = B.
In the bot’s domain every cached contract is immutable (EIP-1967 proxies
upgrade via the implementation storage slot — uncached; the injected
executor is per-block via stateOverrides, never read here). The TTL is a
belt-and-suspenders net against:
- EIP-6780-disabled selfdestruct edge cases (pre-Cancun metamorphic
CREATE2-redeploy-to-same-address: a contract that selfdestructs then redeploys with different bytecode at the same address would be stale). EIP-6780 (Cancun, mainnet since March 2024) disables code-deletion on selfdestruct unless the contract was created in the same tx — so the vector is effectively closed for any persistent contract. - Future protocol changes to code mutability.
§Composition
CacheDB (per-block: overrides + mutable storage, fresh each block, drops at EOB)
→ WarmCodeCache (persistent: bytecode + account-existence, per-entry TTL'd)
↳ holds Arc<RwLock<WarmCodeCacheInner>> cloned from the engine owner
↳ the WarmCodeCache<Db> wrapper value itself is per-block
→ BotStateDb (forwarding storage fallback)
→ WrapDatabaseAsync<AlloyDB> (RPC cold-miss)§Ownership
The wrapper value (WarmCodeCache<Db>) is per-block — constructed in
BlockSimHandle::build, holds the per-block Db, drops at end of block.
Only the inner map (Arc<RwLock<WarmCodeCacheInner>>) persists across
blocks — cloned from the engine owner (PyArbitrageEngine / the
standalone Rust Bot). This preserves the per-block CacheDB discipline
while sharing the bytecode cache across blocks.
Structs§
- Warm
Code Cache - A cross-block warm
DatabaseRefwrapper — cachesbasic_ref+code_by_hash_ref(the immutable rows) with a per-entry TTL, forwardsstorage_ref+block_hash_refto the innerDb(the mutable-per-block row, never cached). - Warm
Code Cache Inner - The cross-block persistent inner state — bytecode + account-existence maps, each entry tagged with the block it was loaded at for per-entry TTL expiry.
Constants§
- WARM_
CODE_ CACHE_ TTL_ BLOCKS - The default per-entry TTL in blocks — ≈ 1 day of mainnet blocks (12s block time → ~7200 blocks/hour → ~10 000 ≈ 1.4 days).