Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

README.md

8.87 Kb · 267 lines

Router

Swap routing engine for optimal trade execution across pools.

Overview

Router handles swap execution across multiple pools, finding optimal paths and managing slippage protection for traders.

Configuration

  • Router Fee: 15 bps (0.15%) by default on output tokens; configurable by admin/governance from 0 through 1000 bps (0–10%)
  • Max Hops: 3 pools per route
  • Deadline Buffer: 5-30 minutes recommended for live swaps

Core Functions

ExactInSwapRoute

Swaps an exact input amount for output, subject to a minimum net output.

  • Fixed input, variable output
  • The returned output is after the router fee
  • Reverts if output < amountOutMin
  • Supports multi-hop routing

ExactOutSwapRoute

Swaps for a requested final user output amount with maximum input. The amountOut target is post-router-fee: the router requests the corresponding gross pool output, deducts the fee, then validates and transfers the net output.

  • With no single-hop price limit, targets the requested post-fee output within the implementation's small per-hop rounding tolerance
  • A nonzero single-hop price limit may stop early and return a partial output
  • Reverts if input > amountInMax
  • Calculates path backwards

DrySwapRoute

Simulates a swap without execution.

  • Frontend price quotes
  • Slippage calculation
  • Path validation
  • No deadline check or token transfer

Technical Details

Route Format vs Pool Format - IMPORTANT DISTINCTION

Route Format (Swap Direction)

Routes in the router follow swap direction ordering: tokenIn:tokenOut:fee

  • First token = Input token (what you're swapping FROM)
  • Second token = Output token (what you're swapping TO)
  • This represents the actual flow of the swap

Example for swapping GNS to WUGNOT:

gno.land/r/gnoswap/gns.GNS:gno.land/r/gnoland/wugnot.wugnot:3000

Pool Format (Alphabetical)

Pools are identified using alphabetical ordering: token0:token1:fee

  • token0 < token1 (lexicographically sorted)
  • This is the canonical pool identifier

Example pool identifier (same pool as above):

gno.land/r/gnoland/wugnot.wugnot:gno.land/r/gnoswap/gns.GNS:3000  # gnoland/wugnot < gnoswap/gns alphabetically

Key Difference

  • Router routes: Follow your swap direction (BAR→BAZ means bar:baz in route)
  • Pool identifiers: Always alphabetically sorted (might be bar:baz or baz:bar)
  • The router automatically handles the conversion between these formats

Native Token Route Specification

IMPORTANT: Router swap functions do not accept native ugnot directly.

  • Token Parameters: Use token keys (pkgPath.SYMBOL) such as "gno.land/r/gnoland/wugnot.wugnot"
  • Route Paths: Also use token keys (pkgPath.SYMBOL) such as "gno.land/r/gnoland/wugnot.wugnot"

This matches the current implementation:

  • Pools operate on token contract paths, including wrapped GNOT (wugnot)
  • Router swap entrypoints reject native-coin handling
  • Native-token refund and unwrap flows are not part of the current router implementation

Route String Format

Single-hop format:

tokenIn:tokenOut:fee

Multi-hop format (using POOL separator):

tokenIn:tokenB:fee1*POOL*tokenB:tokenC:fee2*POOL*tokenC:tokenOut:fee3

Single-hop example:

# Swapping GNS to WUGNOT
Route: gno.land/r/gnoswap/gns.GNS:gno.land/r/gnoland/wugnot.wugnot:3000
# Router interprets: tokenIn=gns, tokenOut=wugnot, fee=3000

Multi-hop example (GNS → WUGNOT → TOKEN_C):

# Each segment follows swap direction, connected by *POOL*
gno.land/r/gnoswap/gns.GNS:gno.land/r/gnoland/wugnot.wugnot:3000*POOL*gno.land/r/gnoland/wugnot.wugnot:gno.land/r/<namespace>/token_c.TOKEN_C:500

Quote Distribution

Split large trades across routes to minimize impact:

  • quoteArr: positive percentage per route, with one quote for each route
  • Quotes must sum to 100; at most 7 routes are accepted
  • Example: "30,70" = 30% route1, 70% route2

Native Token Handling

The current router implementation does not handle native ugnot directly. It rejects native-coin handling and routes swaps only through token keys (pkgPath.SYMBOL) such as wrapped GNOT (wugnot).

Token Identifier Requirements

  • Use token keys (pkgPath.SYMBOL) such as gno.land/r/gnoland/wugnot.wugnot for both inputs/outputs and route specifications.
  • Do not pass "ugnot" as inputToken or outputToken to router swap functions.

Approval and Transfer Requirements

  • Approve the router to spend the token contract you are swapping from.
  • If you want wrapped GNOT exposure, use the wugnot token contract path directly.
  • Native-token refund and unwrap flows are not part of the current router implementation.

For live liquidity-changing swaps:

  • Set amountOutMin = expected * (1 - slippage%)
  • 0.5-1% for stable pairs
  • 1-3% for volatile pairs
  • Reverts if the net output is below the minimum

Usage

These snippets call the public domain proxy from a realm function with a current cur token. Import the proxy package and qualify its function names in integrating code.

Basic Token Swaps

 1// Simple exact input swap
 2amountIn, amountOut := ExactInSwapRoute(
 3    cross(cur),
 4    "gno.land/r/gnoswap/gns.GNS",       // input token
 5    "gno.land/r/gnoland/wugnot.wugnot", // output token
 6    "1000000",                 // amount (6 decimals)
 7    "gno.land/r/gnoswap/gns.GNS:gno.land/r/gnoland/wugnot.wugnot:3000", // route
 8    "100",                     // 100% through route
 9    "950000",                  // min output
10    time.Now().Unix() + 300,   // deadline
11    "g1referrer...",           // referral
12)
13
14// Multi-hop swap
15ExactInSwapRoute(
16    cross(cur),
17    "gno.land/r/gnoswap/gns.GNS",
18    "gno.land/r/<namespace>/token_c.TOKEN_C",
19    "1000000",
20    "gno.land/r/gnoswap/gns.GNS:gno.land/r/gnoland/wugnot.wugnot:3000*POOL*gno.land/r/gnoland/wugnot.wugnot:gno.land/r/<namespace>/token_c.TOKEN_C:3000",
21    "100",
22    "900000",
23    deadline,
24    "",
25)
26
27// Split route for large trades
28ExactInSwapRoute(
29    cross(cur),
30    "gno.land/r/gnoswap/gns.GNS",
31    "gno.land/r/gnoland/wugnot.wugnot",
32    "10000000000",
33    "gno.land/r/gnoswap/gns.GNS:gno.land/r/gnoland/wugnot.wugnot:500,gno.land/r/gnoswap/gns.GNS:gno.land/r/gnoland/wugnot.wugnot:3000",
34    "60,40",  // 60% through 0.05%, 40% through 0.3%
35    "9500000000",
36    deadline,
37    "",
38)

Single-hop functions support partial execution through a nonzero sqrtPriceLimitX96:

 1// Partial swap with price limit - may not consume full input amount
 2amountIn, amountOut := ExactInSingleSwapRoute(
 3    cross(cur),
 4    "gno.land/r/gnoswap/gns.GNS",       // input token
 5    "gno.land/r/gnoland/wugnot.wugnot", // output token
 6    "1000000",                 // max amount to swap
 7    "gno.land/r/gnoswap/gns.GNS:gno.land/r/gnoland/wugnot.wugnot:3000", // single route
 8    "950000",                  // min output
 9    "1000000000000000000",     // sqrtPriceLimitX96 (price limit)
10    deadline,
11    "",
12)
13// If the price limit is reached, only a partial amount is swapped. For exact-in
14// this can consume less input; exact-out can deliver less than its target.
15// amountOutMin or amountInMax remains enforced, respectively.

Important Developer Notes

Common Integration Pitfalls

  1. Native Token Assumptions: Passing "ugnot" to router swap functions will fail because router entrypoints reject native-coin handling.

  2. Route vs Token Identifier Confusion: Using "ugnot" in route strings instead of "gno.land/r/gnoland/wugnot.wugnot" will cause transactions to fail since no pools exist for the "ugnot" identifier.

  3. Wrong Token Path:

    • Use gno.land/r/gnoland/wugnot.wugnot when swapping wrapped GNOT
    • Do not pass native ugnot to router swap functions
    • Route strings must stay in swap-direction order and use token contract paths

Frontend Integration Checklist

  • Implement WUGNOT approval before wrapped-GNOT swaps
  • Use token keys (pkgPath.SYMBOL) such as "gno.land/r/gnoland/wugnot.wugnot" for both parameters and routes
  • Test both partial and full swap scenarios
  • Implement proper error handling for failed approvals

Both single-hop functions support partial execution when sqrtPriceLimitX96 is nonzero:

  • Exact-in may consume less than the specified input amount
  • Exact-out may deliver less than the requested post-fee output
  • The relevant amount limit (amountOutMin or amountInMax) still applies
  • Remaining input tokens stay with the user because the router uses token contract transfers
  • A zero limit uses the global tick-math boundary and preserves full exact semantics

Security

  • Path validation checks syntax, endpoints, hop continuity, and pool existence; it does not reject circular routes
  • Deadline prevents stale live transactions
  • Slippage limits protect against unfavorable execution
  • The router fee rate is configurable; the current rate is fixed during one execution
  • WUGNOT approval requirement prevents unauthorized token transfers