The Balances instruction loads virtual reserves into the swap context, giving the downstream swap-formula instruction the balanceIn and balanceOut values it prices against. Every SwapVM program must run a Balances instruction before any swap-formula instruction.
These reserves are virtual accounting values used by the pricing math. They are not a token deposit: the maker's tokens stay in the maker's own wallet under a revocable allowance and move only when a taker fills a swap.
Engine: Balances belongs to the SwapVM instruction set (SwapVM is the Aqua swap engine) and executes on the Aqua router (AquaSwapVMRouter, the deployed SwapVM router). The static variant embeds reserves in the program args; the dynamic variant persists them in SwapVM storage.
Source: src/instructions/Balances.sol
Static vs dynamic
Both variants share one packed args layout but differ in where the reserves live and how they evolve.
| Variant | Reserves live in | Typical use |
|---|---|---|
_staticBalancesXD |
Embedded in the program args (read-only) | Limit order or auction priced from fixed embedded values rather than evolving reserves |
_dynamicBalancesXD |
SwapVM contract storage, keyed by (orderHash, token) |
Stateful AMM (XYC constant product, concentrated liquidity) where reserves change with each fill |
Args encoding
Both instructions share the same packed args layout:
| Field | Size | Description |
|---|---|---|
tokensCount |
2 bytes (uint16) |
Number of tokens in the list |
tokens[] |
20 × tokensCount bytes |
Token addresses |
initialBalances[] |
32 × tokensCount bytes |
Initial balance for each token (same order) |
Total: 2 + 20N + 32N bytes.
Build with BalancesArgsBuilder.build(address[] tokens, uint256[] balances).
Instructions
_staticBalancesXD
function _staticBalancesXD(Context memory ctx, bytes calldata args) internal pure
Sets ctx.swap.balanceIn and ctx.swap.balanceOut from the embedded initialBalances array. No state is read or written; balances are fixed for the lifetime of this program execution.
Use when liquidity reserves do not change between swaps, for example a limit order or auction priced at a fixed rate.
Errors
| Error | Condition |
|---|---|
SetBalancesExpectZeroBalances(balanceIn, balanceOut) |
balances already set before this instruction |
StaticBalancesRequiresSettingBothBalances(tokenIn, tokenOut, tokens) |
tokenIn or tokenOut not found in the token list |
_dynamicBalancesXD
function _dynamicBalancesXD(Context memory ctx, bytes calldata args) internal
Loads or initializes balances from SwapVM contract storage keyed by (orderHash, token). After running the nested sub-instructions (via ctx.runLoop()), it writes the resulting swapAmountIn and swapAmountOut back to storage:
balances[orderHash][tokenIn] += swapAmountIn
balances[orderHash][tokenOut] -= swapAmountOut
Use for stateful AMM strategies (XYC constant product, concentrated liquidity) where reserves evolve with each swap.
Quote/swap divergence: In quote mode (isStaticContext = true) balances are read but not updated after the nested loop. A quote may succeed while the swap reverts if another transaction fills the order first. Do not use backward jumps to this instruction.
Errors
| Error | Condition |
|---|---|
DynamicBalancesLoadingRequiresSettingBothBalances(tokenIn, tokenOut, tokens) |
tokenIn or tokenOut not found during load |
DynamicBalancesInitRequiresSettingBothBalances(tokenIn, tokenOut, tokens) |
tokenIn or tokenOut not found during init |
Public state
mapping(bytes32 orderHash => mapping(address token => uint256)) public balances;
Related
- XYCSwap — constant product swap, uses these reserves
- XYCConcentrate — concentrated liquidity swap
- LimitSwap — limit order price, uses these reserves
- PeggedSwap — pegged-asset swap, uses these reserves