Search Apps Documentation Source Content File Folder Download Copy Actions Download

/r/gnoswap/router

Directory · 8 Files
README.md Open

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