Cheap Parallel EVM Simulation for Searchers

# Cheap Parallel EVM Simulation for Searchers

> 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.

- Canonical URL: https://blog.eggtech.io/posts/parallel-evm-simulation
- Markdown URL: https://blog.eggtech.io/posts/parallel-evm-simulation/index.md
- Published: 2026-06-30
- Updated: 2026-06-30
- Author: Kai Aldag
- Reading time: 6 min read
- Tags: mev, rust, crypto

---

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:

```text
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:

```text
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.