Skip to content

balancer/exit-ui

Repository files navigation

Balancer Deprecated-Chain Exit UI

Standalone static website for withdrawing Balancer positions from deprecated chains. Currently enabled: Mode, Fraxtal, Polygon zkEVM. Supports Balancer v2 and v3.

  • Connect an injected wallet (MetaMask, Rabby, ...) or watch any address read-only
  • Optionally configure a custom RPC URL per chain (persisted in localStorage)
  • Scan for pool (BPT) and gauge (staked) positions via Multicall3 against a bundled pool list
  • Unstake from gauges (withdraw(amount, claim_rewards=true)) and claim rewards
  • Proportionally exit v2 pools (Vault.exitPool) and v3 pools (Router.removeLiquidityProportional)
  • Exits are simulated first; minimum amounts = expected − slippage (default 1%). An opt-in emergency mode submits with zero minimums.
  • Recovery-mode pools (incl. paused and linear pools) exit via the recovery path automatically; expected amounts are computed client-side (cashBalance × bptIn / virtualSupply) because BalancerQueries.queryExit reverts on paused pools while the real recovery exit succeeds.

The site is fully static: no backend, no subgraph — it only talks to the RPC you configure.

Run locally

npm install
npm run dev      # http://localhost:3001
npm run build    # static output in dist/

Dependencies: react, react-dom, viem. That's all.

How discovery works

Positions are scanned against per-chain pool/gauge lists committed in src/config/data/<chain>.json. These lists are generated once (chains are deprecated; the lists don't change) by:

npm run discover -- --chain mode [--rpc <url>] [--to-block <n>]

The script enumerates:

  • v2 pools: PoolCreated events from every pool factory (from src/config/registry.json)
  • v3 pools: PoolRegistered events from the v3 Vault (covers all v3 factories)
  • gauges: GaugeCreated events from the child-chain gauge factories

and enriches everything (poolId, tokens, symbols, decimals, phantom-BPT detection) via Multicall3. eth_getLogs is chunked adaptively and respects strict public RPCs (zkEVM caps at 1k blocks).

Pools missing from a list can always be added in the UI by pasting the pool address.

Chain registry

src/config/registry.json holds config for all Balancer chains (v2 vault + pool factories + gauge factories + start blocks, v3 vault + router, BalancerQueries, Multicall3, default RPC). It is machine-generated from sibling repos — never hand-edit it; regenerate instead:

node tools/extract-registry.mjs --repos <dir containing the 4 repos below>

Sources: balancer-subgraph-v2/networks.json, balancer-subgraph-v3/networks.json, gauges-subgraph/subgraph.<chain>.yaml, backend/config/<chain>.ts.

Deprecating another chain

  1. Regenerate the registry if factory lists changed: node tools/extract-registry.mjs
  2. Set "deprecated": true for the chain in src/config/registry.json (or mark it in tools/extract-registry.mjs and regenerate)
  3. Run discovery: npm run discover -- --chain <key>
  4. Smoke test the exits: node tools/smoke-test.mjs --chain <key>
  5. Build and deploy

Chains that are not deprecated are hidden in the UI but visible in dev mode (npm run dev or append ?dev=1 to the URL). Base is included as a v3 test chain.

Verification tools

node tools/smoke-test.mjs --chain mode

Headless test against the live chain (no funds needed): uses each gauge contract as a pseudo-user (gauges hold the staked BPT), simulates the exact exit calls the UI would send, and for recovery-mode pools verifies the client-side math by simulating the real exitPool with 0.5%-tight minimums.

For end-to-end testing with real transactions, fork a chain with anvil --fork-url <rpc>, set the UI's RPC to http://localhost:8545, and impersonate a holder.

Notes / limitations

  • Exits always pay out wrapped native tokens (no auto-unwrap), matching vault registration.
  • No USD pricing — deprecated chains have no reliable price source; amounts are token quantities.
  • Linear pools (zkEVM) can only exit while in recovery mode (they all are, post-deprecation); the UI blocks them otherwise instead of implementing batch swaps.
  • Nested/boosted pools (e.g. bb-o-USD on zkEVM) pay out the inner BPTs (linear pool tokens). Rescan after exiting — the inner BPTs show up as new positions and exit via the recovery path.
  • v3 exits require a one-time BPT approval to the v3 Router (plain ERC20 approve, no permit2).
  • Child-chain BAL rewards are distributed as regular gauge reward tokens and are included in claim_rewards.

Releases

No releases published

Packages

 
 
 

Contributors