From 09083987eead0018618af8a46a97041a2563f141 Mon Sep 17 00:00:00 2001 From: Aryan Godara <65490434+AryanGodara@users.noreply.github.com> Date: Mon, 29 Jun 2026 16:58:57 +0530 Subject: [PATCH] Add Balancer V2 indexer DB migrations --- database/sql-balancer-indexer/README.md | 77 +++++++++++++++++++ .../V001__balancer_v2.sql | 40 ++++++++++ 2 files changed, 117 insertions(+) create mode 100644 database/sql-balancer-indexer/README.md create mode 100644 database/sql-balancer-indexer/V001__balancer_v2.sql diff --git a/database/sql-balancer-indexer/README.md b/database/sql-balancer-indexer/README.md new file mode 100644 index 0000000000..cedc7417ca --- /dev/null +++ b/database/sql-balancer-indexer/README.md @@ -0,0 +1,77 @@ +# Balancer-indexer migrations + +Flyway migrations for the Balancer V2 indexer's own per-network database (e.g. +`mainnet_balancer_indexer`), kept out of the shared `../sql/` set so they don't +run against the autopilot/orderbook main DBs. The local/e2e DB is +`balancer_indexer`. + +The migration image ships every dir; init containers pick one via `-locations`: + +| DB | location | +|---------------------|-------------------------------------------------------| +| autopilot/orderbook | `/flyway/sql` (default) | +| pool-indexer | `-locations=filesystem:/flyway/sql-pool-indexer` | +| balancer-indexer | `-locations=filesystem:/flyway/sql-balancer-indexer` | + +New Balancer-indexer migrations go here, never in `../sql/`. Unlike the +pool-indexer's `V110` (duplicated from `../sql/` and cancelled there by +`../sql/V111`), these tables are new to this directory, so there's no +shared-set copy to cancel. + +## Schema + +The tables below live in the indexer's own per-network database, created by the +migrations in this directory. The indexer stores *discovery* metadata only; +dynamic state (balances, amplification, LBP weights, scaling factors, swap fee, +paused) stays on-chain and is fetched by the driver at query time. + +### balancer\_v2\_checkpoints + +Highest finalized block processed per `factory_address` by the Balancer +indexer. The indexer runs one process per network against its own DB, so +there's no `chain_id` column. + + Column | Type | Nullable | Details +-------------------|--------|----------|-------- + factory\_address | bytea | not null | Factory address (20 bytes) + block\_number | bigint | not null | + +Indexes: +- PRIMARY KEY: btree (`factory_address`) + +### balancer\_v2\_pools + +One row per registered pool, discovered from each factory's `PoolCreated` +event. `pool_type` is derived from which factory created the pool (the +factory→type map comes from config — no on-chain classification call). The +weighted V0 vs V3-plus distinction is recovered driver-side from `factory`, so +it isn't a separate `pool_type`. + + Column | Type | Nullable | Details +-----------------|--------|----------|-------- + pool\_id | bytea | not null | 32-byte Balancer poolId + address | bytea | not null | Pool address (poolId's first 20 bytes) + factory | bytea | not null | Address of the factory that emitted `PoolCreated` + pool\_type | text | not null | `Weighted` \| `Stable` \| `ComposableStable` \| `LiquidityBootstrapping`. `CHECK`ed; stored as the string the API serves. + created\_block | bigint | not null | Block in which the pool was created on-chain + +Indexes: +- PRIMARY KEY: btree (`pool_id`) + +### balancer\_v2\_pool\_tokens + +Tokens per pool, in `Vault.getPoolTokens` order (`position`). `decimals` is +nullable and filled in by the backfill task. `weight` is the Balancer Bfp +(1e18 fixed-point) normalized weight, set only for weighted pools. + + Column | Type | Nullable | Details +------------|----------|----------|-------- + pool\_id | bytea | not null | FK → `balancer_v2_pools(pool_id)` + position | int | not null | Token index within the pool (`getPoolTokens` order) + token | bytea | not null | Token address (20 bytes) + decimals | smallint | nullable | `NULL` = not yet fetched. `-1` = sentinel for "fetched but call failed" + weight | numeric | nullable | Bfp (1e18) normalized weight; `NULL` for stable/composable-stable/LBP + +Indexes: +- PRIMARY KEY: btree (`pool_id`, `position`) +- Partial index on `(token)` with predicate `decimals IS NULL` to power the backfill scan. diff --git a/database/sql-balancer-indexer/V001__balancer_v2.sql b/database/sql-balancer-indexer/V001__balancer_v2.sql new file mode 100644 index 0000000000..79b7983a01 --- /dev/null +++ b/database/sql-balancer-indexer/V001__balancer_v2.sql @@ -0,0 +1,40 @@ +-- Tracks the highest finalized block fully processed per factory contract. +-- A DB instance hosts a single network, so no `chain_id` column is needed. +CREATE TABLE balancer_v2_checkpoints ( + factory_address BYTEA NOT NULL, -- factory address + block_number BIGINT NOT NULL, + PRIMARY KEY (factory_address) +); + +-- One row per registered pool, discovered from each factory's `PoolCreated` +-- event. `pool_type` is derived from which factory created the pool (no +-- on-chain classification); the weighted V0/V3-plus distinction is recovered +-- from `factory`, so it isn't a separate type. `pool_type` is stored as the +-- string the API serves so there's no int<->enum mapping at the boundary. +CREATE TABLE balancer_v2_pools ( + pool_id BYTEA NOT NULL, -- 32-byte Balancer poolId + address BYTEA NOT NULL, -- pool address (poolId's first 20 bytes) + factory BYTEA NOT NULL, + pool_type TEXT NOT NULL CHECK (pool_type IN ('Weighted', 'Stable', 'ComposableStable', 'LiquidityBootstrapping')), + created_block BIGINT NOT NULL, + PRIMARY KEY (pool_id) +); + +-- Tokens per pool, in `Vault.getPoolTokens` order (`position`). `decimals` is +-- nullable and filled in by the backfill task. `weight` is the Balancer Bfp +-- (1e18 fixed-point) normalized weight, set only for weighted pools; NULL for +-- stable/composable-stable/LBP (their weights are absent or fetched on-chain). +CREATE TABLE balancer_v2_pool_tokens ( + pool_id BYTEA NOT NULL, + position INT NOT NULL, + token BYTEA NOT NULL, + decimals SMALLINT, -- NULL = not yet fetched; -1 = fetched but call failed + weight NUMERIC, -- Bfp (1e18); weighted pools only, else NULL + PRIMARY KEY (pool_id, position), + FOREIGN KEY (pool_id) REFERENCES balancer_v2_pools(pool_id) +); + +-- Decimals backfill hot path. Partial on `IS NULL` so the index shrinks to +-- near-empty once most rows are populated (real value or the `-1` "tried, +-- failed" sentinel). +CREATE INDEX ON balancer_v2_pool_tokens (token) WHERE decimals IS NULL;