Skip to content

Latest commit

 

History

History
88 lines (59 loc) · 6.78 KB

File metadata and controls

88 lines (59 loc) · 6.78 KB

AGENTS.md

This file provides guidance to coding agents working in this repository.

Project Overview

@balancer/sdk — TypeScript SDK for the Balancer Protocol. Published to npm, consumed by the Balancer frontend and integrators. Supports Balancer V2, Balancer V3, and CowAmm pools across many EVM chains (see ChainId in src/utils/constants.ts).

Package manager: pnpm (packageManager pinned in package.json). Node >=18.

Common Commands

pnpm install                   # install deps
pnpm build                     # runs codegen + tsup (ESM+CJS to dist/)
pnpm codegen                   # GraphQL codegen from the Balancer API (required before build)
pnpm lint                      # dpdm (circular deps) + biome check
pnpm format                    # biome format --write
pnpm check-types               # tsc --noEmit
pnpm test                      # vitest in watch mode
pnpm test:all                  # one-shot full suite
pnpm test:no-integration       # skips *.integration.test.ts
pnpm test:no-v2                # skips test/v2/** (V3-only subset)
pnpm update:deployments        # regenerates contract address maps in src/utils from a Balancer deployments source
pnpm example ./examples/swaps/swapV3.ts   # run an example script

Run a single test file: pnpm test -- swap.test.ts (or pass any vitest filter).

Testing model

Tests hit real contracts on Anvil forks of each supported chain, not mocks. Setup lives in test/anvil/anvil-global-setup.ts:

  • Each chain has a pinned forkBlockNumber and a base port (separated by 100 to avoid collisions). When bumping fork blocks (e.g. to include a newly-deployed factory), edit ANVIL_NETWORKS there — there is a precedent commit ce4456f8 for this pattern.
  • startFork() creates a new Anvil instance per call on a unique port (basePort + VITEST_WORKER_ID + forkCounter*100); forks are not reused across tests.
  • RPC URLs come from env vars (ETHEREUM_RPC_URL, POLYGON_RPC_URL, SEPOLIA_RPC_URL, ARBITRUM_ONE_RPC_URL, etc. — see ANVIL_NETWORKS[x].rpcEnv). If unset, a public Tenderly/DRPC fallback is used with a warning.
  • vitest.config.mts caps maxForks: 3 — don't raise this casually, each fork is a full Anvil process making RPC calls.
  • SKIP_GLOBAL_SETUP=true tells the harness you'll run Anvil yourself (the fork config logs the command to use).
  • Foundry/Anvil must be installed locally (curl -L https://foundry.paradigm.xyz | bash && foundryup).

CI runs test:no-integration — integration tests (*.integration.test.ts) are excluded from PR CI.

Architecture

Protocol-version dispatch

The central shape is PoolState (src/entities/types.ts) with protocolVersion: 1 | 2 | 3 where 1 = CowAmm, 2 = Balancer V2, 3 = Balancer V3. Every top-level entity (AddLiquidity, RemoveLiquidity, Swap, CreatePool, InitPool, boosted/nested variants, etc.) is a thin dispatcher that switches on protocolVersion and delegates to a *V2 / *V3 / *CowAmm implementation. When adding a feature, mirror this layout rather than branching inside a single class.

Entity surface

Each user-facing entity exposes the same two-step flow:

  1. query(input, poolState, block?) — runs an on-chain query*/simulation against the V2 Queries contract or V3 Router, returning expected amounts. This is the price-impact-accurate path.
  2. buildCall(input) — returns { to, callData, value } ready to send. V3 often requires a userData field; V2 requires sender/recipient. The base dispatcher enforces this with missingParameterError / exceedingParameterError.

V3 also exposes buildCallWithPermit2(input, permit2) (via Permit2 in src/entities/permit2Helper) and remove-liquidity has buildCallWithPermit(input, permit) (EIP-2612). Both guard on protocolVersion === 3.

See src/entities/addLiquidity/index.ts, src/entities/removeLiquidity/index.ts, src/entities/swap/index.ts for the canonical dispatcher pattern.

Input validation

src/entities/inputValidator/inputValidator.ts maps PoolType → a InputValidatorBase subclass (Weighted / Stable / ComposableStable / Gyro / CowAmm / ReClamm / LiquidityBootstrapping / LiquidityBootstrappingFixedPrice). The top-level entity calls validate* before dispatching to a V2/V3 impl. Unknown pool types fall back to the base validator with a console.warn.

Data providers

src/data/providers/:

  • balancer-api/ — GraphQL client for https://api-v3.balancer.fi (modules: pool-state, nested-pool-state, boosted-pool-state, buffer-state, sorSwapPaths). Generated types at src/data/providers/balancer-api/generated/types.ts — regenerated by pnpm codegen from codegen.yml; do not hand-edit.
  • onchain/ — read pool state directly from chain via multicall when the API isn't used.
  • initPoolDataProvider.ts — bootstraps PoolState immediately after pool creation (before the API has indexed it).

ABIs and contract addresses

  • src/abi/v2/ and src/abi/v3/ hold Viem-compatible ABIs. V3 has many more routers (router, batchRouter, bufferRouter, compositeLiquidityRouter, unbalancedAddViaSwapRouter, lBPMigrationRouter) and pool factories.
  • Per-chain contract address maps: src/utils/balancerV2Contracts.ts and src/utils/balancerV3Contracts.ts. Constants/chain metadata in src/utils/constants.ts (includes ChainId, API_CHAIN_NAMES, CHAINS, PERMIT2, NATIVE_ASSETS).
  • To add or update deployments, run pnpm update:deployments (see scripts/updateDeployments.ts) rather than editing the address maps by hand.

Build

  • tsup.config.ts produces ESM + CJS + .d.ts from src/index.ts, bundled, with sourcemaps. __PACKAGE_VERSION__ is replaced at build time with the package.json version.
  • TS path alias: @/*src/* (both in tsconfig.json and wired through vite-tsconfig-paths for tests).
  • Public API is re-exported from src/index.tsabi, data, entities, utils, types.

Conventions

  • Biome (not ESLint/Prettier) is the formatter and linter. Config in biome.json: single quotes, trailing commas, 4-space indent, 80-col line width, semicolons. Generated files under src/data/providers/balancer-api/generated/ are ignored.
  • dpdm enforces no circular deps (pnpm lint fails on them).
  • Errors go through typed factories in src/utils/errors.ts (SDKError, protocolVersionError, missingParameterError, exceedingParameterError, inputValidationError) — use these instead of raw throw new Error.
  • Releases use changesets. If your change touches src/**, add one — see .agents/skills/changeset/SKILL.md for the file format and bump-selection rules (write the file directly; pnpm changeset is interactive and agents can't drive it). The release workflow on main cuts version-bump PRs automatically; see .github/workflows/release.yml.