AgentSkillsCN

Balancer V3 Architecture

当用户询问“Balancer V3 架构”、“Vault 设计”、“瞬时会计”、“解锁模式”、“信用/债务体系”、“Balancer V3 概览”,或希望对 Balancer V3 的运行机制获得高层次的理解时,此技能便能派上用场。

SKILL.md
--- frontmatter
name: Balancer V3 Architecture
description: This skill should be used when the user asks about "Balancer V3 architecture", "Vault design", "transient accounting", "unlock pattern", "credit/debt system", "Balancer V3 overview", or needs high-level understanding of how Balancer V3 works.
version: 0.1.0

Balancer V3 Architecture Overview

Balancer V3 is a complete rewrite of the Balancer protocol, featuring a singleton Vault design with transient accounting for gas-efficient operations.

Core Architecture

Singleton Vault Pattern

All liquidity in Balancer V3 is managed by a single Vault contract:

code
┌─────────────────────────────────────────────────────────────┐
│                         VAULT                               │
│  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐           │
│  │   Pool A    │ │   Pool B    │ │   Pool C    │           │
│  │  (Weighted) │ │   (Stable)  │ │   (Custom)  │           │
│  └─────────────┘ └─────────────┘ └─────────────┘           │
│                                                              │
│  Token Reserves: All ERC20 balances held centrally          │
│  Pool Accounting: Per-pool balance tracking                 │
└─────────────────────────────────────────────────────────────┘

Contract Hierarchy

ContractPurpose
VaultMain entry point. Core swap/liquidity operations. Inherits VaultCommon + Proxy pattern.
VaultExtensionLess frequently used functions (pool registration, queries). Delegate-called via Proxy.
VaultAdminPermissioned administrative functions.
RouterUser-facing entry point. Handles ETH, Permit2, unlocking.

Key Design Principles

  1. Transient Accounting: Tokens aren't transferred during operation - only deltas tracked
  2. Unlock Pattern: All operations happen within an unlock() callback
  3. Pools as External Contracts: Pools contain only math logic, no token balances
  4. Hooks: Extensibility via optional hook contracts per pool

Transient Accounting Model

Instead of transferring tokens on each swap, V3 tracks credits and debts:

solidity
// During an unlock session:
// 1. Operations create credits (tokens owed TO user) and debts (tokens owed BY user)
// 2. At session end, all deltas must net to zero
// 3. Only then do actual token transfers occur

function unlock(bytes calldata data) external transient returns (bytes memory result) {
    // Callback to msg.sender with data
    return (msg.sender).functionCall(data);
}

// The transient modifier ensures:
// - Sets isUnlocked = true
// - After callback, checks nonZeroDeltaCount == 0
// - Reverts with BalanceNotSettled() if unsettled

Credit/Debt Functions

solidity
// Give credit to caller (tokens owed TO them)
function _supplyCredit(IERC20 token, uint256 amount) internal;

// Take debt from caller (tokens owed BY them)
function _takeDebt(IERC20 token, uint256 amount) internal;

// Settle debt by transferring tokens to Vault
function settle(IERC20 token, uint256 amountHint) external returns (uint256 credit);

// Claim credit by receiving tokens from Vault
function sendTo(IERC20 token, address to, uint256 amount) external;

Token Handling

Token Types

solidity
enum TokenType {
    STANDARD,    // No rate provider, 1:1 value
    WITH_RATE    // Has rate provider for yield-bearing or wrapped tokens
}

struct TokenConfig {
    IERC20 token;
    TokenType tokenType;
    IRateProvider rateProvider;  // For WITH_RATE tokens
    bool paysYieldFees;          // Yield fees charged on this token
}

Scaling System

All internal math uses 18-decimal precision ("scaled18"):

solidity
struct PoolData {
    IERC20[] tokens;
    uint256[] balancesRaw;           // Native decimals
    uint256[] balancesLiveScaled18;  // 18 decimals, rate-adjusted
    uint256[] tokenRates;            // Rate provider values or FP(1)
    uint256[] decimalScalingFactors; // 10^(18 - decimals)
}

Pools in V3

Pools are separate contracts that implement IBasePool:

solidity
interface IBasePool {
    // Core swap math - called by Vault
    function onSwap(PoolSwapParams memory request) external returns (uint256);

    // Invariant calculation for add/remove liquidity
    function computeInvariant(uint256[] memory balancesLiveScaled18, Rounding rounding)
        external view returns (uint256);

    // Balance computation for single-token operations
    function computeBalance(
        uint256[] memory balancesLiveScaled18,
        uint256 tokenInIndex,
        uint256 invariantRatio
    ) external view returns (uint256 newBalance);
}

Pool Tokens (BPT)

Each pool has its own ERC20 token (BPT - Balancer Pool Token):

  • Minted when adding liquidity
  • Burned when removing liquidity
  • Token contract IS the pool contract (via BalancerPoolToken)
  • Balances stored in Vault, not the token contract

ERC4626 Buffers

V3 supports efficient wrapped token operations via buffers:

solidity
// Buffers hold underlying + wrapped token reserves
// Enables wrap/unwrap without external calls when liquidity available
function erc4626BufferWrapOrUnwrap(BufferWrapOrUnwrapParams memory params) external;

Key Storage Locations

solidity
// Pool state
mapping(address pool => PoolConfigBits) internal _poolConfigBits;
mapping(address pool => IERC20[]) internal _poolTokens;
mapping(address pool => mapping(uint256 index => bytes32)) internal _poolTokenBalances;

// Token reserves (total Vault balance per token)
mapping(IERC20 token => uint256) internal _reservesOf;

// ERC4626 buffers
mapping(IERC4626 => bytes32 packedBalance) internal _bufferTokenBalances;

Progressive Disclosure

For deeper understanding, see these related skills:

SkillTopic
balancer-v3-vaultDetailed Vault operations (swap, add/remove liquidity)
balancer-v3-poolsPool registration and initialization
balancer-v3-weighted-poolWeighted Pool math and factory
balancer-v3-routerRouter patterns and user interaction
balancer-v3-hooksHook system for pool customization