.claude/skills/balancer-v3-architecture/SKILL.md
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.
npx skillsauth add cyotee/crane Balancer V3 ArchitectureInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Balancer V3 is a complete rewrite of the Balancer protocol, featuring a singleton Vault design with transient accounting for gas-efficient operations.
All liquidity in Balancer V3 is managed by a single Vault contract:
┌─────────────────────────────────────────────────────────────┐
│ VAULT │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Pool A │ │ Pool B │ │ Pool C │ │
│ │ (Weighted) │ │ (Stable) │ │ (Custom) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
│ Token Reserves: All ERC20 balances held centrally │
│ Pool Accounting: Per-pool balance tracking │
└─────────────────────────────────────────────────────────────┘
| Contract | Purpose | |----------|---------| | Vault | Main entry point. Core swap/liquidity operations. Inherits VaultCommon + Proxy pattern. | | VaultExtension | Less frequently used functions (pool registration, queries). Delegate-called via Proxy. | | VaultAdmin | Permissioned administrative functions. | | Router | User-facing entry point. Handles ETH, Permit2, unlocking. |
unlock() callbackInstead of transferring tokens on each swap, V3 tracks credits and debts:
// 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
// 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;
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
}
All internal math uses 18-decimal precision ("scaled18"):
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 are separate contracts that implement IBasePool:
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);
}
Each pool has its own ERC20 token (BPT - Balancer Pool Token):
V3 supports efficient wrapped token operations via buffers:
// Buffers hold underlying + wrapped token reserves
// Enables wrap/unwrap without external calls when liquidity available
function erc4626BufferWrapOrUnwrap(BufferWrapOrUnwrapParams memory params) external;
// 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;
For deeper understanding, see these related skills:
| Skill | Topic |
|-------|-------|
| balancer-v3-vault | Detailed Vault operations (swap, add/remove liquidity) |
| balancer-v3-pools | Pool registration and initialization |
| balancer-v3-weighted-pool | Weighted Pool math and factory |
| balancer-v3-router | Router patterns and user interaction |
| balancer-v3-hooks | Hook system for pool customization |
development
Review UI code for Web Interface Guidelines compliance. Use when asked to "review my UI", "check accessibility", "audit design", "review UX", or "check my site against best practices".
documentation
Write to contracts and send transactions. Use when executing state-changing contract functions.
development
HTTP and WebSocket transports for blockchain connectivity. Use when configuring network connections.
data-ai
Read contract data with type-safe ABI. Use when querying smart contract view/pure functions.