# DEX: Router

The **router contract** in a decentralized exchange (DEX) built on Automated Market Makers (AMMs) is a critical component that facilitates user interactions with various liquidity pools. The router contract serves as a middle layer, managing complex actions such as token swaps, adding liquidity, and removing liquidity by interacting with both the factory and pair contracts.

Let’s dive into the details of the router contract’s functionality and how to test it.

## Router Contract&#x20;

In an AMM-based DEX like Uniswap, the router contract abstracts away many of the complexities involved in interacting with the pair contracts (the actual liquidity pools). The router provides users with a simpler interface for:

* **Swapping tokens**: The router handles swapping by locating the correct pool for a given token pair and executing the swap at the current price.
* **Adding liquidity**: The router allows users to provide liquidity in equal values for a token pair. It calculates the optimal amounts needed based on pool ratios.
* **Removing liquidity**: The router lets users withdraw their share of a liquidity pool along with any accrued trading fees.

For instance, Uniswap’s router contract commonly uses functions like `swapExactTokensForTokens()`, `addLiquidity()`, and `removeLiquidity()` to enable these operations.

## Router Contract Example (Pseudo Solidity Code)

Here’s a simplified example of what a router contract might look like:

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

import "./Factory.sol";
import "./Pair.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

contract Router {
    address public factory;

    constructor(address _factory) {
        factory = _factory;
    }

    // Swap function: Swaps an exact amount of one token for another
    function swapExactTokensForTokens(
        address tokenA,
        address tokenB,
        uint amountIn,
        uint amountOutMin
    ) external {
        address pairAddress = Factory(factory).getPair(tokenA, tokenB);
        require(pairAddress != address(0), "Pair does not exist");

        IERC20(tokenA).transferFrom(msg.sender, pairAddress, amountIn);
        uint amountOut = Pair(pairAddress).swap(tokenA, tokenB, amountIn);
        require(amountOut >= amountOutMin, "Insufficient output amount");

        IERC20(tokenB).transfer(msg.sender, amountOut);
    }

    // Adding liquidity function: Deposits tokens into the liquidity pool
    function addLiquidity(
        address tokenA,
        address tokenB,
        uint amountA,
        uint amountB
    ) external {
        address pairAddress = Factory(factory).getPair(tokenA, tokenB);
        require(pairAddress != address(0), "Pair does not exist");

        IERC20(tokenA).transferFrom(msg.sender, pairAddress, amountA);
        IERC20(tokenB).transferFrom(msg.sender, pairAddress, amountB);

        Pair(pairAddress).mint(msg.sender);  // Mint LP tokens for liquidity provider
    }

    // Removing liquidity function: Withdraws tokens from the liquidity pool
    function removeLiquidity(
        address tokenA,
        address tokenB
    ) external {
        address pairAddress = Factory(factory).getPair(tokenA, tokenB);
        require(pairAddress != address(0), "Pair does not exist");

        uint liquidity = Pair(pairAddress).balanceOf(msg.sender);
        Pair(pairAddress).burn(msg.sender);  // Burn LP tokens to remove liquidity

        // Transfer tokens back to user
        uint amountA = IERC20(tokenA).balanceOf(pairAddress);
        uint amountB = IERC20(tokenB).balanceOf(pairAddress);

        IERC20(tokenA).transfer(msg.sender, amountA);
        IERC20(tokenB).transfer(msg.sender, amountB);
    }
}
```

### Explanation of Key Functions

* **`swapExactTokensForTokens()`**: Swaps a specific amount of `tokenA` for `tokenB` using the pair pool. It checks if the `amountOut` is at least the `amountOutMin` to avoid slippage losses.
* **`addLiquidity()`**: Adds liquidity by transferring `tokenA` and `tokenB` from the user to the pool, then mints LP tokens to the user as a reward.
* **`removeLiquidity()`**: Removes liquidity by burning the user’s LP tokens and returning the underlying tokens to them.

## Testing the Router Contract

Testing the router contract is essential to ensure it handles swaps and liquidity management accurately. You can use **Hardhat** for this purpose.

### **Step 1: Install Dependencies and Set Up Project**

Ensure you have Node.js and npm installed. Set up Hardhat if you haven’t already:

```bash
npm install --save-dev hardhat
```

### **Step 2: Configure Your Router Contract and Tests**

Create the router contract under `contracts/Router.sol` and add tests in `test/Router.test.js`.

### **Step 3: Write Unit Tests for the Router Contract**

Here’s an example of tests for the router contract using Hardhat:

```javascript
const { expect } = require("chai");

describe("Router Contract", function () {
    let Factory, factory, Pair, pair, Router, router;
    let owner, addr1, tokenA, tokenB;

    beforeEach(async function () {
        [owner, addr1, tokenA, tokenB] = await ethers.getSigners();

        Factory = await ethers.getContractFactory("Factory");
        factory = await Factory.deploy();
        await factory.deployed();

        Router = await ethers.getContractFactory("Router");
        router = await Router.deploy(factory.address);
        await router.deployed();

        await factory.createPair(tokenA.address, tokenB.address);
        const pairAddress = await factory.getPair(tokenA.address, tokenB.address);
        Pair = await ethers.getContractFactory("Pair");
        pair = Pair.attach(pairAddress);
    });

    it("Should swap tokens", async function () {
        await router.addLiquidity(tokenA.address, tokenB.address, 1000, 1000);

        await tokenA.approve(router.address, 500);
        await router.swapExactTokensForTokens(tokenA.address, tokenB.address, 500, 450);

        const tokenBBalance = await tokenB.balanceOf(owner.address);
        expect(tokenBBalance).to.be.at.least(450);
    });

    it("Should add liquidity", async function () {
        await tokenA.approve(router.address, 1000);
        await tokenB.approve(router.address, 1000);
        await router.addLiquidity(tokenA.address, tokenB.address, 1000, 1000);

        const pairBalance = await pair.balanceOf(owner.address);
        expect(pairBalance).to.be.greaterThan(0);
    });

    it("Should remove liquidity", async function () {
        await tokenA.approve(router.address, 1000);
        await tokenB.approve(router.address, 1000);
        await router.addLiquidity(tokenA.address, tokenB.address, 1000, 1000);

        await router.removeLiquidity(tokenA.address, tokenB.address);

        const tokenABalance = await tokenA.balanceOf(owner.address);
        const tokenBBalance = await tokenB.balanceOf(owner.address);

        expect(tokenABalance).to.be.greaterThan(0);
        expect(tokenBBalance).to.be.greaterThan(0);
    });
});
```

### Explanation of Tests

* **Swapping Tokens**: This test ensures `swapExactTokensForTokens()` works correctly, performing a swap and confirming the output meets the `amountOutMin`.
* **Adding Liquidity**: Tests the `addLiquidity()` function, verifying that LP tokens are issued to the user after providing liquidity.
* **Removing Liquidity**: Ensures `removeLiquidity()` returns the tokens to the user’s account, demonstrating that they can withdraw their liquidity successfully.

### Running Tests

1. **Compile Contracts**:

   ```bash
   npx hardhat compile
   ```
2. **Run Tests**:

   ```bash
   npx hardhat test
   ```

### Additional Test Scenarios

To enhance test coverage:

* **Slippage Check**: Verify that swaps revert if the output is less than `amountOutMin`.
* **Gas Efficiency**: Analyze gas usage to optimize functions for real-world deployments.
* **Event Emission**: Confirm that events are correctly emitted for tracking transactions and pool modifications.

Testing your router contract thoroughly ensures smooth interactions with the liquidity pools, covering both liquidity management and token swaps accurately.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://abc-71.gitbook.io/curriculum/week-5/dex-router.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
