PeggedSwap is a square-root linear swap curve optimized for pegged assets (stablecoins, wrapped tokens, and LSTs with fixed ratios). It provides minimal slippage near the peg and uses an analytical solution, so no iterative solver is required.
PeggedSwap is one of the instructions in the Aqua opcode set (AquaOpcodes), executed by SwapVM, the Aqua swap engine, on the AquaSwapVMRouter. The full deployed set is Controls, XYCSwap, XYCConcentrate, Decay, Fee, PeggedSwap, and Extruction.
Source: src/instructions/PeggedSwap.sol
Curve formula: √(x/X₀) + √(y/Y₀) + A(x/X₀ + y/Y₀) = 1 + A (curvature p = 0.5, hardcoded)
Args encoding
All fields are 32-byte uint256 values, packed via abi.encodePacked:
| Field | Offset | Size | Description |
|---|---|---|---|
x0 |
0 | 32 bytes | Initial reserve normalization factor for tokenLt (token with lower address) |
y0 |
32 | 32 bytes | Initial reserve normalization factor for tokenGt (token with higher address) |
linearWidth |
64 | 32 bytes | Linear component coefficient A in 1e27 scale; max 5000 × 1e27; higher = tighter peg |
rateLt |
96 | 32 bytes | Decimal scale multiplier for tokenLt |
rateGt |
128 | 32 bytes | Decimal scale multiplier for tokenGt |
Total: 160 bytes. Build with PeggedSwapArgsBuilder.build(PeggedSwapArgsBuilder.Args memory args).
Rate multipliers normalize tokens to a common precision. When token addresses determine lt/gt, assign rates so that balance × rate is in the same scale for both tokens.
Example: USDC (6 decimals, lower address) and DAI (18 decimals, higher address):
rateLt = 1e12 // scales 1000e6 USDC → 1000e18
rateGt = 1 // 1000e18 DAI stays as-is
x0 = 1000e18 // normalized initial USDC reserve
y0 = 1000e18 // normalized initial DAI reserve
When to use: Suitable for pairs that maintain a fixed or near-fixed ratio (USDC/USDT, WETH/stETH with hardcoded rate). Not suitable for pairs where the peg ratio drifts over time (e.g. WETH/wstETH with an ever-increasing exchange rate), because the curve has finite reserves and a hard price boundary.
Instructions
_peggedSwapGrowPriceRange2D
function _peggedSwapGrowPriceRange2D(Context memory ctx, bytes calldata args) internal pure
Behavior
- Parses args and determines
(rateIn, rateOut, x0_init, y0_init)based ontokenIn < tokenOut - Normalizes real balances:
x0 = balanceIn × rateIn,y0 = balanceOut × rateOut - Computes the curve invariant from
(x0, y0, x0_init, y0_init, linearWidth) - exactIn: finds
y1(new normalized output reserve) that satisfies the invariant givenx1 = x0 + amountIn × rateIn, then converts back:amountOut = (y0 − y1) / rateOut - exactOut: finds
x1fromy1 = y0 − amountOut × rateOut, then:amountIn = ⌈(x1 − x0) / rateIn⌉
All rounding protects the maker: amountOut rounds down, amountIn rounds up.
Errors
| Error | Condition |
|---|---|
PeggedSwapRecomputeDetected() |
amountOut != 0 (exactIn) or amountIn != 0 (exactOut) |
PeggedSwapBothBalancesZero() |
Both balanceIn and balanceOut are zero |
PeggedSwapInvalidArgsLength(length) |
Args shorter than 160 bytes |
PeggedSwapInvalidLinearWidth(linearWidth) |
linearWidth > 5000e27 (MAX_LINEAR_WIDTH = 5000 × 1e27) |
PeggedSwapInvalidInitialBalances(x0, y0) |
Either x0 or y0 is zero |
PeggedSwapInvalidRates(rateLt, rateGt) |
Either rate is zero |
Related
- XYCSwap: constant-product AMM for non-pegged pairs
- XYCConcentrate: concentrated liquidity for non-pegged pairs
- Balances: provides
balanceIn/balanceOut