Skip to content

fix(mempool): make backrun arbs executable + edge quarantine + multi-venue pools#186

Open
Pablosinyores wants to merge 4 commits into
developfrom
fix/mempool-arb-reality
Open

fix(mempool): make backrun arbs executable + edge quarantine + multi-venue pools#186
Pablosinyores wants to merge 4 commits into
developfrom
fix/mempool-arb-reality

Conversation

@Pablosinyores

Copy link
Copy Markdown
Owner

Summary

Makes the live mempool-backrun path actually produce executable, profitable arbs, fixes the discrepancy/staleness sources that suppressed real candidates, and adds the observability + venue coverage to see and act on them. Findings came from an adversarial audit (file:line proof, skeptic-verified); each confirmed defect is fixed with tests.

Commits

  • feat(metrics): 3 funnel metrics + dashboard §⑥ — aether_mempool_backrun_profit_ratio (revm-actual ÷ optimizer-expected net; the discrepancy gauge, ~1.0 = consistent), …_calldata_built_total{protocol}, …_post_sim_gate_bypassed_total.
  • fix(mempool): the core fixes —
    • Empty SwapStep.calldata (dominant "publishes 0" cause): the executor runs pool.call(step.data) verbatim, so empty data reverted (SwapFailed) and every sim was a false negative. Now builds V2/V3/Sushi inner calldata after sizing; drops Curve/Balancer/Bancor hops (no builder yet) with reason unsupported_protocol_calldata instead of publishing a reverting bundle.
    • Tip accounting: simulate with tipBps=0 so the recipient delta is the full profit (was the ~10% left after a 90% coinbase tip); publish the real-tip calldata separately; net = gross − gas.
    • gate_post_sim units: subtract sim gas from revm gross so both sides are net (was net-vs-gross → frac_diff≈0.9 → dropped marginal arbs as revm_contradicts).
    • Routing: select steps on the post-victim graph the cycle was scored on.
    • Replay fork: pin the detection-time post-state replay to the snapshot block (gated on AETHER_BACKRUN_FORK_LATEST), matching the validating sim.
  • feat(state): quarantine_stale_edges — never-updated placeholder edges (zero reserves, the rate=1.0 phantom-cycle source) are filtered out of Bellman-Ford. Raw edge age is an opt-in, off-by-default backstop (AETHER_EDGE_MAX_AGE_BLOCKS): for an AMM a quiet pool's reserves stay exact between trades, so age alone isn't a corruption signal and must not drop valid quiet pools.
  • feat(pools): +7 UniV2/V3 venues, all verified on-chain (getCode + token0/token1 + fee), so high-traction pairs have ≥2 executable venues — notably USDC/USDT gains a cross-AMM venue (was UniV3 tiers only).

Verification

  • cargo build / clippy (grpc-server + state): clean, zero warnings.
  • Tests: aether-state 64 pass (incl. 4 new quarantine tests); aether-grpc-server 78 pass (incl. 3 new calldata tests). The one failing test (provider::test_provider_run_with_shutdown) is a pre-existing timing flake that fails identically on the base branch.
  • New pool addresses verified against mainnet via cast at block ~25.24M; pools.toml parses (135 pools, no dup addresses).

Known follow-ups (out of scope here)

  • The block-driven path shares the same empty-calldata + net-vs-gross gate bug (separate fix).
  • Curve/Balancer/Bancor have no inner-calldata builder yet, so those hops are dropped (observable via unsupported_protocol_calldata) rather than backrun.

Notes

Rebased onto develop (was developed against an older base); merges cleanly. AETHER_EDGE_MAX_AGE_BLOCKS defaults to 0 (placeholder-only quarantine); set >0 only as a missed-event backstop.

Add three mempool-backrun observability metrics and a funnel dashboard
section (⑥) so detection-vs-simulation discrepancy is measurable:

- aether_mempool_backrun_profit_ratio (histogram): revm-actual net /
  optimizer-expected net; clusters at ~1.0 when detection-state matches
  sim-state, a fat left tail signals a stale-snapshot discrepancy.
- aether_mempool_post_sim_gate_bypassed_total: backruns published with no
  detector-vs-revm cross-check (optimizer fell back, expected_net == 0).
- aether_mempool_backrun_calldata_built_total{protocol}: proves the
  executor receives non-empty step.data per hop.
The backrun path left every SwapStep.calldata empty, so the executor's
pool.call(step.data) reverted (SwapFailed) and every sim was a false
negative. Build the protocol-specific inner swap calldata (V2/V3/Sushi)
after sizing; drop hops whose protocol has no builder (Curve/Balancer/
Bancor) with a distinct reject reason instead of publishing a reverting
bundle.

Also correct the profit accounting that suppressed real arbs:
- simulate with tipBps=0 so the recipient delta is the FULL profit, not
  the ~10% left after a 90% coinbase tip; publish the real-tip calldata
  separately and net = gross - gas.
- align gate_post_sim units (subtract sim gas from revm gross so both
  sides are net) to stop dropping marginal arbs as revm_contradicts.
- route step selection on the post-victim graph the cycle was scored on.
- pin the detection-time post-state replay fork to the snapshot block
  (gated on AETHER_BACKRUN_FORK_LATEST), matching the validating sim.
Add PriceEdge.last_update_block and PriceGraph::quarantine_stale_edges,
wired into the engine detection cycle (and reserve-update batch). Filtered
edges are skipped by Bellman-Ford, killing the placeholder rate=1.0
phantom-cycle source before detection.

Placeholder edges (never updated, zero reserves) are always quarantined.
Raw edge age is an opt-in, off-by-default backstop (AETHER_EDGE_MAX_AGE_
BLOCKS): for an AMM a quiet pool's reserves stay exact between trades, so
age alone is not a corruption signal and must not drop valid quiet pools.
Quarantine is additive and self-heals on the next reserve update.
Add 7 UniswapV2/V3 pools (all verified on-chain: getCode + token0/token1
+ fee) so the most-traded pairs have >=2 executable venues for cross-DEX
cycles. USDC/USDT gains a cross-AMM venue (was UniV3 tiers only); WETH/USDT,
DAI/USDC, WBTC/WETH, wstETH/WETH gain a tier; DAI/USDT added on two venues.
Only V2/V3/Sushi venues are added, since the backrun path can only build
calldata for those today.
@vercel

vercel Bot commented Jun 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
aether Ready Ready Preview, Comment Jun 3, 2026 9:31pm
aether-63xv Ready Ready Preview, Comment Jun 3, 2026 9:31pm

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant