Mayavi
Incident replays·4 min read

Replaying EIGEN Season 1: Bit-Exact Quoter Validation on Real Swaps

EigenLayer's EIGEN token became transferable on 2024-10-01. We pin block 20,900,000, fork mainnet, execute the same swaps Uniswap V3's Quoter quoted, and assert delta == 0. This post is the full walkthrough.

Part of the Incident replays series.

Post 2 — The Credibility Wall used this replay as a claim. This post is the proof: how we picked the block, what the test actually does, why Quoter bit-exact is a stronger semantic than "within 50 bps," and the half-dozen things we deliberately don't test yet.

The incident

EigenLayer's EIGEN token became transferable at the start of Season 1 unlocks on 2024-10-01. The window immediately after that switch is the first observable EIGEN sell-pressure event on Uniswap V3. It's the canonical incident for our incident-replay gate because:

  1. It's recent enough that the relevant Uniswap V3 pool (EIGEN/WETH 0.30%) has real liquidity at the pinned block.
  2. It's old enough that the on-chain truth at that block is immutable — no follow-up unlock can perturb the test's pinned state.
  3. The asset has a clear pre-and-after structure: before October 1, no swaps possible (transferability disabled). After, every swap is a legitimate market action.

The test pins block 20,900,000 (≈ 2024-10-05, four days post-transferability). The pool has had time to attract liquidity and direct sell-pressure flow.

The strategy: Quoter bit-exact

The classical replay strategy is: pick a named historical transaction, fork at the block before it, execute the same calldata against the forked state, compare results. This works but it has known failure modes:

  • Most retail flow post-2023 routes through Universal Router rather than the legacy SwapRouter our find_swap_in_range scanner walks. For EIGEN specifically, named-tx replay would be a sparse handful of swaps.
  • The tolerance has to be > 0 because mempool ordering between the real tx and the simulated tx can differ.

Quoter bit-exact dodges both problems by replaying Uniswap's own simulation oracle instead of a specific user's transaction:

  1. Fork mainnet at the pinned block.
  2. Static-call Uniswap V3's QuoterV2 for an amountOut.
  3. Fund a synthetic trader (the bypass-during-snapshot path from Post 2), approve the SwapRouter, execute the same swap.
  4. Assert swap_amount_out == quoter_amount_out bytewise.

The Quoter and the SwapRouter share the same pool math — same tick crossing, same fee calculation, same rounding. If our engine produces a different number from the Quoter at the same block, it's a Mayavi bug, full stop. Delta must be exactly zero. No tolerance band.

The test

tests/replay/test_eigen_incident.py:

EIGEN_POST_TRANSFERABILITY_BLOCK = 20_900_000
EIGEN_WETH_FEE = 3000  # 0.30% pool
 
EIGEN_INCIDENT_CHECKS: tuple[BitExactCheck, ...] = (
    BitExactCheck(
        name="WETH->EIGEN 0.5 @ 0.30% pool, post-transferability",
        block_number=EIGEN_POST_TRANSFERABILITY_BLOCK,
        token_in=WETH,
        token_out=EIGEN,
        fee=EIGEN_WETH_FEE,
        amount_in=5 * 10**17,        # 0.5 WETH
        decimals_in=18,
        decimals_out=18,
    ),
    BitExactCheck(
        name="WETH->EIGEN 2 @ 0.30% pool, post-transferability",
        block_number=EIGEN_POST_TRANSFERABILITY_BLOCK,
        token_in=WETH,
        token_out=EIGEN,
        fee=EIGEN_WETH_FEE,
        amount_in=2 * 10**18,        # 2 WETH
        decimals_in=18,
        decimals_out=18,
    ),
)
 
@pytest.mark.fork
@pytest.mark.slow
@pytest.mark.parametrize("check", EIGEN_INCIDENT_CHECKS, ids=lambda c: c.name)
def test_eigen_airdrop_replay_within_tolerance(alchemy_rpc_url, check):
    result = run_check(check, rpc_url=alchemy_rpc_url)
    assert result.error is None, f"check {check.name} errored: {result.error}"
    assert result.bit_exact, (
        f"NOT bit-exact: {check.name}\n"
        f"  quoter_amount_out: {result.quoter_amount_out}\n"
        f"  swap_amount_out:   {result.swap_amount_out}\n"
        f"  delta:             {result.delta} ({result.delta_bps:+.4f} bps)"
    )

run_check lives in mayavi/replay/validator.py. It's a thin coordinator: open a Fork, call QuoterV2's quoteExactInputSingle, deposit the trader's WETH, approve the SwapRouter, execute exactInputSingle, return both numbers + the delta.

The two swap sizes (0.5 WETH and 2 WETH) are deliberately modest — well below the per-tick liquidity ceiling at this block. Bit-exactness is amount-independent (the math is deterministic at any size), but realistic numbers keep the test honest about pool-impact scenarios. A future check at 100 WETH would surface different tick crossings; that's exactly the regime where mocked-AMM simulators silently drift.

What we deliberately don't test

A few things are out of scope today, on purpose:

  1. A specific user's named-tx replay. Post-2023 most retail flow routes through Universal Router. Our find_swap_in_range scanner filters for SwapRouter.exactInputSingle — sparse for EIGEN in the relevant window. A Universal-Router-aware replay scanner is queued for Phase 3.
  2. Sell-side (EIGEN → WETH) swaps. Symmetric to the buy-side; the same Quoter→SwapRouter equivalence holds. Adding two more BitExactCheck entries is a five-line PR. Not in PR L, deliberately.
  3. Other pool tiers (0.05%, 1.00%). The 0.30% pool is the deepest tier for EIGEN/WETH at this block. The 0.05% tier had insufficient liquidity for meaningful swap sizes. The 1.00% tier wasn't deployed yet for this pair.
  4. Cross-block consistency. A second-pinned-block check at, say, block 21,000,000 would catch a class of bugs where state mutations between blocks aren't preserved. Not added yet because the fork-cache integrity gate (tests/evm/test_cache_integrity.py) already covers that for the storage-slot layer.

What a regression here looks like

If a future PR introduces a half-wei rounding error in the swap path — say, a Python truncation in our amountOut decode — this test fires immediately:

NOT bit-exact: WETH->EIGEN 0.5 @ 0.30% pool, post-transferability
  quoter_amount_out: 3897582718445789016
  swap_amount_out:   3897582718445789015
  delta:             -1 (-0.0000 bps)

One wei. That's the kind of thing the bps-tolerance world hides. The Quoter bit-exact world finds it. Credibility doesn't compound — every other claim Mayavi makes is suspect-by-association if this test ever silently regresses.

The credibility ratchet

This test is one of seven in the Phase-5 sim-to-real validation suite, all gated as release-blockers:

  1. Replay validation (this test).
  2. Fork cache integrity (Post 2).
  3. PPO vs baselines under interest accrual (Post 8).
  4. Stablecoin depeg → CDP cascade (Post 7).
  5. Real-launch vesting-cliff replay (Post 12 — next).
  6. Determinism (same seed → byte-identical output).
  7. Gym env contract (gymnasium.utils.env_checker).

Adding the eighth is the easiest kind of contribution: write a new BitExactCheck tuple, pin a block, parametrize the test. The infrastructure carries.

Next in this series: Post 12 — ENA vesting cliff replay, the ledger-style test that lives one shelf below bit-exact and required a five-month-old validation.md TODO to close.