# PropSwapRouter Integration Guide

This guide explains how to integrate with the PropSwapRouter contract to get quotes and execute token swaps in the Wasabi Prop AMM system.

## Overview

The `PropSwapRouter` is the main entry point for swapping tokens. It:

* Routes swaps through underlying `PropPool` contracts
* Uses USDC as the common base pair (quote token)
* Supports both **exact-input** and **exact-output** swap modes
* Provides slippage protection and deadline validation

### Contract Addresses

| Chain Name   | Chain ID | PropSwapRouter                               | PropPoolFactory                              |
| ------------ | -------- | -------------------------------------------- | -------------------------------------------- |
| Base Sepolia | 84532    | `0x6bd7186801a50c2158c0c9ac5fd8bebd419212fe` | `0x877c68779d0fef5458515c98f13355ca3780a486` |
| Base         | 8453     | `0xfc81dfde25083a286723b7c9dd7213f8723369fe` | `0x851fc799c9f1443a2c1e6b966605a80f8a1b1bf2` |

***

## Discovering Pools

The `PropPoolFactory` provides functions to discover which tokens are supported and get pool addresses.

### getListedTokens

Returns all tokens that have a pool in the system.

```solidity
function getListedTokens() external view returns (IERC20[] memory);
```

### getPropPool

Returns the pool address for a given token.

```solidity
function getPropPool(IERC20 token) external view returns (IPropPool);
```

**Example (Solidity):**

```solidity
IPropPoolFactory factory = IPropPoolFactory(0x851fc799c9f1443a2c1e6b966605a80f8a1b1bf2);

// Get all supported tokens
IERC20[] memory tokens = factory.getListedTokens();

// Get the pool address for a specific token
IPropPool wethPool = factory.getPropPool(IERC20(weth));
```

**Example (TypeScript/ethers):**

```typescript
const FACTORY_ABI = [
  "function getListedTokens() view returns (address[])",
  "function getPropPool(address token) view returns (address)"
];

const factory = new ethers.Contract(factoryAddress, FACTORY_ABI, provider);

const tokens = await factory.getListedTokens();
const wethPool = await factory.getPropPool(wethAddress);
```

Each pool holds reserves of one base token (e.g. WETH) and USDC as the quote token. You can inspect pool state via `getReserves()`, `getBaseToken()`, and `getQuoteToken()` on the `IPropPool` interface.

***

## Getting Quotes

Quote functions are **view functions** that estimate swap amounts without executing a transaction. Always call these before swapping to determine expected output or required input.

### quoteExactInput

Calculate how much output you'll receive for a given input amount.

```solidity
function quoteExactInput(
    address tokenIn,
    address tokenOut,
    uint256 amountIn
) external view returns (uint256 amountOut);
```

**Parameters:**

| Name       | Type    | Description                           |
| ---------- | ------- | ------------------------------------- |
| `tokenIn`  | address | Token to sell                         |
| `tokenOut` | address | Token to receive                      |
| `amountIn` | uint256 | Amount of input token (in base units) |

**Returns:** Expected output amount in base units

**Example:**

```solidity
// How much USDC will I get for 1 WETH?
uint256 expectedUsdc = propSwapRouter.quoteExactInput(
    address(weth),    // token in
    address(usdc),    // token out
    1e18              // 1 WETH (18 decimals)
);
// Returns amount in USDC base units (6 decimals)
```

### quoteExactOutput

Calculate how much input is needed for a desired output amount.

```solidity
function quoteExactOutput(
    address tokenIn,
    address tokenOut,
    uint256 amountOut
) external view returns (uint256 amountIn);
```

**Parameters:**

| Name        | Type    | Description                           |
| ----------- | ------- | ------------------------------------- |
| `tokenIn`   | address | Token to sell                         |
| `tokenOut`  | address | Token to receive                      |
| `amountOut` | uint256 | Desired output amount (in base units) |

**Returns:** Required input amount in base units

**Example:**

```solidity
// How much WETH must I pay to get exactly 5000 USDC?
uint256 requiredWeth = propSwapRouter.quoteExactOutput(
    address(weth),    // token in
    address(usdc),    // token out
    5000e6            // 5000 USDC (6 decimals)
);
// Returns amount in WETH base units (18 decimals)
```

***

## Executing Swaps

### Prerequisites

1. **Approve tokens**: The router must be approved to spend your input tokens
2. **Authorization**: Your address must be an authorized swapper (check with the factory admin)
3. **Sufficient balance**: Ensure you have enough input tokens

### swapExactInput

Swap a fixed input amount for variable output (with minimum output protection).

```solidity
function swapExactInput(
    address tokenIn,
    address tokenOut,
    uint256 amountIn,
    uint256 minAmountOut,
    address recipient,
    uint256 deadline
) external returns (uint256 amountOut);
```

**Parameters:**

| Name           | Type    | Description                                     |
| -------------- | ------- | ----------------------------------------------- |
| `tokenIn`      | address | Token to sell                                   |
| `tokenOut`     | address | Token to receive                                |
| `amountIn`     | uint256 | Amount of input token to swap                   |
| `minAmountOut` | uint256 | Minimum acceptable output (slippage protection) |
| `recipient`    | address | Address to receive output tokens                |
| `deadline`     | uint256 | Unix timestamp after which the swap reverts     |

**Returns:** Actual amount of output tokens received

**Example:**

```solidity
// Approve router to spend WETH
IERC20(weth).approve(address(propSwapRouter), 1e18);

// Get quote first
uint256 expectedOut = propSwapRouter.quoteExactInput(
    address(weth),
    address(usdc),
    1e18
);

// Execute swap with 1% slippage tolerance
uint256 amountOut = propSwapRouter.swapExactInput(
    address(weth),              // sell WETH
    address(usdc),              // for USDC
    1e18,                       // 1 WETH
    expectedOut * 99 / 100,     // 1% slippage tolerance
    msg.sender,                 // receive to my address
    block.timestamp + 1 hours   // valid for 1 hour
);
```

### swapExactOutput

Swap variable input for a fixed output amount (with maximum input protection).

```solidity
function swapExactOutput(
    address tokenIn,
    address tokenOut,
    uint256 amountOut,
    uint256 maxAmountIn,
    address recipient,
    uint256 deadline
) external returns (uint256 amountIn);
```

**Parameters:**

| Name          | Type    | Description                                        |
| ------------- | ------- | -------------------------------------------------- |
| `tokenIn`     | address | Token to sell                                      |
| `tokenOut`    | address | Token to receive                                   |
| `amountOut`   | uint256 | Exact amount of output tokens desired              |
| `maxAmountIn` | uint256 | Maximum input willing to pay (slippage protection) |
| `recipient`   | address | Address to receive output tokens                   |
| `deadline`    | uint256 | Unix timestamp after which the swap reverts        |

**Returns:** Actual amount of input tokens used (unused tokens are refunded)

**Example:**

```solidity
// Get quote first
uint256 expectedIn = propSwapRouter.quoteExactOutput(
    address(usdc),
    address(weth),
    1e18  // want exactly 1 WETH
);

// Approve with slippage buffer
uint256 maxIn = expectedIn * 101 / 100;  // 1% slippage
IERC20(usdc).approve(address(propSwapRouter), maxIn);

// Execute swap
uint256 amountIn = propSwapRouter.swapExactOutput(
    address(usdc),              // pay with USDC
    address(weth),              // receive WETH
    1e18,                       // exactly 1 WETH
    maxIn,                      // max USDC willing to pay
    msg.sender,                 // receive to my address
    block.timestamp + 1 hours   // valid for 1 hour
);
// Unused USDC is automatically refunded
```

***

## Swap Routing

The router automatically determines the optimal path:

| tokenIn | tokenOut | Path                               |
| ------- | -------- | ---------------------------------- |
| USDC    | Token    | Single hop: USDC → Token           |
| Token   | USDC     | Single hop: Token → USDC           |
| Token A | Token B  | Two hops: Token A → USDC → Token B |

Multi-hop swaps incur fees on each leg.

***

## Error Handling

The router reverts with specific errors:

```solidity
error InvalidToken(address token);           // Token not supported
error InvalidAmount();                       // Zero amount provided
error InvalidRecipient();                    // Recipient is zero address
error SwapExpired();                         // Deadline has passed
error InsufficientOutput(uint256 actual, uint256 minimum);  // Slippage exceeded
error ExcessiveInput(uint256 actual, uint256 maximum);      // Input exceeds max
```

**Handling errors:**

```solidity
try propSwapRouter.swapExactInput(
    tokenIn, tokenOut, amountIn, minAmountOut, recipient, deadline
) returns (uint256 amountOut) {
    // Swap succeeded
} catch Error(string memory reason) {
    // Handle string error
} catch (bytes memory lowLevelData) {
    // Decode custom error
    // e.g., InsufficientOutput, SwapExpired, etc.
}
```

***

## Complete Integration Example

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IPropSwapRouter} from "./interfaces/IPropSwapRouter.sol";

contract SwapIntegration {
    IPropSwapRouter public immutable router;
    address public immutable usdc;

    constructor(address _router, address _usdc) {
        router = IPropSwapRouter(_router);
        usdc = _usdc;
    }

    /// @notice Swap tokens with automatic quote and slippage protection
    /// @param tokenIn Input token address
    /// @param tokenOut Output token address
    /// @param amountIn Amount to swap
    /// @param slippageBps Slippage tolerance in basis points (100 = 1%)
    /// @return amountOut Amount received
    function swap(
        address tokenIn,
        address tokenOut,
        uint256 amountIn,
        uint256 slippageBps
    ) external returns (uint256 amountOut) {
        // Transfer tokens from user
        IERC20(tokenIn).transferFrom(msg.sender, address(this), amountIn);

        // Approve router
        IERC20(tokenIn).approve(address(router), amountIn);

        // Get quote
        uint256 expectedOut = router.quoteExactInput(tokenIn, tokenOut, amountIn);

        // Calculate minimum with slippage
        uint256 minOut = expectedOut * (10000 - slippageBps) / 10000;

        // Execute swap
        amountOut = router.swapExactInput(
            tokenIn,
            tokenOut,
            amountIn,
            minOut,
            msg.sender,  // send directly to user
            block.timestamp + 20 minutes
        );
    }

    /// @notice Get quote for a swap
    function getQuote(
        address tokenIn,
        address tokenOut,
        uint256 amountIn
    ) external view returns (uint256) {
        return router.quoteExactInput(tokenIn, tokenOut, amountIn);
    }
}
```

***

## TypeScript/Ethers.js Integration

```typescript
import { ethers } from "ethers";

const ROUTER_ABI = [
  "function quoteExactInput(address tokenIn, address tokenOut, uint256 amountIn) view returns (uint256)",
  "function quoteExactOutput(address tokenIn, address tokenOut, uint256 amountOut) view returns (uint256)",
  "function swapExactInput(address tokenIn, address tokenOut, uint256 amountIn, uint256 minAmountOut, address recipient, uint256 deadline) returns (uint256)",
  "function swapExactOutput(address tokenIn, address tokenOut, uint256 amountOut, uint256 maxAmountIn, address recipient, uint256 deadline) returns (uint256)"
];

const ERC20_ABI = [
  "function approve(address spender, uint256 amount) returns (bool)",
  "function allowance(address owner, address spender) view returns (uint256)"
];

async function swapExactInput(
  signer: ethers.Signer,
  routerAddress: string,
  tokenIn: string,
  tokenOut: string,
  amountIn: bigint,
  slippagePercent: number = 1
) {
  const router = new ethers.Contract(routerAddress, ROUTER_ABI, signer);
  const inputToken = new ethers.Contract(tokenIn, ERC20_ABI, signer);

  // Get quote
  const expectedOut = await router.quoteExactInput(tokenIn, tokenOut, amountIn);
  console.log(`Expected output: ${expectedOut}`);

  // Calculate minimum with slippage
  const minOut = expectedOut * BigInt(100 - slippagePercent) / 100n;

  // Approve if needed
  const signerAddress = await signer.getAddress();
  const allowance = await inputToken.allowance(signerAddress, routerAddress);
  if (allowance < amountIn) {
    const approveTx = await inputToken.approve(routerAddress, amountIn);
    await approveTx.wait();
  }

  // Execute swap
  const deadline = Math.floor(Date.now() / 1000) + 3600; // 1 hour
  const tx = await router.swapExactInput(
    tokenIn,
    tokenOut,
    amountIn,
    minOut,
    signerAddress,
    deadline
  );

  const receipt = await tx.wait();
  console.log(`Swap executed in tx: ${receipt.hash}`);

  return receipt;
}
```

***

## Best Practices

1. **Always get a quote first** - Quotes help you set appropriate slippage limits
2. **Use reasonable deadlines** - 10-30 minutes for manual swaps, shorter for bots
3. **Set appropriate slippage**:
   * 0.5-1% for stable pairs
   * 1-3% for volatile pairs
   * Higher for large trades relative to pool liquidity
4. **Check token decimals** - USDC uses 6 decimals, most tokens use 18
5. **Handle refunds** - For `swapExactOutput`, unused input tokens are automatically refunded
6. **Monitor events** - Listen for `SwapExactInput` and `SwapExactOutput` events for confirmations

***

## Events

```solidity
event SwapExactInput(
    address indexed sender,
    address indexed recipient,
    address tokenIn,
    address tokenOut,
    uint256 amountIn,
    uint256 amountOut
);

event SwapExactOutput(
    address indexed sender,
    address indexed recipient,
    address tokenIn,
    address tokenOut,
    uint256 amountIn,
    uint256 amountOut
);
```

***

## Support

For questions or issues, contact Wasabi via Telegram (if we already have a chat open) or open a ticket in our Discord.

Discord: <https://discord.gg/A9xZuxdRKP>
