Search Apps Documentation Source Content File Folder Download Copy Actions Download

README.md

11.44 Kb · 360 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: 0.15% on all swaps
  • Max Hops: 3 pools per route
  • Deadline Buffer: 5-30 minutes recommended

Core Functions

ExactInSwapRoute

Swaps exact input amount for minimum output.

  • Fixed input, variable output
  • Reverts if output < amountOutMin
  • Supports multi-hop routing

ExactOutSwapRoute

Swaps for exact output amount with maximum input.

  • Fixed output, variable input
  • Reverts if input > amountInMax
  • Calculates path backwards

DrySwapRoute

Simulates swap without execution.

  • Frontend price quotes
  • Slippage calculation
  • Gas estimation
  • Path validation

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 BAR to BAZ:

gno.land/r/demo/bar:gno.land/r/demo/baz: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/demo/bar:gno.land/r/demo/baz:3000  # if bar < baz 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: When using native GNOT tokens, there's a critical distinction between token identifiers and route paths:

  • Token Parameters: Use "ugnot" for inputToken and outputToken parameters
  • Route Paths: Must use "gno.land/r/gnoland/wugnot" in route strings

This dual-identifier system exists because:

  • Pools only operate on wrapped tokens (WUGNOT)
  • Router functions accept native token identifiers for user convenience
  • Internal processing automatically converts between native and wrapped forms

Correct Usage Example:

 1// CORRECT: inputToken="ugnot", route uses wugnot path
 2ExactInSwapRoute(
 3    "ugnot",                                    // input token identifier
 4    "gno.land/r/demo/bar",                    // output token
 5    "1000000",
 6    "gno.land/r/gnoland/wugnot:gno.land/r/demo/bar:3000", // route uses wugnot
 7    "100", "950000", deadline, ""
 8)
 9
10// INCORRECT: using "ugnot" in route will fail
11ExactInSwapRoute(
12    "ugnot", "gno.land/r/demo/bar", "1000000",
13    "ugnot:gno.land/r/demo/bar:3000",          // Wrong: pools don't exist for "ugnot"
14    "100", "950000", deadline, ""
15)

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 BAR to BAZ
Route: gno.land/r/demo/bar:gno.land/r/demo/baz:3000
# Router interprets: tokenIn=bar, tokenOut=baz, fee=3000

Multi-hop example (BAR → BAZ → QUX):

# Each segment follows swap direction, connected by *POOL*
gno.land/r/demo/bar:gno.land/r/demo/baz:3000*POOL*gno.land/r/demo/baz:gno.land/r/demo/qux:500

Quote Distribution

Split large trades across routes to minimize impact:

  • quoteArr: Percentage per route (must sum to 100)
  • Example: "30,70" = 30% route1, 70% route2

GNOT Handling

The Router automatically handles native GNOT token operations through wrapping/unwrapping mechanisms:

Token Identifier Requirements

  • Input Token: Use "ugnot" to specify native GNOT as input token
  • Output Token: Use "ugnot" to specify native GNOT as output token
  • Routes: Must always use wrapped token path "gno.land/r/gnoland/wugnot" in route specifications

Native Token Send Requirements

When using native GNOT tokens, you must send the appropriate amount of native ugnot with your function call:

  • ExactInSwapRoute: Send exactly amountIn amount of ugnot
  • ExactInSingleSwapRoute: Send exactly amountIn amount of ugnot
  • ExactOutSwapRoute: Send exactly amountInMax amount of ugnot
  • ExactOutSingleSwapRoute: Send exactly amountInMax amount of ugnot

WUGNOT Approval Requirements for Refunds

1// Before calling router functions with native tokens
2wugnot.Approve(cross, routerAddress, maxAmount)

This approval is required because:

  • Router wraps native GNOT to WUGNOT for internal processing
  • Unused GNOT is refunded by transferring WUGNOT from user and unwrapping it
  • The unwrapWithTransferFrom function requires WUGNOT transfer approval

Refund Logic

  • ExactIn Functions: Unused GNOT is automatically refunded after swap
  • ExactOut Functions: Excess GNOT (difference between amountInMax and actual input used) is refunded
  • Refunds use unwrapWithTransferFrom which requires prior WUGNOT approval

Example with Native GNOT

 1// 1. First approve WUGNOT spending for potential refunds
 2wugnot.Approve(cross, routerAddress, 1000000) // Approve max amount
 3
 4// 2. Call swap function with native GNOT, sending ugnot
 5// Note: inputToken="ugnot" but route uses wugnot path
 6amountIn, amountOut := ExactInSwapRoute(
 7    "ugnot",                                    // input token (native)
 8    "gno.land/r/demo/bar",                    // output token
 9    "1000000",                                // amount (send this much ugnot)
10    "gno.land/r/gnoland/wugnot:gno.land/r/demo/bar:3000", // route uses wugnot
11    "100",                                    // 100% through route
12    "950000",                                 // min output
13    time.Now().Unix() + 300,                  // deadline
14    "",                                       // no referrer
15)
16// Any unused GNOT will be automatically refunded

Slippage Protection

  • Set amountOutMin = expected * (1 - slippage%)
  • 0.5-1% for stable pairs
  • 1-3% for volatile pairs
  • Reverts if exceeded

Usage

Basic Token Swaps

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

Single Swap with Partial Execution

Single swap functions support partial execution through price limits:

 1// Partial swap with price limit - may not consume full input amount
 2amountIn, amountOut := ExactInSingleSwapRoute(
 3    "gno.land/r/demo/bar",     // input token
 4    "gno.land/r/demo/baz",     // output token
 5    "1000000",                 // max amount to swap
 6    "gno.land/r/demo/bar:gno.land/r/demo/baz:3000", // single route
 7    "950000",                  // min output
 8    "1000000000000000000",     // sqrtPriceLimitX96 (price limit)
 9    deadline,
10    "",
11)
12// If price limit is reached, only partial amount is swapped
13// Remaining input tokens stay with user (no refund needed for GRC20 tokens)

Native GNOT Swaps with Refunds

When using native GNOT, automatic refunds handle unused amounts:

 1// STEP 1: Approve WUGNOT for potential refunds
 2wugnot.Approve(cross, routerAddress, 2000000) // Approve more than needed
 3
 4// STEP 2: ExactIn with native GNOT (send exactly amountIn)
 5amountIn, amountOut := ExactInSwapRoute(
 6    "ugnot",                    // native input
 7    "gno.land/r/demo/bar",     // output token
 8    "1000000",                 // send this amount of ugnot with call
 9    "gno.land/r/gnoland/wugnot:gno.land/r/demo/bar:3000",
10    "100", "950000", deadline, ""
11)
12// Any unused GNOT automatically refunded
13
14// STEP 3: ExactOut with native GNOT (send amountInMax)
15amountIn, amountOut := ExactOutSwapRoute(
16    "ugnot",                    // native input
17    "gno.land/r/demo/bar",     // output token
18    "1000000",                 // exact output desired
19    "gno.land/r/gnoland/wugnot:gno.land/r/demo/bar:3000",
20    "100",
21    "1200000",                 // send this max amount of ugnot with call
22    deadline, ""
23)
24// Excess GNOT (1200000 - actual_input_used) automatically refunded
25
26// STEP 4: Single swap with partial execution + refund
27amountIn, amountOut := ExactInSingleSwapRoute(
28    "ugnot",                    // native input
29    "gno.land/r/demo/bar",     // output token
30    "1000000",                 // send this amount of ugnot with call
31    "gno.land/r/gnoland/wugnot:gno.land/r/demo/bar:3000",
32    "950000",
33    "1000000000000000000",     // price limit may cause partial swap
34    deadline, ""
35)
36// Unswapped GNOT due to price limit automatically refunded

Important Developer Notes

Common Integration Pitfalls

  1. WUGNOT Approval Forgotten: Most transaction failures with native GNOT occur because developers forget to approve WUGNOT spending before calling router functions.

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

  3. Incorrect Native Token Send Amount:

    • ExactIn functions: Must send exactly amountIn of native gnot
    • ExactOut functions: Must send exactly amountInMax of native gnot
    • Sending wrong amounts will cause transaction reversion
  4. Missing Refund Handling: When integrating, remember that native GNOT refunds are automatic but require prior WUGNOT approval.

Frontend Integration Checklist

  • Implement WUGNOT approval before native GNOT swaps
  • Use correct token identifiers: "ugnot" for parameters, "gno.land/r/gnoland/wugnot" for routes
  • Send correct native token amounts with function calls
  • Handle automatic refunds in UI balance updates
  • Test both partial and full swap scenarios
  • Implement proper error handling for failed approvals

Single Swap Partial Execution

The ExactInSingleSwapRoute and ExactOutSingleSwapRoute functions support partial execution when sqrtPriceLimitX96 is set. This means:

  • Swap may consume less than the specified input amount
  • Price impact is limited by the price limit parameter
  • Remaining tokens are handled automatically (refunded for native GNOT, stay with user for GRC20)
  • This is useful for large trades to prevent excessive slippage

Security

  • Path validation prevents circular routes
  • Deadline prevents stale transactions
  • Slippage limits protect against MEV
  • Router fees immutable per swap
  • WUGNOT approval requirement prevents unauthorized token transfers