Reference for the built-in instruction contracts in src/instructions/ of the SwapVM engine. Each instruction is a Solidity contract method that SwapVM, the Aqua swap engine, calls while it walks a strategy program during quote() and swap().
Programs encode opcode numbers, so each @1inch/swap-vm-sdk version targets a specific router opcode table, and strategy bytecode is not portable across router versions. The tables below separate the instruction families registered on the deployed Aqua router (AquaSwapVMRouter v1.0.1, wired through AquaOpcodes) from families that live on other routers, are experimental, or are not registered.
Naming convention
The suffix on each instruction function name indicates its execution context and token scope:
| Suffix | Meaning |
|---|---|
XD |
Runs in both static (quote) and dynamic (swap) contexts |
1D |
Dynamic only: reads or writes on-chain state; single-token direction |
2D |
Dynamic, two-token: operates on both tokenIn and tokenOut together |
Deployed Aqua opcode set
The deployed Aqua router (AquaSwapVMRouter v1.0.1, through AquaOpcodes) registers exactly seven instruction families: Controls, XYCSwap, XYCConcentrate, Decay, Fee, PeggedSwap, and Extruction. Only these are callable on-chain through Aqua today. The subsections below list each family and its functions.
Swap formulas (terminal)
| Instruction | Function | Description |
|---|---|---|
| XYCSwap | _xycSwapXD |
Constant-product AMM (x × y = k) |
| XYCConcentrate | _xycConcentrateGrowLiquidity2D |
Concentrated liquidity with sqrtPriceMin/sqrtPriceMax bounds |
| PeggedSwap | _peggedSwapGrowPriceRange2D |
Square-root linear curve for pegged assets |
These formulas read and write virtual reserves. Virtual reserves are not one of the seven registered families; they are the router's reserve and accounting state, exposed through a base Balances mixin that the swap formulas read and write internally rather than a top-level opcode composed into most programs. The Balances mixin exposes:
| Instruction | Function | Description |
|---|---|---|
| Balances | _staticBalancesXD |
Set fixed virtual reserves from embedded args |
| Balances | _dynamicBalancesXD |
Load or init reserves from storage; persist changes after swap |
Virtual reserves are an internal accounting counter. Maker tokens stay in the maker's own wallet under a revocable, per-chain, per-token allowance, and move only when a taker fills a swap atomically. The protocol holds no tokens.
Pre-swap price modifier
| Instruction | Function | Description |
|---|---|---|
| Decay | _decayXD |
Mooniswap-style virtual offset with time decay |
Fees
Fee instructions embed the fee into the strategy program and the swap amounts; there is no on-chain fee event. In the MVP only the LP fee (shown in the interface as the "Swap fee") is non-zero. The protocol fee and dynamic protocol fee exist in the contracts but are set to 0 in v1 and are not displayed. LP fees auto-compound through the registry's Pushed credit.
| Instruction | Function | Applied to | Transfer |
|---|---|---|---|
| Fee | _flatFeeAmountInXD |
amountIn |
None |
| Fee | _protocolFeeAmountInXD |
amountIn |
ERC-20 |
| Fee | _aquaProtocolFeeAmountInXD |
amountIn |
Aqua pull |
| Fee | _dynamicProtocolFeeAmountInXD |
amountIn |
ERC-20 (dynamic rate) |
| Fee | _aquaDynamicProtocolFeeAmountInXD |
amountIn |
Aqua pull (dynamic rate) |
Control flow and access guards
| Instruction | Function | Description |
|---|---|---|
| Controls | _salt |
No-op; used for order hash uniqueness |
| Controls | _jump |
Unconditional jump to program counter |
| Controls | _jumpIfTokenIn |
Conditional jump if tokenIn matches |
| Controls | _jumpIfTokenOut |
Conditional jump if tokenOut matches |
| Controls | _deadline |
Revert if past timestamp |
| Controls | _onlyTakerTokenBalanceNonZero |
Revert if taker holds no balance |
| Controls | _onlyTakerTokenBalanceGte |
Revert if taker balance below threshold |
| Controls | _onlyTakerTokenSupplyShareGte |
Revert if taker supply share below threshold |
The launch taker gate is the _onlyTxOriginTokenBalanceNonZero(token) control, which reverts with TxOriginTokenBalanceIsZero unless balanceOf(tx.origin) > 0. The dApp assembler applies it to every strategy by calling withTxOriginAccessToken(aquaKycToken), so the taker must hold the KycNFT credential. Because the check is on tx.origin, smart accounts, multisigs, and ERC-4337 bundlers cannot pass it. The gate is evaluated at swap time, not at ship time; makers stay permissionless and only takers are gated. Per-strategy taker allow-lists (_whitelistSingleTaker / _whitelistMultipleTakers) exist in source but are not registered on-chain today, so do not build against them.
Extruction (external pricing)
| Instruction | Function | Description |
|---|---|---|
| Extruction | _extruction |
Delegate to an external contract for proprietary pricing via IExtruction |
An Extruction target must be non-upgradeable, and it must return identical amounts from quote() and swap(). The canonical spelling is Extruction / IExtruction (never "extraction").
Not registered on the deployed Aqua router
The following instruction families exist in the engine source but are not part of the deployed Aqua opcode set. Programs on the Aqua router cannot call them.
Limit Order and Fusion opcode set
Order invalidation and the limit, TWAP, and Dutch-auction primitives belong to the Limit Order and Fusion product on a separate router, not to Aqua. Aqua strategies have no order-level partial fills. Invalidators and SeriesEpochManager are limit-order families and are not Aqua opcodes.
| Instruction | Function | Description |
|---|---|---|
| Invalidators | _invalidateBit1D |
One-time execution via bitmap |
| Invalidators | _invalidateTokenIn1D |
Track cumulative amountIn against balanceIn cap |
| Invalidators | _invalidateTokenOut1D |
Track cumulative amountOut against balanceOut cap |
| LimitSwap | _limitSwap1D |
Fixed-rate swap; partial fills allowed |
| LimitSwap | _limitSwapOnlyFull1D |
Fixed-rate swap; full fill only |
| TWAPSwap | _twap |
TWAP with linear unlocking and Dutch auction decay |
| DutchAuction | _dutchAuctionBalanceIn1D |
Pre-swap Dutch auction modifier: decays balanceIn over time |
| DutchAuction | _dutchAuctionBalanceOut1D |
Pre-swap Dutch auction modifier: grows balanceOut over time |
Additional engine instructions
Present in src/instructions/ but not part of the deployed Aqua opcode set.
| Instruction | Function | Description |
|---|---|---|
| BaseFeeAdjuster | _baseFeeAdjuster1D |
Improves rate when gas price exceeds baseline |
| OraclePriceAdjuster | _oraclePriceAdjuster1D |
Adjusts amounts toward Chainlink oracle price |
| MinRate | _requireMinRate1D |
Reverts if exchange rate below minimum |
| MinRate | _adjustMinRate1D |
Caps amounts at minimum rate instead of reverting |
Experimental fees (not shipped)
The FeeExperimental family is not part of the deployed opcode set. "LP progressive fees" do not exist in the shipped product.
| Instruction | Function | Applied to | Transfer |
|---|---|---|---|
| FeeExperimental | _flatFeeAmountOutXD |
amountOut |
None |
| FeeExperimental | _progressiveFeeInXD |
amountIn |
None (volume-sensitive) |
| FeeExperimental | _progressiveFeeOutXD |
amountOut |
None (volume-sensitive) |
| FeeExperimental | _protocolFeeAmountOutXD |
amountOut |
ERC-20 |
| FeeExperimental | _aquaProtocolFeeAmountOutXD |
amountOut |
Aqua pull |
Debug (Foundry only)
Debug instructions are available only in Foundry test builds and are not registered on-chain.
| Instruction | Function | Description |
|---|---|---|
| Debug | _printSwapRegisters |
Log balanceIn, balanceOut, amountIn, amountOut |
| Debug | _printSwapQuery |
Log orderHash, taker, maker, tokenIn, tokenOut, isExactIn |
| Debug | _printContext |
Log nextPC and takerArgs |
| Debug | _printGasLeft |
Log gasleft() |
| Debug | _printFreeMemoryPointer |
Log free memory pointer |
Related
- Patterns & decision tree: recipes that compose these instructions
Canonical opcode table
The table below is the complete, deployed Aqua instruction set as registered in src/opcodes/AquaOpcodes.sol (the opcode contract mixed into AquaSwapVMRouter). Every numeric index is taken directly from the _runOpcode dispatcher in that file, which is authoritative: the router dispatches by these exact indices. Instructions are grouped by their source contract (family). The SDK builder method column shows how the instruction is emitted with ProgramBuilder (Program memory program = ProgramBuilder.init(_opcodes())); the Args helper column shows the *ArgsBuilder library call that produces the packed argument bytes.
Indices are contiguous only within a family. Gaps in the numbering (0–9, and 22–26) map to a reserved _notInstruction sentinel and are not registered — 0–9 are held for debugging utilities, 22–26 are held for backward-compatibility slots. Calling an unregistered index reverts.
| Family | Function | Opcode index | SDK builder method | Args helper |
|---|---|---|---|---|
| Controls | _jump |
10 | program.build(_jump, args) |
ControlsArgsBuilder.buildJump(uint16 nextPC) |
| Controls | _jumpIfTokenIn |
11 | program.build(_jumpIfTokenIn, args) |
ControlsArgsBuilder.buildJumpIfToken(address token, uint16 nextPC) |
| Controls | _jumpIfTokenOut |
12 | program.build(_jumpIfTokenOut, args) |
ControlsArgsBuilder.buildJumpIfToken(address token, uint16 nextPC) |
| Controls | _deadline |
13 | program.build(_deadline, args) |
ControlsArgsBuilder.buildDeadline(uint40 deadline) |
| Controls | _onlyTakerTokenBalanceNonZero |
14 | program.build(_onlyTakerTokenBalanceNonZero, args) |
ControlsArgsBuilder.buildTokenBalanceNonZero(address token) |
| Controls | _onlyTakerTokenBalanceGte |
15 | program.build(_onlyTakerTokenBalanceGte, args) |
ControlsArgsBuilder.buildTakerTokenBalanceGte(address token, uint256 minAmount) |
| Controls | _onlyTakerTokenSupplyShareGte |
16 | program.build(_onlyTakerTokenSupplyShareGte, args) |
ControlsArgsBuilder.buildTakerTokenSupplyShareGte(address token, uint64 minShareE18) |
| XYCSwap | _xycSwapXD |
17 | program.build(_xycSwapXD) |
— (no args; the args field is ignored) |
| XYCConcentrate | _xycConcentrateGrowLiquidity2D |
18 | program.build(_xycConcentrateGrowLiquidity2D, args) |
XYCConcentrateArgsBuilder.build2D(uint256 sqrtPriceMin, uint256 sqrtPriceMax) |
| Decay | _decayXD |
19 | program.build(_decayXD, args) |
DecayArgsBuilder.build(uint16 decayPeriod) |
| Controls | _salt |
20 | program.build(_salt, args) |
ControlsArgsBuilder.buildSalt(uint64 salt) / buildSalt(bytes salt) |
| Fee | _flatFeeAmountInXD |
21 | program.build(_flatFeeAmountInXD, args) |
FeeArgsBuilder.buildFlatFee(uint32 feeBps) |
| Fee | _protocolFeeAmountInXD |
27 | program.build(_protocolFeeAmountInXD, args) |
FeeArgsBuilder.buildProtocolFee(uint32 feeBps, address to) |
| Fee | _aquaProtocolFeeAmountInXD |
28 | program.build(_aquaProtocolFeeAmountInXD, args) |
FeeArgsBuilder.buildProtocolFee(uint32 feeBps, address to) |
| Fee | _dynamicProtocolFeeAmountInXD |
29 | program.build(_dynamicProtocolFeeAmountInXD, args) |
FeeArgsBuilder.buildDynamicProtocolFee(address feeProvider) |
| Fee | _aquaDynamicProtocolFeeAmountInXD |
30 | program.build(_aquaDynamicProtocolFeeAmountInXD, args) |
FeeArgsBuilder.buildDynamicProtocolFee(address feeProvider) |
| PeggedSwap | _peggedSwapGrowPriceRange2D |
31 | program.build(_peggedSwapGrowPriceRange2D, args) |
PeggedSwapArgsBuilder.build(PeggedSwapArgsBuilder.Args{x0, y0, linearWidth, rateLt, rateGt}) |
| Extruction | _extruction |
32 | program.build(_extruction, args) |
— (no dedicated builder; args = abi.encodePacked(address target, bytes extructionArgs)) |
| Controls | _onlyTxOriginTokenBalanceNonZero |
33 | program.build(_onlyTxOriginTokenBalanceNonZero, args) |
ControlsArgsBuilder.buildTokenBalanceNonZero(address token) |
Reserves (Balances) on the Aqua router
On the deployed Aqua router there is no reserve-setting opcode in the program. Reserves are sourced from Aqua itself: before the program runs, SwapVM loads them with AQUA.safeBalances(maker, router, orderHash, tokenIn, tokenOut) into ctx.swap.balanceIn / ctx.swap.balanceOut whenever the order uses useAquaInsteadOfSignature. The maker provisions those reserves at ship time via aqua.ship(swapVM, strategy, tokens, balances). Instructions such as _xycSwapXD, _xycConcentrateGrowLiquidity2D and _peggedSwapGrowPriceRange2D then read the reserves already present in ctx.swap.
The signature-mode Balances opcodes — _staticBalancesXD, _dynamicBalancesXD and the BalancesArgsBuilder.build(uint256[2] balances) helper — belong to the generic SwapVMRouter opcode set (src/opcodes/Opcodes.sol), not to AquaOpcodes. They are not registered on the Aqua router.
Opcodes not in this table are not registered on the deployed Aqua router. At program-build time, referencing an instruction the router does not register reverts OpcodeNotFound (ProgramBuilder.findOpcode); at run time, dispatching any index outside this set reverts UnknownOpcode(opcode) from AquaOpcodes._runOpcode.