README.md

# 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**:

```go
// CORRECT: inputToken="ugnot", route uses wugnot path
ExactInSwapRoute(
    "ugnot",                                    // input token identifier
    "gno.land/r/demo/bar",                    // output token
    "1000000",
    "gno.land/r/gnoland/wugnot:gno.land/r/demo/bar:3000", // route uses wugnot
    "100", "950000", deadline, ""
)

// INCORRECT: using "ugnot" in route will fail
ExactInSwapRoute(
    "ugnot", "gno.land/r/demo/bar", "1000000",
    "ugnot:gno.land/r/demo/bar:3000",          // Wrong: pools don't exist for "ugnot"
    "100", "950000", deadline, ""
)
```

#### 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`

### 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

```go
// Simple exact input swap
amountIn, amountOut := ExactInSwapRoute(
    "gno.land/r/demo/bar",     // input token
    "gno.land/r/demo/baz",     // output token
    "1000000",                 // amount (6 decimals)
    "gno.land/r/demo/bar:gno.land/r/demo/baz:3000", // route
    "100",                     // 100% through route
    "950000",                  // min output
    time.Now().Unix() + 300,   // deadline
    "g1referrer...",           // referral
)

// Multi-hop swap
ExactInSwapRoute(
    "gno.land/r/demo/bar",
    "gno.land/r/demo/baz",
    "1000000",
    "gno.land/r/demo/bar:gno.land/r/gnoland/wugnot:3000*POOL*gno.land/r/gnoland/wugnot:gno.land/r/demo/baz:3000",
    "100",
    "900000",
    deadline,
    "",
)

// Split route for large trades
ExactInSwapRoute(
    "gno.land/r/demo/usdc",
    "ugnot",
    "10000000000",
    "gno.land/r/demo/usdc:gno.land/r/gnoland/wugnot:500,gno.land/r/demo/usdc:gno.land/r/gnoland/wugnot:3000",
    "60,40",  // 60% through 0.05%, 40% through 0.3%
    "9500000000",
    deadline,
    "",
)
```

### Single Swap with Partial Execution

Single swap functions support partial execution through price limits:

```go
// Partial swap with price limit - may not consume full input amount
amountIn, amountOut := ExactInSingleSwapRoute(
    "gno.land/r/demo/bar",     // input token
    "gno.land/r/demo/baz",     // output token
    "1000000",                 // max amount to swap
    "gno.land/r/demo/bar:gno.land/r/demo/baz:3000", // single route
    "950000",                  // min output
    "1000000000000000000",     // sqrtPriceLimitX96 (price limit)
    deadline,
    "",
)
// If price limit is reached, only partial amount is swapped
// 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:

```go
// STEP 1: Approve WUGNOT for potential refunds
wugnot.Approve(cross, routerAddress, 2000000) // Approve more than needed

// STEP 2: ExactIn with native GNOT (send exactly amountIn)
amountIn, amountOut := ExactInSwapRoute(
    "ugnot",                    // native input
    "gno.land/r/demo/bar",     // output token
    "1000000",                 // send this amount of ugnot with call
    "gno.land/r/gnoland/wugnot:gno.land/r/demo/bar:3000",
    "100", "950000", deadline, ""
)
// Any unused GNOT automatically refunded

// STEP 3: ExactOut with native GNOT (send amountInMax)
amountIn, amountOut := ExactOutSwapRoute(
    "ugnot",                    // native input
    "gno.land/r/demo/bar",     // output token
    "1000000",                 // exact output desired
    "gno.land/r/gnoland/wugnot:gno.land/r/demo/bar:3000",
    "100",
    "1200000",                 // send this max amount of ugnot with call
    deadline, ""
)
// Excess GNOT (1200000 - actual_input_used) automatically refunded

// STEP 4: Single swap with partial execution + refund
amountIn, amountOut := ExactInSingleSwapRoute(
    "ugnot",                    // native input
    "gno.land/r/demo/bar",     // output token
    "1000000",                 // send this amount of ugnot with call
    "gno.land/r/gnoland/wugnot:gno.land/r/demo/bar:3000",
    "950000",
    "1000000000000000000",     // price limit may cause partial swap
    deadline, ""
)
// 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