Skip to content

1inch/swap-vm

Repository files navigation

SwapVM

Github Release CI Coverage Tests npm License Solidity Foundry

A virtual machine for programmable token swaps. Execute complex trading strategies from bytecode programs without deploying contracts.


πŸ“š Table of Contents


Overview

What is SwapVM?

SwapVM is a computation engine that executes token swap strategies from bytecode programs. Instead of deploying smart contracts, you compose instructions into programs that are signed off-chain and executed on-demand.

Key Features:

  • Static Balances - Fixed exchange rates for single-direction trades (limit orders, auctions, TWAP, DCA, RFQ)
  • Dynamic Balances - Persistent, isolated AMM-style orders (each maker's liquidity is separate)
  • Composable Instructions - Mix and match building blocks for complex strategies (combining pricing, fees, MEV protection)

Who is this for?

  • 🌾 Makers - Provide liquidity through limit orders, AMM-style orders, or complex strategies
  • πŸƒ Takers - Execute swaps to arbitrage or fulfill trades
  • πŸ›  Developers - Build custom instructions and integrate SwapVM

🌐 Deployment

SwapVM is deployed across multiple chains with a unified address for seamless cross-chain integration.

Contract Address: 0x8fdd04dbf6111437b44bbca99c28882434e0958f

Supported Networks:

  • Ethereum Mainnet
  • Base
  • Optimism
  • Polygon
  • Arbitrum
  • Avalanche
  • Binance Smart Chain
  • Linea
  • Sonic
  • Unichain
  • Gnosis
  • zkSync

How It Works

The 4-Register Model

SwapVM uses 4 registers to compute token swaps:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    SwapRegisters                           β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  balanceIn:  Maker's available input token balance         β”‚
β”‚  balanceOut: Maker's available output token balance        β”‚
β”‚  amountIn:   Input amount (taker provides OR VM computes)  β”‚
β”‚  amountOut:  Output amount (taker provides OR VM computes) β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

The Core Principle:

  1. Taker specifies ONE amount (either amountIn or amountOut)
  2. VM computes the OTHER amount using the 4 registers
  3. Instructions modify registers to apply fees, adjust rates, etc.

Execution Flow

The execution flow shows all available instructions and strategies for each balance type:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚      1D STRATEGY (Static Balances, Single Direction)     β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ BYTECODE COMPOSITION (Off-chain)                         β”‚
β”‚                                                          β”‚
β”‚ 1. Balance Setup (Required)                              β”‚
β”‚    └─ _staticBalancesXD β†’ Fixed exchange rate            β”‚
β”‚                                                          β”‚
β”‚ 2. Core Swap Logic (Choose One)                          β”‚
β”‚    β”œβ”€ _limitSwap1D β†’ Partial fills allowed               β”‚
β”‚    └─ _limitSwapOnlyFull1D β†’ All-or-nothing              β”‚
β”‚                                                          β”‚
β”‚ 3. Order Invalidation (Required for Partial Fills)       β”‚
β”‚    β”œβ”€ _invalidateBit1D β†’ One-time order                  β”‚
β”‚    β”œβ”€ _invalidateTokenIn1D β†’ Track input consumed        β”‚
β”‚    └─ _invalidateTokenOut1D β†’ Track output distributed   β”‚
β”‚                                                          β”‚
β”‚ 4. Dynamic Pricing (Optional, Combinable)                β”‚
β”‚    β”œβ”€ _dutchAuctionBalanceIn1D β†’ Decreasing input amount  β”‚
β”‚    β”œβ”€ _dutchAuctionBalanceOut1D β†’ Increasing output amountβ”‚
β”‚    β”œβ”€ _oraclePriceAdjuster1D β†’ External price feed       β”‚
β”‚    └─ _baseFeeAdjuster1D β†’ Gas-responsive pricing        β”‚
β”‚                                                          β”‚
β”‚ 5. Fee Mechanisms (Optional, Combinable)                 β”‚
β”‚    β”œβ”€ _flatFeeAmountInXD β†’ Fee from input amount         β”‚
β”‚    β”œβ”€ _flatFeeAmountOutXD β†’ Fee from output amount       β”‚
β”‚    β”œβ”€ _progressiveFeeInXD β†’ Size-based dynamic fee (input)β”‚
β”‚    β”œβ”€ _progressiveFeeOutXD β†’ Size-based dynamic fee (output)β”‚
β”‚    β”œβ”€ _protocolFeeAmountOutXD β†’ Protocol revenue (ERC20) β”‚
β”‚    └─ _aquaProtocolFeeAmountOutXD β†’ Protocol revenue (Aqua)β”‚
β”‚                                                          β”‚
β”‚ 6. Advanced Strategies (Optional)                        β”‚
β”‚    β”œβ”€ _requireMinRate1D β†’ Enforce minimum exchange rate  β”‚
β”‚    β”œβ”€ _adjustMinRate1D β†’ Adjust amounts to meet min rate β”‚
β”‚    β”œβ”€ _twap β†’ Time-weighted average price execution      β”‚
β”‚    └─ _extruction β†’ Extract and execute custom logic     β”‚
β”‚                                                          β”‚
β”‚ 7. Control Flow (Optional)                               β”‚
β”‚    β”œβ”€ _jump β†’ Skip instructions                          β”‚
β”‚    β”œβ”€ _jumpIfTokenIn β†’ Conditional on exact input        β”‚
β”‚    β”œβ”€ _jumpIfTokenOut β†’ Conditional on exact output      β”‚
β”‚    β”œβ”€ _deadline β†’ Expiration check                       β”‚
β”‚    β”œβ”€ _onlyTakerTokenBalanceNonZero β†’ Require balance > 0β”‚
β”‚    β”œβ”€ _onlyTakerTokenBalanceGte β†’ Minimum balance check  β”‚
β”‚    β”œβ”€ _onlyTakerTokenSupplyShareGte β†’ Min % of supply   β”‚
β”‚    └─ _salt β†’ Order uniqueness (hash modifier)           β”‚
β”‚                                                          β”‚
β”‚ EXECUTION (On-chain)                                     β”‚
β”‚ β”œβ”€ Verify signature & expiration                         β”‚
β”‚ β”œβ”€ Load static balances into 4 registers                 β”‚
β”‚ β”œβ”€ Execute bytecode instructions sequentially            β”‚
β”‚ β”œβ”€ Update invalidator state (prevent replay/overfill)    β”‚
β”‚ └─ Transfer tokens (single direction only)               β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  AMM STRATEGIES (2D/XD Bidirectional, Two Balance Options) β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ BALANCE MANAGEMENT OPTIONS                                 β”‚
β”‚                                                            β”‚
β”‚ Option A: Dynamic Balances (SwapVM Internal)               β”‚
β”‚    β”œβ”€ Setup: Sign order with EIP-712                       β”‚
β”‚    β”œβ”€ Balance Instruction: _dynamicBalancesXD              β”‚
β”‚    └─ Storage: SwapVM contract (self-managed)              β”‚
β”‚                                                            β”‚
β”‚ Option B: Aqua Protocol (External)                         β”‚
β”‚    β”œβ”€ Setup: Deposit via Aqua.ship() (on-chain)            β”‚
β”‚    β”œβ”€ Balance Instruction: None (Aqua manages)             β”‚
β”‚    β”œβ”€ Configuration: useAquaInsteadOfSignature = true      β”‚
β”‚    └─ Storage: Aqua protocol (shared liquidity)            β”‚
β”‚                                                            β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ BYTECODE COMPOSITION (Same for Both)                       β”‚
β”‚                                                            β”‚
β”‚ 1. Balance Setup                                           β”‚
β”‚    β”œβ”€ Dynamic: _dynamicBalancesXD (required)               β”‚
β”‚    └─ Aqua: Skip (balances in Aqua)                        β”‚
β”‚                                                            β”‚
β”‚ 2. AMM Logic (Choose Primary Strategy)                     β”‚
β”‚    β”œβ”€ _xycSwapXD β†’ Classic x*y=k constant product          β”‚
β”‚    └─ _xycConcentrateGrowLiquidityXD/2D β†’ CLMM ranges      β”‚
β”‚                                                            β”‚
β”‚ 3. Fee Mechanisms (Optional, Combinable)                   β”‚
β”‚    β”œβ”€ _flatFeeAmountInXD β†’ Fee from input amount           β”‚
β”‚    β”œβ”€ _flatFeeAmountOutXD β†’ Fee from output amount         β”‚
β”‚    β”œβ”€ _progressiveFeeInXD β†’ Size-based dynamic fee (input) β”‚
β”‚    β”œβ”€ _progressiveFeeOutXD β†’ Size-based dynamic fee (output)β”‚
β”‚    β”œβ”€ _protocolFeeAmountOutXD β†’ Protocol revenue (ERC20)   β”‚
β”‚    └─ _aquaProtocolFeeAmountOutXD β†’ Protocol revenue (Aqua)β”‚
β”‚                                                            β”‚
β”‚ 4. MEV Protection (Optional)                               β”‚
β”‚    └─ _decayXD β†’ Virtual reserves (Mooniswap-style)        β”‚
β”‚                                                            β”‚
β”‚ 5. Advanced Features (Optional)                            β”‚
β”‚    β”œβ”€ _twap β†’ Time-weighted average price trading          β”‚
β”‚    └─ _extruction β†’ Extract and execute custom logic       β”‚
β”‚                                                            β”‚
β”‚ 6. Control Flow (Optional)                                 β”‚
β”‚    β”œβ”€ _jump β†’ Skip instructions                            β”‚
β”‚    β”œβ”€ _jumpIfTokenIn β†’ Conditional jump on exact input     β”‚
β”‚    β”œβ”€ _jumpIfTokenOut β†’ Conditional jump on exact output   β”‚
β”‚    β”œβ”€ _deadline β†’ Expiration check                         β”‚
β”‚    β”œβ”€ _onlyTakerTokenBalanceNonZero β†’ Require balance > 0  β”‚
β”‚    β”œβ”€ _onlyTakerTokenBalanceGte β†’ Minimum balance check    β”‚
β”‚    β”œβ”€ _onlyTakerTokenSupplyShareGte β†’ Min % of supply     β”‚
β”‚    └─ _salt β†’ Order uniqueness (hash modifier)             β”‚
β”‚                                                            β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ EXECUTION (On-chain)                                       β”‚
β”‚                                                            β”‚
β”‚ Dynamic Balances Flow:                                     β”‚
β”‚ β”œβ”€ Verify EIP-712 signature                                β”‚
β”‚ β”œβ”€ Load maker's isolated reserves from SwapVM              β”‚
β”‚ β”œβ”€ Execute AMM calculations                                β”‚
β”‚ β”œβ”€ Update maker's state in SwapVM storage                  β”‚
β”‚ └─ Transfer tokens (bidirectional)                         β”‚
β”‚                                                            β”‚
β”‚ Aqua Protocol Flow:                                        β”‚
β”‚ β”œβ”€ Verify Aqua balance (no signature)                      β”‚
β”‚ β”œβ”€ Load reserves from Aqua protocol                        β”‚
β”‚ β”œβ”€ Execute AMM calculations (same logic!)                  β”‚
β”‚ β”œβ”€ Aqua updates balance accounting                         β”‚
β”‚ └─ Transfer tokens via Aqua settlement                     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚           COMMON TAKER FLOW (All Strategies)            β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ 1. Discovery (Off-chain)                                β”‚
β”‚    β”œβ”€ Find orders via indexer/API                       β”‚
β”‚    β”œβ”€ Filter by tokens, rates, liquidity                β”‚
β”‚    └─ Simulate profitability                            β”‚
β”‚                                                         β”‚
β”‚ 2. Quote (On-chain View)                                β”‚
β”‚    β”œβ”€ Call quote() to preview exact amounts             β”‚
β”‚    β”œβ”€ Check slippage and fees                           β”‚
β”‚    └─ Verify execution conditions                       β”‚
β”‚                                                         β”‚
β”‚ 3. Execution Parameters                                 β”‚
β”‚    β”œβ”€ isExactIn β†’ Specify input or output amount        β”‚
β”‚    β”œβ”€ threshold β†’ Minimum/maximum acceptable amount     β”‚
β”‚    β”œβ”€ to β†’ Recipient address                            β”‚
β”‚    └─ hooks β†’ Pre/post swap callbacks                   β”‚
β”‚                                                         β”‚
β”‚ 4. Settlement                                           β”‚
β”‚    β”œβ”€ Maker β†’ Taker (output token)                      β”‚
β”‚    └─ Taker β†’ Maker (input token)                       β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Bytecode Format

Programs are sequences of instructions, each encoded as:

[opcode_index][args_length][args_data]
     ↑            ↑            ↑
  1 byte       1 byte      N bytes

Example: A limit order might compile to:

[17][4A][balance_args][26][01][swap_args]
  ↑                      ↑
  staticBalances        limitSwap

Balance Types Explained

SwapVM offers two primary balance management approaches:

Static Balances (Single-Direction Trading)

Use Case: Limit orders, Dutch auctions, TWAP, DCA, RFQ, range orders, stop-loss

  • Fixed Rate: Exchange rate remains constant
  • Partial Fills: Supports partial execution with amount invalidators
  • No Storage: Pure function, no state persistence
  • Direction: Single-direction trades (e.g., only sell ETH for USDC)
// Example: Sell 1 ETH for 2000 USDC
p.build(Balances._staticBalancesXD,
    BalancesArgsBuilder.build(
        dynamic([WETH, USDC]),
        dynamic([1e18, 2000e6])  // Fixed rate
    ))

Dynamic Balances (Automated Market Making)

Use Case: Constant product AMMs, CLMMs

  • Self-Rebalancing: Balances update after each trade
  • State Persistence: Order state stored in SwapVM
  • Isolated Liquidity: Each maker's funds are separate (no pooling)
  • Bidirectional: Supports trading in both directions
  • Price Discovery: Price adjusts based on reserves
// Example: Initialize AMM-style order with 10 ETH and 20,000 USDC
p.build(Balances._dynamicBalancesXD,
    BalancesArgsBuilder.build(
        dynamic([WETH, USDC]),
        dynamic([10e18, 20_000e6])  // Initial reserves
    ))

Core Invariants

SwapVM maintains fundamental invariants that ensure economic security and predictable behavior across all instructions:

1. Exact In/Out Symmetry

Every instruction MUST maintain symmetry between exactIn and exactOut swaps:

  • If exactIn(X) β†’ Y, then exactOut(Y) β†’ X (within rounding tolerance)
  • Critical for price consistency and preventing internal arbitrage
  • Validated by test suite across all swap instructions

2. Swap Additivity

Splitting swaps must not provide better rates:

  • swap(A+B) should equal swap(A) + swap(B) for output amounts
  • Ensures no gaming through order splitting
  • Larger trades cannot be improved by breaking into smaller ones

3. Quote/Swap Consistency

Quote and swap functions must return identical amounts:

  • quote() is a view function that previews swap results
  • swap() execution must match the quoted amounts exactly
  • Essential for MEV protection and predictable execution

4. Price Monotonicity

Larger trades receive equal or worse prices:

  • Price defined as amountOut/amountIn
  • Must decrease (or stay constant) as trade size increases
  • Natural consequence of liquidity curves and market impact

5. Rounding Favors Maker

All rounding operations must favor the liquidity provider:

  • Small trades (few wei) shouldn't exceed theoretical spot price
  • amountIn always rounds UP (ceil)
  • amountOut always rounds DOWN (floor)
  • Protects makers from rounding-based value extraction

6. Balance Sufficiency

Trades cannot exceed available liquidity:

  • Must revert if computed amountOut > balanceOut
  • Prevents impossible trades and protects order integrity
  • Enforced at the VM level before token transfers

These invariants are validated through comprehensive test suites and must be maintained by any new instruction implementations.

Testing Invariants in Your Code

SwapVM provides a reusable CoreInvariants base contract for testing:

import { CoreInvariants } from "test/invariants/CoreInvariants.t.sol";

contract MyInstructionTest is Test, OpcodesDebug, CoreInvariants {
    function test_MyInstruction_MaintainsInvariants() public {
        // Create order with your instruction
        ISwapVM.Order memory order = createOrderWithMyInstruction();
        
        // Test all invariants at once
        assertAllInvariants(swapVM, order, tokenIn, tokenOut);
        
        // Or test specific invariants
        assertSymmetryInvariant(swapVM, order, tokenIn, tokenOut, 
            amount, tolerance, exactInData, exactOutData);
        assertMonotonicityInvariant(swapVM, order, tokenIn, tokenOut, 
            amounts, takerData);
    }
}

Configuration options for complex scenarios:

InvariantConfig memory config = createInvariantConfig(testAmounts, tolerance);
config.skipAdditivity = true;    // For stateless orders
config.skipMonotonicity = true;  // For fixed-rate orders
assertAllInvariantsWithConfig(swapVM, order, tokenIn, tokenOut, config);

See test/invariants/ExampleInvariantUsage.t.sol for complete examples.


🌾 For Makers (Liquidity Providers)

Makers provide liquidity by creating orders with custom swap logic.

Your Role

  • Define swap logic via bytecode programs (includes setting balances/exchange rate)
  • Configure order parameters (expiration, fees, hooks)
  • Sign orders off-chain (gasless)

Creating a Simple Limit Order

// 1. Build your swap program
Program memory p = ProgramBuilder.init(_opcodes());
bytes memory program = bytes.concat(
    // Set your exchange rate: 1000 USDC for 0.5 WETH
    p.build(Balances._staticBalancesXD,
        BalancesArgsBuilder.build(
            dynamic([USDC, WETH]),
            dynamic([1000e6, 0.5e18])  // Your offered rate
        )),
    // Execute the swap
    p.build(LimitSwap._limitSwap1D,
        LimitSwapArgsBuilder.build(USDC, WETH)),
    // Track partial fills (prevents overfilling)
    p.build(Invalidators._invalidateTokenOut1D,
        InvalidatorsArgsBuilder.buildInvalidateByTokenOut(WETH))
);

// 2. Configure order parameters
MakerTraits makerTraits = MakerTraitsLib.build(MakerTraitsLib.Args({
    shouldUnwrapWeth: false,         // Keep WETH (don't unwrap to ETH)
    expiration: block.timestamp + 1 days,  // Order expires in 24h
    receiver: address(0),             // You receive the tokens
    useAquaInsteadOfSignature: false // Use standard EIP-712 signing
}));

// 3. Create order (completely off-chain)
ISwapVM.Order memory order = ISwapVM.Order({
    maker: yourAddress,
    traits: makerTraits,
    program: program
});

// 4. Sign order off-chain (gasless)
bytes32 orderHash = swapVM.hash(order);
bytes memory signature = signEIP712(orderHash);

Building an AMM Strategy

Create a persistent, isolated AMM-style order (your liquidity only):

// Constant product AMM with 0.3% fee
bytes memory program = bytes.concat(
    // Load/initialize balances
    p.build(Balances._dynamicBalancesXD,
        BalancesArgsBuilder.build(
            dynamic([USDC, WETH]),
            dynamic([100_000e6, 50e18])  // Initial liquidity
        )),
    // Apply trading fee
    p.build(Fee._flatFeeAmountInXD, 
        FeeArgsBuilder.buildFlatFee(0.003e9)),  // 0.3%
    // Execute constant product swap (x*y=k)
    p.build(XYCSwap._xycSwapXD)
);

Balance Management Options

Option 1: Static Balances (1D Single-Direction Strategies)

// Fixed exchange rate for 1D strategies (limit orders, auctions)
p.build(Balances._staticBalancesXD, ...)

Characteristics:

  • Fixed exchange rate throughout order lifetime
  • Supports partial fills with amount invalidators
  • No state storage (pure function)
  • Single-direction trades only
  • Ideal for: Limit orders, Dutch auctions, TWAP, DCA, RFQ, range orders, stop-loss

Option 2: AMM Strategies (2D/XD Bidirectional) - Two Storage Choices

Both options use the same AMM logic and support identical features. The only difference is where balances are stored:

2A. Dynamic Balances (SwapVM Internal)
// Persistent AMM-style order with isolated liquidity
p.build(Balances._dynamicBalancesXD, ...)
// Sign with EIP-712

Storage: SwapVM contract (per-maker isolation)
Setup: Sign order off-chain (gasless)
Use Case: Individual AMM strategies, custom curves
Key Point: Replicates Aqua-like functionality but with signature-based orders (no deposits)
Note: Each maker's liquidity is isolated - no pooling with others

2B. Aqua Protocol (External Shared Liquidity)
// Use Aqua's shared liquidity layer
MakerTraits makerTraits = MakerTraitsLib.build({
    useAquaInsteadOfSignature: true
});
// Requires prior: aqua.ship(token, amount)

Storage: Aqua protocol (external)
Setup: Deposit to Aqua via ship()
Use Case: Share liquidity across multiple strategies
Key Difference: Unlike isolated dynamic balances, Aqua enables shared liquidity

See Aqua Protocol for details

Maker Security

Your orders are protected by:

  • EIP-712 Signatures - Orders cannot be modified
  • Expiration Control - Orders expire when you want
  • Balance Limits - Cannot trade more than specified
  • Custom Receivers - Send tokens where you want
  • Hooks - Custom validation logic
  • Order Invalidation - One-time execution via bitmaps

Best Practices:

  • Always set expiration dates
  • Use _invalidateBit1D for one-time orders
  • Validate rates match market conditions
  • Consider MEV protection (_decayXD)

πŸƒ For Takers (Swap Executors)

Takers execute swaps against maker orders to arbitrage or fulfill trades.

Your Role

  • Find profitable orders to execute
  • Specify swap amount (either input or output)
  • Provide dynamic data for adaptive instructions
  • Execute swaps on-chain

Executing a Swap

// 1. Find an order to execute
ISwapVM.Order memory order = findProfitableOrder();

// 2. Preview the swap (free call)
(uint256 amountIn, uint256 amountOut) = swapVM.asView().quote(
    order,
    USDC,           // Token you're trading
    WETH,           // Token you're receiving
    1000e6,         // Amount (input if isExactIn=true)
    takerTraitsData // Your execution parameters
);

// 3. Prepare taker parameters
bytes memory takerTraits = TakerTraitsLib.build(TakerTraitsLib.Args({
    isExactIn: true,              // You specify input amount
    threshold: minAmountOut,      // Minimum output (slippage protection)
    to: yourAddress,              // Where to receive tokens
    shouldUnwrapWeth: false,      // Keep as WETH
    // Optional features:
    hasPreTransferInHook: false,
    isFirstTransferFromTaker: false
}));

// 4. Execute the swap
(uint256 actualIn, uint256 actualOut, bytes32 orderHash) = swapVM.swap(
    order,
    USDC,
    WETH,
    1000e6,        // Your input amount
    abi.encodePacked(signature, takerTraits, customData)
);

Providing Dynamic Data

Some instructions read data from takers at execution time:

// Pack custom data for instructions
bytes memory customData = abi.encode(
    oraclePrice,    // For oracle-based adjustments
    maxGasPrice,    // For gas-sensitive orders
    userPreference  // Any custom parameters
);

// Instructions access via:
// ctx.tryChopTakerArgs(32) - extracts 32 bytes

Understanding isExactIn

The isExactIn flag determines which amount you control:

isExactIn You Specify VM Computes Use Case
true Input amount Output amount "I want to sell exactly 1000 USDC"
false Output amount Input amount "I want to buy exactly 0.5 WETH"

Taker Security

Your swaps are protected by:

  • Threshold Validation - Minimum output / maximum input
  • Slippage Protection - Via threshold amounts
  • Custom Recipients - Send tokens anywhere
  • Pre-hooks - Validate before execution
  • Quote Preview - Check amounts before executing

Best Practices:

  • Always use quote() before swap()
  • Set appropriate thresholds for slippage
  • Verify order hasn't expired
  • Check for MEV opportunities
  • Consider gas costs vs profit

MEV Opportunities

SwapVM creates MEV opportunities:

  1. Arbitrage - Price differences between orders
  2. Liquidations - Execute against distressed positions
  3. JIT Liquidity - Provide liquidity just-in-time
  4. Sandwich Protection - Some orders use _decayXD for protection

πŸ›  For Developers

Build custom instructions and integrate SwapVM into your protocols.

Understanding the Execution Environment

The Context Structure

Every instruction receives a Context with three components:

Context
β”œβ”€β”€ VM (Execution State)
β”‚   β”œβ”€β”€ nextPC ───────────────────── Program counter (MUTABLE - for jumps)
β”‚   β”œβ”€β”€ programPtr ───────────────── Bytecode being executed
β”‚   β”œβ”€β”€ takerArgsPtr ─────────────── Taker's dynamic data (MUTABLE - via tryChopTakerArgs)
β”‚   └── opcodes ──────────────────── Available instructions array
β”‚
β”œβ”€β”€ SwapQuery (READ-ONLY)
β”‚   β”œβ”€β”€ orderHash ────────────────── Unique order identifier
β”‚   β”œβ”€β”€ maker ────────────────────── Liquidity provider address
β”‚   β”œβ”€β”€ taker ────────────────────── Swap executor address
β”‚   β”œβ”€β”€ tokenIn ──────────────────── Input token address
β”‚   β”œβ”€β”€ tokenOut ─────────────────── Output token address
β”‚   └── isExactIn ────────────────── Taker's swap direction (true = exact in, false = exact out)
β”‚
└── SwapRegisters (MUTABLE)
    β”œβ”€β”€ balanceIn ────────────────── Maker's available input token balance
    β”œβ”€β”€ balanceOut ───────────────── Maker's available output token balance
    β”œβ”€β”€ amountIn ─────────────────── Input amount (taker provides OR VM computes)
    └── amountOut ────────────────── Output amount (taker provides OR VM computes)

Order Configuration (MakerTraits & TakerTraits)

MakerTraits (256-bit packed)
β”œβ”€β”€ Token Handling
β”‚   └── shouldUnwrapWeth ────────── Unwrap WETH to ETH on output
β”‚
β”œβ”€β”€ Order Lifecycle  
β”‚   └── expiration (40 bits) ────── Unix timestamp when order expires
β”‚
β”œβ”€β”€ Balance Management
β”‚   β”œβ”€β”€ useAquaInsteadOfSignature ─ Use Aqua balance instead of signature
β”‚   └── allowZeroAmountIn ─── Skip Aqua for input transfers
β”‚
β”œβ”€β”€ Recipient Control
β”‚   └── receiver ─────────────────── Custom recipient (0 = maker)
β”‚
└── Hooks (Callbacks)
    β”œβ”€β”€ hasPreTransferOutHook ────── Call maker before output transfer
    β”œβ”€β”€ hasPostTransferInHook ────── Call maker after input transfer
    β”œβ”€β”€ preTransferOutData ────────── Data for pre-transfer hook
    └── postTransferInData ────────── Data for post-transfer hook
TakerTraits (Variable-length)
β”œβ”€β”€ Swap Direction
β”‚   └── isExactIn ────────────────── true = specify input, false = output
β”‚
β”œβ”€β”€ Amount Validation
β”‚   β”œβ”€β”€ threshold ────────────────── Min output or max input
β”‚   └── isStrictThresholdAmount ─── true = exact, false = min/max
β”‚
β”œβ”€β”€ Token Handling
β”‚   β”œβ”€β”€ shouldUnwrapWeth ─────────── Unwrap WETH to ETH on output
β”‚   └── to ───────────────────────── Custom recipient (0 = taker)
β”‚
β”œβ”€β”€ Transfer Mechanics
β”‚   β”œβ”€β”€ isFirstTransferFromTaker ── Who transfers first
β”‚   └── useTransferFromAndAquaPush ─ SwapVM does transferFrom + Aqua push (vs taker pushes in callback)
β”‚
└── Hooks (Callbacks)
    └── hasPreTransferInHook ─────── Call taker before input transfer

Instruction Capabilities

Instructions compute swap amounts only - they do NOT execute the actual token transfers (except protocol fee instructions which can transfer fees). The swap itself happens after all instructions complete.

Instructions can only modify three aspects of the Context:

1. Swap Registers (ctx.swap.*)

All four registers can be modified to calculate swap amounts:

  • balanceIn / balanceOut - Set or adjust available balances for calculations
  • amountIn / amountOut - Compute the missing swap amount

2. Program Counter (ctx.vm.nextPC)

Control execution flow between instructions:

  • Skip instructions (jump forward)
  • Loop back to previous instructions
  • Conditional branching based on computation state

3. Taker Data (ctx.tryChopTakerArgs())

Consume data provided by taker at execution time:

  • Read dynamic parameters for calculations
  • Process variable-length data
  • Advance the taker data pointer

Special: Nested Execution (ctx.runLoop())

Instructions can invoke ctx.runLoop() to execute remaining instructions and then continue:

  • Apply pre-processing, let other instructions compute amounts, then post-processing
  • Wrap amount computations with fee calculations
  • Wait for amount computation before validation
  • Implement complex multi-phase amount calculations

Instruction Security Model

Instructions operate within SwapVM's execution framework:

What Instructions CAN Do:

  • βœ… Read all context data (query, VM state, registers)
  • βœ… Modify the 4 swap registers
  • βœ… Change program counter for control flow
  • βœ… Consume taker-provided data
  • βœ… Read and write to their own storage mappings
  • βœ… Make external calls (via _extruction)
  • βœ… Execute fee transfers (protocol fee instructions)

What Instructions CANNOT Do:

  • ❌ Modify query data (maker, taker, tokens, etc. - immutable)
  • ❌ Transfer swap tokens directly (except protocol fees)
  • ❌ Bypass SwapVM's validation (thresholds, signatures, etc.)
  • ❌ Modify core SwapVM protocol state
  • ❌ Execute after swap is complete

Security Considerations:

  • Reentrancy protection only for Aqua settlement (via transient storage when taker pushes)
  • Gas limited by block and transaction
  • External calls risk managed by maker's instruction choice
  • Deterministic execution

Building a Custom Router

Routers define available instructions:

contract MyRouter is SwapVM, Opcodes {
    constructor(address aqua) 
        SwapVM(aqua, "MyRouter", "1.0") 
        Opcodes(aqua) 
    {}
    
    function _instructions() internal pure override 
        returns (function(Context memory, bytes calldata) internal[] memory) 
    {
        // Return your instruction set
        return _opcodes();
    }
}

Testing Instructions

Use the provided CoreInvariants base contract to ensure your instructions maintain all invariants:

contract MyInstructionTest is Test, OpcodesDebug, CoreInvariants {
    function test_MyInstruction() public {
        // Build program with your instruction
        bytes memory program = buildProgramWithMyInstruction();
        ISwapVM.Order memory order = createOrder(program);
        
        // Validate all core invariants are maintained
        assertAllInvariants(swapVM, order, tokenA, tokenB);
    }
}

For manual testing:

function testMyInstructionManually() public {
    // Create test context
    Context memory ctx = Context({
        vm: VM({
            isStaticContext: false,
            nextPC: 0,
            programPtr: CalldataPtrLib.from(program),
            takerArgsPtr: CalldataPtrLib.from(takerData),
            opcodes: _opcodes()
        }),
        query: SwapQuery({
            orderHash: bytes32(0),
            maker: makeAddr("maker"),
            taker: makeAddr("taker"),
            tokenIn: address(tokenA),
            tokenOut: address(tokenB),
            isExactIn: true
        }),
        swap: SwapRegisters({
            balanceIn: 1000e18,
            balanceOut: 2000e18,
            amountIn: 100e18,
            amountOut: 0
        })
    });
    
    // Execute instruction
    bytes memory args = abi.encode(0.003e9); // 0.3% fee
    MyInstruction._myInstruction(ctx, args);
    
    // Verify results
    assertGt(ctx.swap.amountOut, 0);
}

πŸ”’ Security Model

Core Invariants as Security Foundation

SwapVM's security is built on maintaining fundamental invariants that ensure economic correctness:

  1. Exact In/Out Symmetry - Prevents internal arbitrage opportunities
  2. Swap Additivity - Ensures no gaming through order splitting
  3. Quote/Swap Consistency - Guarantees predictable execution
  4. Price Monotonicity - Natural market dynamics are preserved
  5. Rounding Favors Maker - Protects liquidity providers from value extraction
  6. Balance Sufficiency - Prevents impossible trades

These invariants are enforced at the VM level and validated through comprehensive test suites.

Protocol-Level Security

Core Security Features:

  • EIP-712 Typed Signatures - Prevents signature malleability
  • Order Hash Uniqueness - Each order has unique identifier
  • Reentrancy Protection - Transient storage locks (EIP-1153)
  • Overflow Protection - Solidity 0.8+ automatic checks
  • Gas Limits - Block gas limit prevents infinite loops
  • Invariant Validation - All instructions must maintain core invariants

Signature Verification:

// Standard EIP-712
orderHash = keccak256(abi.encode(
    ORDER_TYPEHASH,
    order.maker,
    order.traits,
    keccak256(order.program)
));

// Or Aqua Protocol (no signature needed)
if (useAquaInsteadOfSignature) {
    require(AQUA.balances(maker, orderHash, token) >= amount);
}

Maker Security

Protection Mechanisms:

Feature Description Implementation
Signature Control Orders cannot be modified EIP-712 signatures
Expiration Time-limited orders expiration in MakerTraits
Balance Limits Cannot exceed specified amounts Register bounds checking
One-time Execution Prevent replay _invalidateBit1D instruction
Custom Logic Hooks for validation Pre/post transfer hooks
Receiver Control Specify token recipient receiver in MakerTraits

Risk Mitigations:

// Limit order exposure
p.build(Invalidators._invalidateBit1D, bitIndex);

// Add expiration
traits.expiration = block.timestamp + 1 hours;

// MEV protection
p.build(Decay._decayXD, DecayArgsBuilder.build(30));

Taker Security

Protection Mechanisms:

Feature Description Implementation
Slippage Protection Min output/max input threshold in TakerTraits
Amount Validation Exact amounts enforced isStrictThresholdAmount flag
Preview Execution Check before swap quote() function
Custom Recipients Control token destination to in TakerTraits
Hook Validation Pre-execution checks hasPreTransferInHook

Risk Mitigations:

// Set minimum output
takerTraits.threshold = minAcceptableOutput;

// Preview first
(amountIn, amountOut) = swapVM.asView().quote(...);
require(amountOut >= minRequired, "Insufficient output");

// Then execute
swapVM.swap(...);

Instruction Security

Sandboxed Execution:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚         Instruction Sandbox             β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  βœ… Allowed:                            β”‚
β”‚  β€’ Read context data                    β”‚
β”‚  β€’ Modify swap registers                β”‚
β”‚  β€’ Control flow (jumps)                 β”‚
β”‚  β€’ Pure computations                    β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  ❌ Restricted:                         β”‚
β”‚  β€’ External calls                       β”‚
β”‚  β€’ Storage modification                 β”‚
β”‚  β€’ Query data modification              β”‚
β”‚  β€’ Infinite loops                       β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Validation Example:

function _safeInstruction(Context memory ctx, bytes calldata args) internal {
    // βœ… Can read and modify swap registers
    ctx.swap.amountIn = ctx.swap.amountIn * 99 / 100;
    
    // βœ… Can read query data (read-only)
    address maker = ctx.query.maker;
    
    // βœ… Can modify VM state for control flow
    ctx.vm.nextPC = newPC;
    
    // βœ… Can consume taker data
    bytes calldata data = ctx.tryChopTakerArgs(32);
    
    // ❌ Cannot do:
    // IERC20(token).transfer(...);  // No external calls
    // ctx.query.maker = newMaker;    // Query is read-only
    // selfdestruct();                // No destructive operations
}

Risk Assessment and Mitigation Options

Program Construction Risks (Maker Responsibility)

Makers define programs that trade assets on their behalf and are responsible for correctness:

Logic Errors

  • Risk: Incorrect instruction sequence or arguments
  • Mitigation: Test thoroughly, use proven patterns, audit critical strategies

Replay Attacks

  • Risk: Order executed multiple times or overfilled
  • Mitigation:
    • Include _invalidateBit1D for one-time execution
    • Use _invalidateTokenIn/Out1D for partial fills
    • Set appropriate expiration

Price Exposure

  • Risk: Trades at unfavorable market conditions
  • Mitigation:
    • Add _requireMinRate1D checks
    • Set expiration timestamps
    • Use oracle price bounds

Order Uniqueness

  • Risk: Cannot create multiple identical orders
  • Mitigation: Use _salt instruction to differentiate, vary parameters slightly

Execution Risks (Taker Responsibility)

Takers control execution parameters and must verify rates:

Rate Slippage

  • Risk: Receive worse exchange rate than expected
  • Mitigation Options:
    • Threshold Protection:
      • Exact: isStrictThresholdAmount = true
      • Min output: isExactIn = true, threshold = minOut
      • Max input: isExactIn = false, threshold = maxIn
    • Callback Validation:
      • Pre-transfer hook: hasPreTransferInHook = true
      • Custom logic via ITakerCallbacks
    • Return Data Verification:
      • Check returned (amountIn, amountOut)
      • Compare with quote() results

MEV Attacks

  • Risk: Front-running or sandwich attacks
  • Mitigation: Use private mempools (Flashbots), set tight thresholds, use commit-reveal patterns

Failed Transactions

  • Risk: Wasted gas from reverts
  • Mitigation: Always call quote() first, verify token balances, check order expiration

SwapVM Security Guarantees

The protocol provides these built-in protections:

Parameter Integrity

  • Never violates maker/taker constraints through strict trait enforcement

Balance Isolation

  • Each maker's liquidity is separate using per-maker storage slots

Instruction Sandboxing

  • No external calls from instructions (pure/view functions only)

Reentrancy Protection

  • Prevents recursive calls using transient locks (EIP-1153)

Overflow Protection

  • Safe arithmetic operations with Solidity 0.8+ checks

Deterministic Execution

  • Same inputs always produce same outputs (no external dependencies in core logic)

πŸ”¬ Advanced Topics

Concentrated Liquidity

Provide liquidity within specific price ranges:

// Calculate concentration parameters
(uint256 deltaA, uint256 deltaB) = XYCConcentrateArgsBuilder.computeDeltas(
    1000e6,   // balanceA
    0.5e18,   // balanceB
    2000e18,  // current price
    1900e18,  // lower bound
    2100e18   // upper bound
);

// Build CLMM strategy
bytes memory program = bytes.concat(
    p.build(Balances._dynamicBalancesXD, balances),
    p.build(XYCConcentrate._xycConcentrateGrowLiquidity2D, 
        XYCConcentrateArgsBuilder.build2D(tokenA, tokenB, deltaA, deltaB)),
    p.build(Fee._flatFeeAmountInXD, fee),
    p.build(XYCSwap._xycSwapXD)
);

1inch Fusion Orders

Complex multi-instruction strategies:

// Dutch auction + gas adjustment + oracle + rate limit
bytes memory program = bytes.concat(
    p.build(Balances._staticBalancesXD, ...),
    p.build(DutchAuction._dutchAuctionBalanceOut1D, ...),
    p.build(BaseFeeAdjuster._baseFeeAdjuster1D, ...),
    p.build(OraclePriceAdjuster._oraclePriceAdjuster1D, ...),
    p.build(MinRate._adjustMinRate1D, ...),
    p.build(LimitSwap._limitSwap1D, ...)
);

Protocol Fee Instructions

SwapVM offers two protocol fee instructions with different settlement mechanisms:

1. _protocolFeeAmountOutXD - Direct ERC20 Transfer

  • Uses standard transferFrom to collect fees
  • Requires maker to have approved SwapVM contract
  • Fee is transferred directly from maker to recipient
  • Suitable for standard ERC20 tokens

2. _aquaProtocolFeeAmountOutXD - Aqua Protocol Integration

  • Uses Aqua's pull function for fee collection
  • Works with orders using Aqua balance management
  • No separate approval needed (uses Aqua's existing permissions)
  • Enables batched fee collection and gas optimization

Usage Example:

// Direct ERC20 protocol fee
p.build(Fee._protocolFeeAmountOutXD, 
    FeeArgsBuilder.buildProtocolFee(10, treasury)); // 0.1% to treasury

// Aqua protocol fee (for Aqua-managed orders)
p.build(Fee._aquaProtocolFeeAmountOutXD,
    FeeArgsBuilder.buildProtocolFee(10, treasury)); // 0.1% via Aqua

Both calculate fees identically but differ in the transfer mechanism.

MEV Protection Strategies

// Virtual balance decay
p.build(Decay._decayXD, DecayArgsBuilder.build(30)); // 30s decay

// Progressive fees (larger swaps pay more)
p.build(Fee._progressiveFeeInXD, ...);  // or _progressiveFeeOutXD

/* Progressive Fee Improvements:
 * New formula: dx_eff = dx / (1 + Ξ» * dx / x) 
 * - Maintains near-perfect exact in/out symmetry
 * - Only ~1 gwei asymmetry from safety ceiling operations
 * - Mathematically reversible for consistent pricing
 */

// Time-based pricing
p.build(DutchAuction._dutchAuctionBalanceOut1D, ...);

TWAP (Time-Weighted Average Price) Configuration

The _twap instruction implements a sophisticated selling strategy with:

  • Linear liquidity unlocking over time
  • Exponential price decay (Dutch auction) for price discovery
  • Automatic price bumps after illiquidity periods
  • Minimum trade size enforcement

Minimum Trade Size Guidelines

Set minTradeAmountOut 1000x+ larger than expected gas costs:

Network Gas Cost Recommended Min Trade
Ethereum $50 $50,000+
Arbitrum/Optimism $0.50 $500+
BSC/Polygon $0.05 $50+

This ensures gas costs remain <0.1% of trade value.

Price Bump Configuration

The priceBumpAfterIlliquidity compensates for mandatory waiting periods:

Min Trade % of Balance Unlock Time Recommended Bump
0.1% 14.4 min 5-10% (1.05e18 - 1.10e18)
1% 14.4 min 10-20% (1.10e18 - 1.20e18)
5% 1.2 hours 30-50% (1.30e18 - 1.50e18)
10% 2.4 hours 50-100% (1.50e18 - 2.00e18)

Additional factors:

  • Network gas costs: Higher gas β†’ larger bumps
  • Pair volatility: Volatile pairs β†’ larger bumps
  • Market depth: Thin markets β†’ higher bumps

Debug Instructions

SwapVM reserves opcodes 1-10 for debugging utilities, available only in debug routers:

Available Debug Instructions:

  • _printSwapRegisters - Logs all 4 swap registers (balances and amounts)
  • _printSwapQuery - Logs query data (orderHash, maker, taker, tokens, isExactIn)
  • _printContext - Logs complete execution context
  • _printFreeMemoryPointer - Logs current memory usage
  • _printGasLeft - Logs remaining gas

Usage:

// Deploy debug router
SwapVMRouterDebug debugRouter = new SwapVMRouterDebug(aquaAddress);

// Include debug instructions in program
bytes memory program = bytes.concat(
    p.build(Balances._staticBalancesXD, ...),
    p.build(Debug._printSwapRegisters),  // Debug output
    p.build(LimitSwap._limitSwap1D, ...),
    p.build(Debug._printContext)          // Final state
);

Note: Debug instructions are no-ops in production routers and should only be used for development and testing.

Gas Optimization

Architecture Benefits:

  • Transient storage (EIP-1153) for reentrancy guards
  • Zero deployment cost for makers
  • Compact bytecode encoding (8-bit opcodes)

Tips for Makers:

  • Use _staticBalancesXD for single-direction trades with fixed rates
  • Use _dynamicBalancesXD for AMM strategies with automatic rebalancing
  • Pack multiple operations in single program
  • Minimize argument sizes

Tips for Takers:

  • Batch multiple swaps
  • Use quote() to avoid failed transactions
  • Consider gas costs in profit calculations

AquaAMM Strategy Builder

The AquaAMM contract provides a helper for building AMM programs with Aqua integration:

import { AquaAMM } from "@1inch/swap-vm/contracts/strategies/AquaAMM.sol";

// Build a concentrated liquidity AMM with fees
ISwapVM.Order memory order = AquaAMM(aquaAMM).buildProgram(
    maker,           // Your address
    expiration,      // Order expiration
    token0,          // First token
    token1,          // Second token
    feeBpsIn,        // Trading fee (e.g., 30 for 0.3%)
    delta0,          // Concentration parameter for token0
    delta1,          // Concentration parameter for token1
    decayPeriod,     // MEV protection decay period
    protocolFeeBps,  // Protocol fee share
    feeReceiver,     // Protocol fee recipient
    salt             // Order uniqueness salt
);

Features:

  • Automatically constructs bytecode with proper instruction ordering
  • Integrates concentrated liquidity, fees, and MEV protection
  • Uses Aqua protocol for balance management (no signatures needed)
  • Includes debug output in development mode

Example: 0.3% Fee Concentrated AMM:

// Calculate concentration deltas for price range
(uint256 delta0, uint256 delta1) = XYCConcentrateArgsBuilder.computeDeltas(
    1000e6,   // 1000 USDC
    0.5e18,   // 0.5 ETH
    2000e18,  // Current price: 2000 USDC/ETH
    1900e18,  // Lower bound
    2100e18   // Upper bound
);

// Build order
ISwapVM.Order memory order = aquaAMM.buildProgram(
    msg.sender,    // maker
    block.timestamp + 30 days,  // expiration
    USDC,          // token0
    WETH,          // token1
    30,            // 0.3% fee
    delta0,        // USDC concentration
    delta1,        // ETH concentration
    30,            // 30s decay period
    10,            // 0.1% protocol fee
    treasury,      // fee receiver
    1              // salt
);

πŸš€ Getting Started

Installation

npm install @1inch/swap-vm
# or
yarn add @1inch/swap-vm

Quick Example

import { SwapVMRouter } from "@1inch/swap-vm/contracts/SwapVMRouter.sol";
import { Program, ProgramBuilder } from "@1inch/swap-vm/test/utils/ProgramBuilder.sol";

// Deploy router
SwapVMRouter router = new SwapVMRouter(aquaAddress, "MyDEX", "1.0");

// Create and execute orders...

Resources

  • GitHub: github.com/1inch/swap-vm
  • Documentation: See /docs directory
  • Tests: Comprehensive examples in /test
  • Audits: Security review reports in /audits

πŸ“„ License

This project is licensed under the LicenseRef-Degensoft-SwapVM-1.1

See the LICENSE file for details. See the THIRD_PARTY_NOTICES file for information about third-party software, libraries, and dependencies used in this project.

Contact for licensing inquiries:

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

No packages published