Instruction set overview

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

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.

Did you find what you need?