Skip to content

fix(balancer-v3): require registered pool address, drop zeroAddress fallback - #10

Merged
Hiksang merged 1 commit into
mainfrom
fix/balancer-v3-pool-required
May 6, 2026
Merged

fix(balancer-v3): require registered pool address, drop zeroAddress fallback#10
Hiksang merged 1 commit into
mainfrom
fix/balancer-v3-pool-required

Conversation

@Hiksang

@Hiksang Hiksang commented May 6, 2026

Copy link
Copy Markdown
Collaborator

Summary

BalancerV3Adapter.buildSwap() was encoding a swapSingleTokenExactIn call with pool = 0x0, gated only by a // TODO: resolve pool from registry comment in dex/balancer_v3.ts:55. No chain in chains.toml currently uses interface = "balancer_v3", so this code path is unreachable in production — but if anyone wires up a Balancer V3 entry tomorrow, they'd ship a router call to pool = address(0) that reverts (or worse, silently routes through some default).

This PR closes the footgun: register a pool, or the adapter refuses to build the call.

Changes

dex/balancer_v3.ts

  • Constructor now reads entry.contracts["pool"] and stores it (optional).
  • buildSwap throws DefiError.invalidParam with a clear remediation message when the pool is missing — same error class as the existing amount_out_min guard.
  • When the pool is registered, the calldata uses it verbatim. The description string includes the pool address for log clarity.

dex/balancer_v3.test.ts

  • Test fixture now includes pool in ENTRY.contracts. New ENTRY_NO_POOL fixture mirrors the production "router only" mistake.
  • New test: "buildSwap refuses to ship without a registered pool address" — proves the new guard fires on the missing-pool path.
  • Hardened the existing happy-path test to also assert that the pool address survives ABI encoding (was only checking minAmountOut).

Result: balancer_v3 test file goes from 2 → 3 tests, all passing.

What's NOT in this PR

  • Multi-pool routing / on-chain factory queries. Balancer V3's BatchRouter is the right surface for that — intentionally out of scope.
  • Removing the BalancerV3Adapter export / case "balancer_v3" in factory.ts. Keeping the scaffold dormant so the next consumer can register pool = "0x..." and ship without re-adding the adapter.

Test plan

  • pnpm -C ts -r build — clean.
  • pnpm -C ts -r lint — 3 packages, tsc --noEmit clean.
  • pnpm -C ts -r test — 64/64 (defi-cli) + 39/39 (defi-protocols).
  • Reviewer: confirm Balancer V3 BatchRouter integration is still the right pivot for multi-pool support if/when needed.

🤖 Generated with Claude Code

…allback

Background:
  BalancerV3Adapter.buildSwap() shipped a `swapSingleTokenExactIn`
  call with `zeroAddress` as the pool argument, gated only by a
  `// TODO: resolve pool from registry` comment. The factory wires
  the adapter via `case "balancer_v3"`, but no chain in the registry
  declares `interface = "balancer_v3"`, so the broken path has been
  unreachable in practice — yet the unit test was happily decoding
  calldata that, if anyone wired it up, would have called the V3
  Router with `pool = 0x0` and reverted (or worse, silently routed
  through some default).

Fix:
  - Constructor reads `entry.contracts["pool"]` if present.
  - buildSwap throws DefiError.invalidParam with a clear remediation
    message ("register `pool = "0x..."` under [protocol.contracts]")
    when the pool is missing — same error class as the existing
    amount_out_min guard.
  - When the pool is registered, the calldata uses it verbatim;
    description string echoes the pool for log clarity.

Test changes:
  - Added pool to the existing ENTRY fixture and a parallel
    ENTRY_NO_POOL fixture for the new failure case.
  - New test: "buildSwap refuses to ship without a registered pool
    address".
  - Hardened the existing happy-path test to also assert the pool
    address survives encoding (previously only checked
    minAmountOut).

Out of scope:
  - Multi-pool routing / on-chain factory queries / quoter
    integration — Balancer V3's BatchRouter is the right surface
    for that and is intentionally not pursued here.
  - Removing the BalancerV3 export+factory case. Keeping it dormant
    so the next consumer can register `pool = "0x..."` and ship
    without re-adding the adapter.

Verified:
  - pnpm -C ts -r build  — clean.
  - pnpm -C ts -r lint   — 3 packages clean.
  - pnpm -C ts -r test   — 64/64 (defi-cli) + 39/39 (defi-protocols),
                           balancer_v3.test.ts now 3 tests (was 2).
@Hiksang
Hiksang merged commit e18d28e into main May 6, 2026
4 checks passed
@Hiksang
Hiksang deleted the fix/balancer-v3-pool-required branch May 6, 2026 14:30
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