This page is a directory of every opcode family registered on the deployed Aqua router, AquaSwapVMRouter v1.0.2 (0x111111338c5091e8440b67b168bae16a668ac0de). Each family maps to one instruction contract in 1inch/swap-vm. The router dispatches an opcode through _dispatch, which calls _runOpcode in AquaOpcodes.sol: the numeric index in the bytecode selects the instruction function directly.
An opcode is one byte in the program, immediately followed by a one-byte args length and then that many argument bytes. The dispatcher (AquaOpcodes._opcodes()) is a fixed jump table. Reserved gap indices (0–9 and 22–26) map to _notInstruction, an empty function — reaching one is a no-op (execution continues; it neither prices nor reverts). An opcode byte beyond the registered table reverts with a Solidity array-out-of-bounds panic (Panic(0x32)). There is no named UnknownOpcode/OpcodeNotFound error and no dynamic registration on the deployed v1.0.2 router.
Registered opcode summary
The table lists every opcode registered in the deployed _runOpcode dispatcher, grouped by instruction family. Indices are the exact numeric slots from AquaOpcodes.sol. The SDK builder column names the exported Opcode from @1inch/swap-vm (package path typescript/swap-vm/src/swap-vm/instructions).
| Family | Opcode index | Function | SDK builder | Args (byte schema) | Source |
|---|---|---|---|---|---|
| Controls | 10 | _jump |
jump |
nextPC 2 (uint16) |
src/instructions/Controls.sol |
| Controls | 11 | _jumpIfTokenIn |
jumpIfTokenIn |
token 20 + nextPC 2 |
src/instructions/Controls.sol |
| Controls | 12 | _jumpIfTokenOut |
jumpIfTokenOut |
token 20 + nextPC 2 |
src/instructions/Controls.sol |
| Controls | 13 | _deadline |
deadline |
deadline 5 (uint40) |
src/instructions/Controls.sol |
| Controls | 14 | _onlyTakerTokenBalanceNonZero |
onlyTakerTokenBalanceNonZero |
token 20 |
src/instructions/Controls.sol |
| Controls | 15 | _onlyTakerTokenBalanceGte |
onlyTakerTokenBalanceGte |
token 20 + minAmount 32 |
src/instructions/Controls.sol |
| Controls | 16 | _onlyTakerTokenSupplyShareGte |
onlyTakerTokenSupplyShareGte |
token 20 + minShareE18 8 (uint64) |
src/instructions/Controls.sol |
| Controls | 20 | _salt |
salt |
salt 8 (uint64); arbitrary bytes also accepted |
src/instructions/Controls.sol |
| Controls | 33 | _onlyTxOriginTokenBalanceNonZero |
onlyTxOriginTokenBalanceNonZero |
token 20 |
src/instructions/Controls.sol |
| XYCSwap | 17 | _xycSwapXD |
xycSwapXD |
none (0 bytes) | src/instructions/XYCSwap.sol |
| XYCConcentrate | 18 | _xycConcentrateGrowLiquidity2D |
concentrateGrowLiquidity2D |
sqrtPriceMin 32 + sqrtPriceMax 32 |
src/instructions/XYCConcentrate.sol |
| Decay | 19 | _decayXD |
decayXD |
period 2 (uint16) |
src/instructions/Decay.sol |
| Fee | 21 | _flatFeeAmountInXD |
flatFeeAmountInXD |
feeBps 4 (uint32) |
src/instructions/Fee.sol |
| Fee | 27 | _protocolFeeAmountInXD |
protocolFeeAmountInXD |
feeBps 4 + to 20 |
src/instructions/Fee.sol |
| Fee | 28 | _aquaProtocolFeeAmountInXD |
aquaProtocolFeeAmountInXD |
feeBps 4 + to 20 |
src/instructions/Fee.sol |
| Fee | 29 | _dynamicProtocolFeeAmountInXD |
dynamicProtocolFeeAmountInXD |
feeProvider 20 |
src/instructions/Fee.sol |
| Fee | 30 | _aquaDynamicProtocolFeeAmountInXD |
aquaDynamicProtocolFeeAmountInXD |
feeProvider 20 |
src/instructions/Fee.sol |
| PeggedSwap | 31 | _peggedSwapGrowPriceRange2D |
peggedSwapGrowPriceRange2D |
x0 32 + y0 32 + linearWidth 32 + rateLt 32 + rateGt 32 (160 total) |
src/instructions/PeggedSwap.sol |
| Extruction | 32 | _extruction |
extruction |
target 20 + extructionArgs N |
src/instructions/Extruction.sol |
Opcodes outside this registered set are not executable on the deployed router: the reserved gaps (indices 0–9, 22–26) resolve to the no-op _notInstruction, and an index past the table reverts with an array-out-of-bounds panic (Panic(0x32)). This includes every experimental fee opcode in FeeExperimental.sol (for example flatFeeAmountOutXD, progressiveFeeInXD, protocolFeeAmountOutXD): those SDK builders exist for tooling but their opcodes are not wired into AquaSwapVMRouter v1.0.2.
Families
Controls
Control flow and access-guard instructions. They never compute swap amounts — they redirect execution (jumps) or revert on a failed state check. _jump, _jumpIfTokenIn and _jumpIfTokenOut set the next program counter (targets limited to uint16, 0–65,535). _deadline reverts once block.timestamp passes the encoded time. _onlyTakerTokenBalanceNonZero, _onlyTakerTokenBalanceGte and _onlyTakerTokenSupplyShareGte gate the fill on the taker's ERC-20 or NFT holdings; _onlyTxOriginTokenBalanceNonZero gates on tx.origin instead (a weaker check — see the source notes on delegation). _salt is a no-op that adds uniqueness to the order hash. Registered indices: 10–16, 20, 33. See the Controls instruction reference.
XYCSwap
Constant-product AMM (x × y = k). Index 17, function _xycSwapXD. Terminal swap formula that takes no args and requires both virtual balances to be non-zero; on exact-in it computes amountOut, on exact-out it computes amountIn with ceil-division to favor the maker. See the XYCSwap instruction reference.
XYCConcentrate
Concentrated-liquidity swap bounded by a sqrt price range (Uniswap-v3-style), for two tokens only. Index 18, function _xycConcentrateGrowLiquidity2D. Args are two uint256 sqrt-price bounds in 1e18 fixed point (sqrtPriceMin, sqrtPriceMax) where price P = tokenGt / tokenLt. The instruction recomputes liquidity L from the real balances each swap, derives virtual reserves, and performs a single constant-product step; fee reinvestment happens implicitly as real balances grow. See the XYCConcentrate instruction reference.
Decay
Mooniswap-style virtual-balance protection. Index 19, function _decayXD. Args are a uint16 period (2 bytes). It adjusts balanceIn/balanceOut by decaying offsets stored per order, token and direction, then runs the inner loop; after a real swap it records new offsets so a large swap temporarily worsens the rate and restores linearly over the decay period. Reads offset state in quote mode without updating it, so quote and swap can diverge if state changed between calls. See the Decay instruction reference.
Fee
Fee instructions applied to amountIn. All use the shared BPS scale where 1e9 = 100% and feeBps ≤ 1e9 (for example 0.30% = 3000000). _flatFeeAmountInXD (index 21) keeps the fee inside the pool — this is the only non-zero fee in v1. _protocolFeeAmountInXD (27) transfers the fee from the maker to a recipient via safeTransferFrom; _aquaProtocolFeeAmountInXD (28) pulls it from the maker's Aqua balance instead. _dynamicProtocolFeeAmountInXD (29) and _aquaDynamicProtocolFeeAmountInXD (30) read feeBps and recipient from an external provider via staticcall. Registered indices: 21, 27–30. See the Fee instruction reference.
PeggedSwap
Square-root linear curve for pegged assets (stablecoin, wrapped and LST/LRT pairs). Index 31, function _peggedSwapGrowPriceRange2D. Args are 160 bytes: five uint256 fields x0, y0, linearWidth, rateLt, rateGt (initial normalization reserves, the linear coefficient A scaled by 1e27, and per-token rate multipliers assigned by address order). The curve √(x/X₀) + √(y/Y₀) + A(x/X₀ + y/Y₀) = 1 + A is solved analytically at fixed curvature p = 0.5. See the PeggedSwap instruction reference.
Extruction
Delegation to external maker logic for advanced strategies. Index 32, function _extruction. Args are a 20-byte target address followed by N bytes of extructionArgs forwarded to the callee. In quote mode it calls IStaticExtruction.extruction (view); in swap mode IExtruction.extruction (state-modifying). The target returns an updated program counter, a chopped-length of consumed taker args, and updated swap registers. Targets must be deterministic and consistent across both interfaces or quote and swap will diverge. See the Extruction instruction reference.
Precision details
This section pins down four things that trip up program authors reading the summary table above: the exact byte offset of each argument field, how the SDK's raw-price helper maps onto the sqrt-price bytes the opcode actually stores, the difference between the decimal indices shown here and the hex bytes in a program, and why the flat-fee argument is called feeBps on-chain but built with FlatFeeArgs.fromBps in the SDK. Everything below is verified against 1inch/swap-vm at tag v1.0.1 and the matching @1inch/swap-vm TypeScript coders.
Argument byte offsets
Argument blobs are built with abi.encodePacked and read back with fixed slice(start, end) calls, so fields are concatenated in declaration order with no padding and no length prefixes between them. The one length byte that precedes the blob (see the info panel at the top of this page) is the total args length; individual fields are located purely by offset. The table gives the offset of every field for the opcodes where the layout is non-trivial (more than one field, or a field wider than the opcode's mnemonic implies).
| Opcode | Field | Offset (bytes) | Length | Type |
|---|---|---|---|---|
_jumpIfTokenIn (11) / _jumpIfTokenOut (12) |
token |
0 | 20 | address |
nextPC |
20 | 2 | uint16 | |
_onlyTakerTokenBalanceGte (15) |
token |
0 | 20 | address |
minAmount |
20 | 32 | uint256 | |
_onlyTakerTokenSupplyShareGte (16) |
token |
0 | 20 | address |
minShareE18 |
20 | 8 | uint64 | |
_flatFeeAmountInXD (21) |
feeBps |
0 | 4 | uint32 |
_protocolFeeAmountInXD (27) / _aquaProtocolFeeAmountInXD (28) |
feeBps |
0 | 4 | uint32 |
to |
4 | 20 | address | |
_xycConcentrateGrowLiquidity2D (18) |
sqrtPriceMin |
0 | 32 | uint256 |
sqrtPriceMax |
32 | 32 | uint256 | |
_peggedSwapGrowPriceRange2D (31) |
x0 |
0 | 32 | uint256 |
y0 |
32 | 32 | uint256 | |
linearWidth |
64 | 32 | uint256 | |
rateLt |
96 | 32 | uint256 | |
rateGt |
128 | 32 | uint256 |
The guard opcodes all lead with the 20-byte token address and then append their value: _onlyTakerTokenBalanceGte reads minAmount as a full uint256 at offset 20 (total 52 bytes), while _onlyTakerTokenSupplyShareGte reads only an 8-byte minShareE18 at offset 20 (total 28 bytes). PeggedSwap requires the blob to be at least 160 bytes (5 × 32) and reverts PeggedSwapInvalidArgsLength otherwise; it then casts the calldata pointer directly onto the five fields with no copy. rateLt/rateGt are assigned to input/output by address order at runtime (tokenIn < tokenOut keeps them in order, otherwise they swap), which is why they are stored as an ordered pair rather than an in/out pair.
rawPrice → sqrtPrice bridge (XYCConcentrate)
The _xycConcentrateGrowLiquidity2D opcode stores sqrt prices, not prices: two uint256 fields, sqrtPriceMin and sqrtPriceMax, each 32 bytes in 1e18 fixed-point, where P = tokenGt / tokenLt (higher-address token over lower-address token). The Solidity builder XYCConcentrateArgsBuilder.build2D takes those sqrt values directly and requires 0 < sqrtPriceMin < sqrtPriceMax.
The SDK exposes two constructors on ConcentrateGrowLiquidity2DArgs so callers can supply whichever form they hold. If you already have sqrt values, use fromSqrtPrices; if you hold plain prices, use fromRawPrices, which does the sqrt conversion for you before encoding. The bridge is:
// raw price P in 1e18 fixed-point -> stored sqrt price in 1e18 fixed-point
sqrtPrice = bigintSqrt(rawPrice * 1e18) // so (sqrtPrice / 1e18)^2 == rawPrice / 1e18
// @1inch/swap-vm
ConcentrateGrowLiquidity2DArgs.fromRawPrices(rawPriceMin, rawPriceMax)
// is equivalent to
ConcentrateGrowLiquidity2DArgs.fromSqrtPrices(
bigintSqrt(rawPriceMin * 1e18n),
bigintSqrt(rawPriceMax * 1e18n),
)
Both paths land in the same coder, which writes exactly addUint256(sqrtPriceMin) then addUint256(sqrtPriceMax) — the 64-byte layout in the offsets table above. The contract reads them back with slice(0, 32) and slice(32, 64).
Do not hand a raw price to fromSqrtPrices (or a sqrt price to fromRawPrices). Either mistake encodes valid-looking bytes that price a completely different range: the opcode never re-derives one form from the other, it trusts the stored sqrt values as-is when it recomputes liquidity L from real balances each swap.
Opcode radix: decimal here, hex in program bytes
Every opcode index in this gallery is written in decimal, matching the numeric slots in AquaOpcodes.sol. In an actual program each opcode is a single byte, so when you inspect or hand-assemble order.data (hooks || program) you will see the index in hex. Convert before you read raw bytes:
| Decimal (gallery) | Hex (program byte) | Function |
|---|---|---|
| 17 | 0x11 |
_xycSwapXD |
| 18 | 0x12 |
_xycConcentrateGrowLiquidity2D |
| 21 | 0x15 |
_flatFeeAmountInXD |
| 27 | 0x1b |
_protocolFeeAmountInXD |
| 31 | 0x1f |
_peggedSwapGrowPriceRange2D |
| 32 | 0x20 |
_extruction |
| 33 | 0x21 |
_onlyTxOriginTokenBalanceNonZero |
So a byte reading 0x11 in the program is index 17, XYCSwap — not the decimal value 11 (which is 0x0b, _jumpIfTokenIn). The reserved gap indices are equally worth converting: decimal 0–9 is 0x00–0x09 and decimal 22–26 is 0x16–0x1a, all mapping to the no-op _notInstruction.
Fee argument naming: feeBps vs FlatFeeArgs.fromBps
The names collide but describe different scales. On-chain the field is called feeBps, yet it is not basis points: Fee.sol defines BPS = 1e9 and treats feeBps as a value on a 1e9 = 100% scale, requiring feeBps ≤ 1e9. The SDK stores that same 1e9-scale integer in the field FlatFeeArgs.fee (a uint32), while the factory FlatFeeArgs.fromBps takes real basis points (10,000 bps = 100%) and scales up by 100,000 to reach the on-chain units:
// @1inch/swap-vm — FlatFeeArgs
FlatFeeArgs.fromBps(30); // 30 bps = 0.30% -> fee = 30 * 100000 = 3_000_000
FlatFeeArgs.fromPercent(0.3); // 0.30% -> fromBps(0.3 * 100) = 3_000_000
FlatFeeArgs.fromBps(10000); // 100% -> fee = 10000 * 100000 = 1_000_000_000 (1e9)
new FlatFeeArgs(3_000_000n); // raw 1e9-scale constructor, same as fromBps(30)
The coder encodes fee as a single uint32 (addUint32), which is the 4-byte feeBps field the opcode parses with parseFlatFee at offset 0. So the summary table's feeBps 4 (uint32) and the SDK's FlatFeeArgs.fromBps(...) agree on the bytes; only the input unit differs (1e9-scale on-chain and in the fee field, real bps at the fromBps boundary). The example in the Fee family note above — 0.30% = 3000000 — is exactly FlatFeeArgs.fromBps(30). The same 1e9 scale and the same fromBps/fromPercent convention apply to the protocol-fee opcodes (27–30), whose feeBps field shares the identical 4-byte uint32 layout.