Skip to content

Adapters pipeline: revm swap simulation + Balancer reactive + E2E validation - #13

Merged
KaiCode2 merged 3 commits into
codex/phase-a4-slice2-balancerfrom
codex/e2e-adapter-pipeline
Jun 24, 2026
Merged

Adapters pipeline: revm swap simulation + Balancer reactive + E2E validation#13
KaiCode2 merged 3 commits into
codex/phase-a4-slice2-balancerfrom
codex/e2e-adapter-pipeline

Conversation

@KaiCode2

Copy link
Copy Markdown
Owner

Stacked on #10 (Phase A4 slice 2: Balancer cold-start). Review/merge order: #8#9#10 → this. Base is the slice-2 branch, so the diff is this milestone only.

What

Completes the adapters pipeline — track → react → simulate — for Uniswap V2, Uniswap V3, and Balancer V2, validated offline, against RPC parity, and against a live WebSocket feed.

Swap simulation (src/adapters/sim.rs + AmmAdapter::simulate_swap)

Executes the protocol's canonical quote in revm against the cold-start snapshot (AdapterCache::call_raw) and decodes amount_out — the deployed bytecode does the math, no amm-math/LocalAMM/hand-rolled formulas.

  • V2 → Router02.getAmountsOut · V3 family → QuoterV2.quoteExactInputSingle (fee from metadata) · Balancer → Vault.queryBatchSwap (-assetDeltas[1]).
  • SwapQuote/SimError/SimConfig (mainnet defaults + overrides) + local sol! ABIs; one run_quote maps revert/halt → Reverted.

Balancer V2 reactive (was routing-only)

On a Swap, decode_event now emits RepairAction::VerifySlots over the cold-start-discovered vault balance slots (persisted on BalancerV2Metadata.balance_slots), which the runtime lowers into a resync — keeping cached balances fresh for a subsequent simulate_swap, without reverse-engineering the vault's balance-mapping layout.

Validation

  • Offline (tests/adapter_swap_sim.rs, 8): per-protocol quote vs a mock quoter (no RPC); revert → Reverted; V3 missing-fee → MissingMetadata; Balancer reactive integration (cold-start → Swap refresh → re-simulate reflects the new balance).
  • Chained lifecycle (tests/pipeline_e2e.rs): cold-start → reactive Sync on a shared cache+registry updates the warmed reserves slot.
  • RPC parity (tests/adapter_swap_sim_rpc.rs, #[ignore]): sim == eth_call at a pinned block for all three. ✅ confirmed live.
  • Live WebSocket E2E (tests/reactive_ws_e2e.rs, #[ignore]): cold-start pinned at B0, then only Sync events over a wss:// subscription move reserves; simulate_swap matches on-chain getAmountsOut at the event block and not the pinned-B0 quote — proving event-sourced state, not a refetch. ✅ confirmed live (6 events / 6 min; sim == eth_call to the wei).

Test plan

Full matrix green: fmt; clippy default + adapters-only + --no-default-features (-D warnings); cargo test default + adapters-only + --no-default-features; cargo doc -D warnings. Live/RPC tests are #[ignore] and gated on E2E_RPC_URL (manager-run, not CI).

🤖 Generated with Claude Code

KaiCode2 and others added 3 commits June 24, 2026 13:01
…idation

Completes the adapters pipeline (track -> react -> simulate) for Uniswap V2,
Uniswap V3, and Balancer V2, validated offline, against RPC parity, and against
a live WebSocket feed. Stacks on the Phase A4 slice-2 Balancer cold-start.

Swap simulation (src/adapters/sim.rs + AmmAdapter::simulate_swap): executes the
protocol's canonical quote in revm against the cold-start snapshot via
AdapterCache::call_raw and decodes amount_out — the deployed bytecode does the
math, no amm-math/LocalAMM/hand-rolled formulas. V2 = Router02.getAmountsOut,
V3 family = QuoterV2.quoteExactInputSingle (fee from metadata), Balancer =
Vault.queryBatchSwap (amount_out = -assetDeltas[1]). SwapQuote/SimError/SimConfig
(mainnet quoter/router defaults + overrides) + local sol! quote ABIs; a single
run_quote maps revert/halt -> SimError::Reverted. Quote-target bytecode is lazily
fetched live or installed as a fixture offline.

Balancer V2 reactive (was routing-only): on a Swap, decode_event now emits
RepairAction::VerifySlots over the cold-start-discovered vault balance slots
(persisted on BalancerV2Metadata.balance_slots by finish), which the reactive
runtime lowers into a resync — keeping cached balances fresh for a subsequent
simulate_swap without reverse-engineering the vault's balance-mapping layout.

Tests:
- tests/adapter_swap_sim.rs (8, offline): per-protocol quote vs a mock quoter
  (no RPC), revert -> Reverted, V3 missing-fee -> MissingMetadata, and a Balancer
  reactive integration test (cold-start -> Swap refresh -> re-simulate reflects
  the new balance). New mock fixtures.
- tests/pipeline_e2e.rs (1): chained cold-start -> reactive Sync on a shared
  cache+registry updates the warmed reserves slot (the integration gap).
- tests/adapter_swap_sim_rpc.rs (3, #[ignore], RPC-gated): sim == eth_call at a
  pinned block for V2/V3/Balancer. Confirmed passing against a live archive node.
- tests/reactive_ws_e2e.rs (#[ignore], RPC-gated): LIVE WebSocket E2E — cold-start
  pinned at B0, then ONLY Sync events over a wss subscription move reserves, and
  simulate_swap matches on-chain getAmountsOut at the event block while NOT
  matching the pinned-B0 quote (proves event-sourced state, not a refetch).
  Confirmed passing live (6 events / 6 min; sim == eth_call to the wei). Subscribes
  topic-only (the provider does not push address-filtered subscriptions) and
  routes by the handler. Plus a fast subscription-health probe.

Full matrix green: fmt; clippy default + adapters-only + no-default (-D warnings);
tests default + adapters-only + no-default; cargo doc -D warnings. Live/RPC tests
are #[ignore] and gated on E2E_RPC_URL (run by the manager, not CI).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add offline register -> cold-start -> reactive-event -> simulate_swap regression
tests for Uniswap V2 and V3 in tests/pipeline_e2e.rs, exercising all three
adapter-pipeline legs in one flow (mock quote contracts return a seeded value, so
these pin the chain WIRES end-to-end; state-vs-quote correctness is covered by the
RPC-parity and live-WebSocket tests). Balancer's full chain already lives in
tests/adapter_swap_sim.rs. pipeline_e2e: 1 -> 3 tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The adapters pipeline (cold-start -> reactive -> simulate) is self-contained and
validated, so remove the legacy stack entirely to make the crate adapters-only
and agent-traversable. Net: 34 files / ~14.5k lines deleted.

Deleted (all behind the now-removed `simulation`/`search` features):
- legacy modules: amm_wrapper (LocalAMM), cache_sync/, configured_amms, data,
  discovery, events/, progress, routing/ (search/arbitrage, built on LocalAMM),
  and the pool-math modules balancer_pool, balancer_v3_pool, cryptoswap_math,
  curve_pool, slipstream_pool, solidly_v2_pool, stableswap_math, uniswap_v4_pool,
  plus the inline balancer_math + profit modules in lib.rs.
- legacy examples (event_subscription, triangular_arbitrage, programmatic_loading,
  toml_loading, amms.toml) and the legacy `simulation` bench.

Cargo.toml: removed the `simulation`, `search`, `toml`, `full-protocols`,
`common-protocols` features and the no-adapter protocol sub-features; removed the
now-orphaned deps `amms`, `amm-math`, `rayon`, `toml`, `criterion`,
`tracing-subscriber` (all gone from Cargo.lock). `default` is now
`["adapters", "uniswap-v2", "uniswap-v3", "balancer-v2"]`.

Added examples/adapter_pipeline.rs: a runnable adapters-path demo (register ->
cold-start -> WS event subscribe -> reactive apply -> simulate_swap), modeled on
tests/reactive_ws_e2e.rs; env-gated, no-ops (never panics) if no RPC/WS URL.

Arbitrage search (routing/) is removed with the legacy path; it can be rebuilt on
the adapters `simulate_swap` later. No adapter logic changed. Full matrix green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@KaiCode2
KaiCode2 merged commit 7f303a7 into codex/phase-a4-slice2-balancer Jun 24, 2026
1 check passed
KaiCode2 added a commit that referenced this pull request Jun 24, 2026
Deploy adapters pipeline + hardening to main (PRs #10, #13, #14, #15, #16)
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