Skip to content
Writing

Cheap Parallel EVM Simulation for Searchers

6 min readKai Aldag

Three Rust crates that give MEV searchers a small runtime — a forked-EVM state cache, reactive oracle adapters, and AMM state — to run thousands of parallel liquidation simulations against real protocol bytecode without RPC round-trips.


MEV searchers spend most of their time asking a simple question in many slightly different ways: if this state transition happens, which response is profitable?

The hard part is not a single EVM call. The hard part is running thousands of stateful simulations while the world is moving underneath you. You need fresh oracle prices, token balances, AMM reserves, concentrated-liquidity ticks, protocol state, and enough execution context to evaluate competing bundles. You also need to do it cheaply enough that the search loop can afford to be wrong many times before it is right once.

I built three Rust crates around that problem:

  • evm-fork-cache: a forked EVM state cache for fast local simulation.
  • evm-oracle-state: reactive oracle adapters for turning price-feed events into local cache updates.
  • evm-amm-state: AMM adapters for warming and quoting common venue types.

Together, they form a small searcher runtime: keep the subset of chain state you care about warm, update it reactively from events, and run many local EVM simulations in parallel without round-tripping to RPC in the hot loop.

The Core: evm-fork-cache

evm-fork-cache is the base layer. It wraps a forked EVM database with local state injection, snapshots, overlays, bundle execution, and targeted purging.

The design goal is to make the expensive part explicit. RPC is used during warmup or reconciliation. Once the relevant state is warm, the search loop takes a snapshot and fans out isolated overlays. Each worker can simulate a different bundle against the same post-event state without refetching the world or serializing on one mutable database.

That matters for searchers because most useful strategy evaluation is not one call. It is a matrix:

  • Which debt asset should I repay?
  • Which collateral should I seize?
  • How much should I cover?
  • Should I route through a V2 pool, a V3 pool, Curve, or a multi-hop path?
  • What happens after gas and protocol edge cases?

The crate is built to make that matrix cheap to evaluate.

Reactive Oracles: evm-oracle-state

Oracle updates are often the trigger for an opportunity. A price feed moves, a position crosses a health-factor boundary, and liquidation logic becomes live.

evm-oracle-state provides the bridge from on-chain oracle events into local EVM state. It registers feeds, decodes relevant transmissions, computes the storage updates implied by the event, and applies those updates to evm-fork-cache.

The important point is that this is not a mock price table next to the EVM. The cache state itself is updated, so downstream protocol calls observe the same storage they would observe on-chain. Aave’s getUserAccountData, liquidation validation, and execution all run against the changed local state.

The demo also shows why extension points matter. The historical Aave fixture uses a WETH price source wrapped by a DualAggregator contract. It emits OCR2-shaped events, but its storage layout differs from the standard Chainlink OCR2 layout. The demo plugs in a tiny custom storage adapter for that wrapper rather than special-casing the core cache.

AMM State: evm-amm-state

Liquidating is only half of the search. The seized collateral still needs to be unwound.

evm-amm-state supplies protocol adapters for warming and quoting AMM venues. In this showcase, the same seized WETH amount is quoted through:

  • Uniswap V2 WETH/USDT
  • Uniswap V3 0.05% WETH/USDT
  • Curve Tricrypto2

The point is not that these three venues are the only routes a real searcher would evaluate. The point is that they have different state shapes and pricing models: constant product reserves, concentrated liquidity, and Curve crypto-pool math. Running them side by side demonstrates that the cache can support realistic DeFi heterogeneity.

The Showcase: A Historical Aave V3 Liquidation

The demo repository is evm-liquidation-showcase. It uses a historical Ethereum mainnet Aave V3 liquidation:

  • User: 0x6DAAfC55ad39970d5EBeA808DBA2e8A598945632
  • Pre-trigger block: 25232834
  • Oracle trigger block: 25232835
  • Liquidation block: 25232836
  • Reference transaction: 0xf19a03d68c9ffabb06f8e7b30db32e5aeefb5b235be0bdea6429050324142153

At the pre-trigger block, Aave reports the account with health factor 1.004628. After the oracle movement, the account is below water at 0.998193.

The demo then:

  1. Warms the Aave account, oracle feeds, and AMM venues.
  2. Replays the WETH oracle transmission through the reactive oracle runtime.
  3. Reconciles the companion AAVE collateral source for the same trigger block.
  4. Confirms the account is liquidatable.
  5. Funds a synthetic searcher inside the local cache.
  6. Runs liquidation candidates in parallel from one post-oracle snapshot.
  7. Quotes the seized WETH through three AMM venue types.
  8. Picks the best expected PnL.

The successful run currently selects the actual liquidation size routed through Uniswap V3:

candidate=actual route=Uniswap V3 seized=1.077825 WETH -> 2010.444319 USDT
repay=1925.651145 USDT, gas=2.572193 USDT, expected PnL=+82.220981

One candidate intentionally reverts:

candidate 50% reverted: MustNotLeaveDust()

That is useful. It shows the engine is not just applying spreadsheet math around Aave. It is actually executing protocol validation. Invalid candidates can fail inside Aave while other candidates continue and compete on route quality.

Why This Is Interesting

The technical bet behind these crates is that a searcher should not have to choose between correctness and throughput.

Pure off-chain math is fast, but it tends to drift from protocol reality. Raw RPC simulation is accurate, but too slow and too expensive for wide search. This project sits in the middle: keep a local EVM cache close enough to chain state, react to the events that matter, and use parallel snapshots to explore many possible actions cheaply.

For DeFi search, that shape is powerful:

  • Oracle events can update local protocol state immediately.
  • AMM events can mark pools stale or update reserves locally.
  • Candidate bundles can execute against real protocol bytecode.
  • Worker threads can evaluate competing paths without shared mutable EVM state.
  • The final ranking can include gas and route-specific unwind pricing.

This is the kind of infrastructure I want in a serious trading system: narrow enough to be fast, faithful enough to catch protocol edge cases, and modular enough to extend as new venues and triggers matter.

What Comes Next

The natural next step is expanding adapters and making the warmup/reconciliation surface more declarative:

  • More oracle wrapper layouts.
  • More AMM variants and multi-hop route generation.
  • Event-driven AMM reserve updates, not only cold-started quotes.
  • Better reusable fixtures for historical opportunity replay.
  • Strategy-level harnesses that compare candidate bundles across collateral, debt asset, route, and gas assumptions.

The initial release is intentionally small, but the architecture is already pointing at the full searcher loop: reactive state ingestion, cheap parallel EVM simulation, and practical DeFi execution analysis.

Written by

Kai Aldag

I build and write about crypto, cryptography, and distributed systems. Egg Tech is my one-person company — the home for what I ship and the notes I keep along the way.

Related writing