# Kodiak Island Router

## Overview

The IslandRouter contract serves as a helper contract for users to easily provide liquidity to Kodiak Islands. It handles the complexities of depositing tokens, slippage protection, and token swaps when needed, while ensuring optimal liquidity provision.

Kodiak Islands require liquidity providers to deposit tokens in specific ratios that match the Island's underlying Kodiak V3 position. The router provides several key protections:

* Minimum output amount protection for swaps when providing liquidity with single token.
* Deposit ratio slippage protection
* Minimum LP token (shares) protection

## Managing Liquidity

#### Token Deposit Ratio

The router handles liquidity addition by:

1. Calculating the optimal deposit amounts using `island.getMintAmounts()`
2. Ensuring the amounts meet minimum requirements
3. Transferring tokens and minting LP tokens

#### Steps to Add Liquidity

1. Approve the router to spend your tokens (both tokens or one token in case of singleSided
2. Choose the appropriate liquidity addition method based on your tokens
3. If the liquidity addition is using a single token, find the appropriate swap data and use Kodiak Quoter api to get the calldata according to the swap params.
4. Set reasonable slippage parameters
5. Execute transaction
6. Kodiak Island LP tokens sent to the receiver as passed in params
7. In case of zaps msg.sender receives back any unused token0 or token1

### Adding liquidity with both tokens

#### 1. Standard Two Token Deposit

#### Prerequisites

* Token0 and Token1 balances sufficient for desired liquidity.
* Approved router contract to spend both tokens.

```solidity
function addLiquidity(
    IKodiakIsland island,     // Address of the Kodiak Island
    uint256 amount0Max,       // Maximum amount of token0 willing to deposit
    uint256 amount1Max,       // Maximum amount of token1 willing to deposit
    uint256 amount0Min,       // Minimum acceptable token0 deposit (slippage protection)
    uint256 amount1Min,       // Minimum acceptable token1 deposit (slippage protection)
    uint256 amountSharesMin,  // Minimum IslandTokens to receive
    address receiver          // Address to receive LP tokens
) external returns (
    uint256 amount0,         // Actual token0 amount deposited
    uint256 amount1,         // Actual token1 amount deposited
    uint256 mintAmount       // LP tokens received
)
```

#### Implementation Example

<pre class="language-javascript"><code class="lang-javascript"><strong>// 1. Start with maximum amounts to deposit, for example 10 token0 and 10 token1
</strong>const amount0Max = ethers.parseUnits("10", token0Decimals);
const amount1Max = ethers.parseUnits("10", token1Decimals);

// 2. Find the appropriate ratio of tokens to deposit
(amount0Used, amount1Used, ) = await island.getMintAmounts(amount0Max, amount1Max)

amount0Max = amount0Used
amount1Max = amount1Used

// 3. Set slippage tolerance for the minimum amounts of token0 and token1 
// (e.g. 1% slippage). This means that atleast 99% of these tokens should 
// be deposited in the pool. This gives you a protection from the pool price
// deviating a lot before your transaction goes through
//  which affects the tokens and ratio they are deposited in
const amount0Min = amount0Max.mul(99).div(100);
const amount1Min = amount1Max.mul(99).div(100);
const amountSharesMin = 0; // Set based on expected shares

// 4. Approve router
await token0.approve(routerAddress, amount0Max);
await token1.approve(routerAddress, amount1Max);

// 5. Callstatic deposit to get the amountShares minted
const simulationResult = await router.callStatic.addLiquidity(
    islandAddress,
    amount0Max,
    amount1Max,
    amount0Min,
    amount1Min,
    amountSharesMin,
    receiverAddress
);

// 6. Mint Island tokens, Use the mintAmount from above
// add a comfortable slippage (ex 1%) to this and use this for amountSharesMin
// Use BPS for higher precision.
amountSharesMin = simulationResult[2]
       .mul(99).div(100).toString()

<strong>const tx = await router.addLiquidity(
</strong>    islandAddress,
    amount0Max,
    amount1Max,
    amount0Min,
    amount1Min,
    amountSharesMin,
    receiverAddress
);

</code></pre>

#### 2. Native BERA + Token Deposit

> It is important that one of the tokens in the underlying pool is WBERA for depositing with native BERA.

#### Prerequisites

* Sender must have sufficient NativeToken and Token1 balances for desired liquidity.
* Approve router contract to spend Token1 tokens.
* Send required bera as msg.value&#x20;

```solidity
// Assuming token0 is WBERA
function addLiquidityNative(
    IKodiakIsland island,     // Address of the Kodiak Island
    uint256 amount0Max,       // Maximum BERA amount
    uint256 amount1Max,       // Maximum token amount
    uint256 amount0Min,       // Minimum BERA deposit
    uint256 amount1Min,       // Minimum token deposit
    uint256 amountSharesMin,  // Minimum LP tokens to receive
    address receiver          // Address to receive LP tokens
) external payable returns (
    uint256 amount0,         // Actual BERA amount deposited
    uint256 amount1,         // Actual token amount deposited
    uint256 mintAmount       // LP tokens received
)
```

Implementation is same as above except for sending native token BERA as msg.value

<pre class="language-javascript"><code class="lang-javascript"><strong>const tx = await router.addLiquidityNative(
</strong>    islandAddress,
    amount0Max,
    amount1Max,
    amount0Min,
    amount1Min,
    amountSharesMin,
    receiverAddress,
    {
        value: amount0Max // assuming island.token0() is WBERA
    }
);
</code></pre>

### Adding Liquidity with a single token

When depositing into a concentrated liquidity position like an Island, it's crucial to understand that the underlying tokens need to be in a specific ratio to maximize the value of the position. If a user wants to deposit a single token, a swap is required to balance the tokens before adding liquidity to the position. This process involves calculating the ideal amount of the input token to swap for the other token in the pair.

The IslandRouter uses a external swap Routers to swap this token to achieve maximal efficiency during the swap. The kodiak router is whitelisted to begin with and other routers will be whitelisted later to further increase this swap efficiency.

#### Prerequisites

* Sufficient balance of input token or native token.
* Approved router for input token amount
* Understanding on how to find the swap params such that after the swap the token0 and token1 are in correct ratio to deposit into the island
* Understanding on how to generate swap calldata using Kodiak Router.

#### Single Token Deposit Implementation

```solidity
function addLiquiditySingle(
    IKodiakIsland island,              // Island address
    uint256 totalAmountIn,             // Total input token amount
    uint256 amountSharesMin,           // Minimum LP tokens to receive
    uint256 maxStakingSlippageBPS,     // Max slippage in basis points (100 = 1%)
    RouterSwapParams calldata swapData, // Swap parameters for converting portion of input
    address receiver                    // LP token recipient
) external returns (
    uint256 amount0,                   // Final token0 amount deposited
    uint256 amount1,                   // Final token1 amount deposited
    uint256 mintAmount                 // LP tokens received
)

struct RouterSwapParams {
    bool zeroForOne;        // Swap direction
    uint256 amountIn;       // Amount to swap
    uint256 minAmountOut;   // Minimum output from swap
    bytes routeData;        // Encoded swap route data
}
```

#### Implementation Example

```javascript
// 1. Prepare swap parameters. refer the section on finding the swap amounts
const swapParams = {
    zeroForOne: true, // true if swapping token0 for token1
    amountIn: swapAmount,
    minAmountOut: minimumSwapOutput,
    routeData: swapCalldata
};

// 2. Set slippage parameters
const maxStakingSlippageBPS = 100; // 1% slippage
const minShares = minShares; // find the min shares by making a static call 
// and adding a 1% slippage to it as demonstrated in previous examples

// 3. Approve island router to spend your inputToken
await token0.approve(islandRouterAddress, totalAmount);

// 3. Execute single token deposit
const tx = await islandRouter.addLiquiditySingle(
    islandAddress,
    totalAmount,
    minShares,
    maxStakingSlippageBPS,
    swapParams,
    receiverAddress
);
```

### Adding Liquidity with Native BERA

When you want to provide liquidity to an island with native token BERA.

#### Prerequisites

* Native BERA balance
* Island must have WBERA as one of its tokens

#### Single Native Token Deposit

```solidity
function addLiquiditySingleNative(
    IKodiakIsland island,              // Island address (must include WBERA)
    uint256 amountSharesMin,           // Minimum LP tokens to receive
    uint256 maxStakingSlippageBPS,     // Max slippage in basis points
    RouterSwapParams calldata swapData, // Swap parameters
    address receiver                    // LP token recipient
) external payable returns (
    uint256 amount0,                   // Final token0 amount deposited
    uint256 amount1,                   // Final token1 amount deposited
    uint256 mintAmount                 // LP tokens received
)
```

#### Implementation Example

```typescript
// 1. Calculate BERA amount to send
const beraAmount = ethers.parseEther("1.0");

// 2. Prepare swap parameters
const swapParams = {
    zeroForOne: true,
    amountIn: swapAmount,
    minAmountOut: minOutput,
    routeData: swapCalldata
};

// 3. Execute native deposit
const tx = await islandRouter.addLiquiditySingleNative(
    islandAddress,
    minimumShares,
    100, // 1% max slippage
    swapParams,
    receiverAddress,
    { value: beraAmount }
);
```

### Important Notes

* For native BERA deposits, one token in the Island pair must be WBERA
* Unused BERA is automatically returned to sender as native tokens when performing single sided deposits with native BERA
* If the msg.sender is a contract, it must handle both type of unused tokens i.e ERC20 tokens and native tokens when depositing with single token or native token
* Set appropriate slippage parameters based on market conditions
* Monitor gas costs, especially for operations involving swaps
* Verify all addresses and amounts before execution
* Consider using view functions to estimate outputs before executing transactions

## Removing Liquidity

The router exposes two functions for removing liquidity with slippage control

**`removeLiquidity`**

```solidity
function removeLiquidity(
    IKodiakIsland island,
    uint256 burnAmount,
    uint256 amount0Min,
    uint256 amount1Min,
    address receiver
) external returns (uint256 amount0, uint256 amount1, uint128 liquidityBurned)
```

**Purpose**: Removes liquidity from a Kodiak Island position by burning LP tokens and getting the underlying tokens.

**Parameters**:

* `island`: The address of the Kodiak Island position to withdraw from
* `burnAmount`: The quantity of Kodiak Island LP tokens to burn
* `amount0Min`: Minimum amount of token0 that must be received (slippage protection)
* `amount1Min`: Minimum amount of token1 that must be received (slippage protection)
* `receiver`: The address that will receive the withdrawn tokens

**Returns**:

* `amount0`: The actual amount of token0 received
* `amount1`: The actual amount of token1 received
* `liquidityBurned`: The amount of liquidity removed from the underlying Kodiak V3 position

**Important Notes**:

* Caller must approve the router contract to spend their Kodiak Island LP tokens
* Transaction will revert if received amounts are less than specified minimums
* Useful for standard token pairs where wrapped native token conversion isn't needed

***

**`removeLiquidityNative`**

```solidity
function removeLiquidityNative(
    IKodiakIsland island,
    uint256 burnAmount,
    uint256 amount0Min,
    uint256 amount1Min,
    address payable receiver
) external returns (uint256 amount0, uint256 amount1, uint128 liquidityBurned)
```

**Purpose**: Similar to `removeLiquidity` but specifically handles positions involving WBERA (wrapped BERA), automatically unwrapping it to native BERA before returning it to the user.

**Parameters**:

* `island`: The address of the Kodiak Island position to withdraw from
* `burnAmount`: The quantity of Kodiak Island LP tokens to burn
* `amount0Min`: Minimum amount of token0 that must be received (slippage protection)
* `amount1Min`: Minimum amount of token1 that must be received (slippage protection)
* `receiver`: The address that will receive the withdrawn tokens (must be payable to receive native BERA)

**Returns**:

* `amount0`: The actual amount of token0 received
* `amount1`: The actual amount of token1 received
* `liquidityBurned`: The amount of liquidity removed from the underlying Kodiak V3 position

**Important Notes**:

* Caller must approve the router contract to spend their Kodiak Island LP tokens
* One of the tokens in the pair must be WBERA
* WBERA portion will be automatically unwrapped and sent as native BERA to the receiver
* The non-WBERA token will be sent as is to the receiver
* Transaction will revert if received amounts are less than specified minimums
* Receiver address must be payable to receive native BERA
* Particularly useful for positions involving native BERA pairs

For both functions, it's recommended to:

1. Calculate expected output amounts before calling
2. Include reasonable slippage protection via `amount0Min` and `amount1Min`
3. Ensure sufficient approvals are in place
4. Handle both success and failure cases in your integration
5. Verify received amounts match expectations after the transaction

## ABI

````json
```json
[
        {
            "type": "constructor",
            "inputs": [
                {
                    "name": "_wBera",
                    "type": "address",
                    "internalType": "contract IWETH"
                },
                {
                    "name": "_kodiakRouter",
                    "type": "address",
                    "internalType": "address"
                }
            ],
            "stateMutability": "nonpayable"
        },
        {
            "type": "receive",
            "stateMutability": "payable"
        },
        {
            "type": "function",
            "name": "addLiquidity",
            "inputs": [
                {
                    "name": "island",
                    "type": "address",
                    "internalType": "contract IKodiakIsland"
                },
                {
                    "name": "amount0Max",
                    "type": "uint256",
                    "internalType": "uint256"
                },
                {
                    "name": "amount1Max",
                    "type": "uint256",
                    "internalType": "uint256"
                },
                {
                    "name": "amount0Min",
                    "type": "uint256",
                    "internalType": "uint256"
                },
                {
                    "name": "amount1Min",
                    "type": "uint256",
                    "internalType": "uint256"
                },
                {
                    "name": "amountSharesMin",
                    "type": "uint256",
                    "internalType": "uint256"
                },
                {
                    "name": "receiver",
                    "type": "address",
                    "internalType": "address"
                }
            ],
            "outputs": [
                {
                    "name": "amount0",
                    "type": "uint256",
                    "internalType": "uint256"
                },
                {
                    "name": "amount1",
                    "type": "uint256",
                    "internalType": "uint256"
                },
                {
                    "name": "mintAmount",
                    "type": "uint256",
                    "internalType": "uint256"
                }
            ],
            "stateMutability": "nonpayable"
        },
        {
            "type": "function",
            "name": "addLiquidityNative",
            "inputs": [
                {
                    "name": "island",
                    "type": "address",
                    "internalType": "contract IKodiakIsland"
                },
                {
                    "name": "amount0Max",
                    "type": "uint256",
                    "internalType": "uint256"
                },
                {
                    "name": "amount1Max",
                    "type": "uint256",
                    "internalType": "uint256"
                },
                {
                    "name": "amount0Min",
                    "type": "uint256",
                    "internalType": "uint256"
                },
                {
                    "name": "amount1Min",
                    "type": "uint256",
                    "internalType": "uint256"
                },
                {
                    "name": "amountSharesMin",
                    "type": "uint256",
                    "internalType": "uint256"
                },
                {
                    "name": "receiver",
                    "type": "address",
                    "internalType": "address"
                }
            ],
            "outputs": [
                {
                    "name": "amount0",
                    "type": "uint256",
                    "internalType": "uint256"
                },
                {
                    "name": "amount1",
                    "type": "uint256",
                    "internalType": "uint256"
                },
                {
                    "name": "mintAmount",
                    "type": "uint256",
                    "internalType": "uint256"
                }
            ],
            "stateMutability": "payable"
        },
        {
            "type": "function",
            "name": "addLiquiditySingle",
            "inputs": [
                {
                    "name": "island",
                    "type": "address",
                    "internalType": "contract IKodiakIsland"
                },
                {
                    "name": "totalAmountIn",
                    "type": "uint256",
                    "internalType": "uint256"
                },
                {
                    "name": "amountSharesMin",
                    "type": "uint256",
                    "internalType": "uint256"
                },
                {
                    "name": "maxStakingSlippageBPS",
                    "type": "uint256",
                    "internalType": "uint256"
                },
                {
                    "name": "swapData",
                    "type": "tuple",
                    "internalType": "struct RouterSwapParams",
                    "components": [
                        {
                            "name": "amountIn",
                            "type": "uint256",
                            "internalType": "uint256"
                        },
                        {
                            "name": "minAmountOut",
                            "type": "uint256",
                            "internalType": "uint256"
                        },
                        {
                            "name": "zeroForOne",
                            "type": "bool",
                            "internalType": "bool"
                        },
                        {
                            "name": "routeData",
                            "type": "bytes",
                            "internalType": "bytes"
                        }
                    ]
                },
                {
                    "name": "receiver",
                    "type": "address",
                    "internalType": "address"
                }
            ],
            "outputs": [
                {
                    "name": "amount0",
                    "type": "uint256",
                    "internalType": "uint256"
                },
                {
                    "name": "amount1",
                    "type": "uint256",
                    "internalType": "uint256"
                },
                {
                    "name": "mintAmount",
                    "type": "uint256",
                    "internalType": "uint256"
                }
            ],
            "stateMutability": "nonpayable"
        },
        {
            "type": "function",
            "name": "addLiquiditySingleNative",
            "inputs": [
                {
                    "name": "island",
                    "type": "address",
                    "internalType": "contract IKodiakIsland"
                },
                {
                    "name": "amountSharesMin",
                    "type": "uint256",
                    "internalType": "uint256"
                },
                {
                    "name": "maxStakingSlippageBPS",
                    "type": "uint256",
                    "internalType": "uint256"
                },
                {
                    "name": "swapData",
                    "type": "tuple",
                    "internalType": "struct RouterSwapParams",
                    "components": [
                        {
                            "name": "amountIn",
                            "type": "uint256",
                            "internalType": "uint256"
                        },
                        {
                            "name": "minAmountOut",
                            "type": "uint256",
                            "internalType": "uint256"
                        },
                        {
                            "name": "zeroForOne",
                            "type": "bool",
                            "internalType": "bool"
                        },
                        {
                            "name": "routeData",
                            "type": "bytes",
                            "internalType": "bytes"
                        }
                    ]
                },
                {
                    "name": "receiver",
                    "type": "address",
                    "internalType": "address"
                }
            ],
            "outputs": [
                {
                    "name": "amount0",
                    "type": "uint256",
                    "internalType": "uint256"
                },
                {
                    "name": "amount1",
                    "type": "uint256",
                    "internalType": "uint256"
                },
                {
                    "name": "mintAmount",
                    "type": "uint256",
                    "internalType": "uint256"
                }
            ],
            "stateMutability": "payable"
        },
        {
            "type": "function",
            "name": "kodiakRouter",
            "inputs": [],
            "outputs": [
                {
                    "name": "",
                    "type": "address",
                    "internalType": "address"
                }
            ],
            "stateMutability": "view"
        },
        {
            "type": "function",
            "name": "removeLiquidity",
            "inputs": [
                {
                    "name": "island",
                    "type": "address",
                    "internalType": "contract IKodiakIsland"
                },
                {
                    "name": "burnAmount",
                    "type": "uint256",
                    "internalType": "uint256"
                },
                {
                    "name": "amount0Min",
                    "type": "uint256",
                    "internalType": "uint256"
                },
                {
                    "name": "amount1Min",
                    "type": "uint256",
                    "internalType": "uint256"
                },
                {
                    "name": "receiver",
                    "type": "address",
                    "internalType": "address"
                }
            ],
            "outputs": [
                {
                    "name": "amount0",
                    "type": "uint256",
                    "internalType": "uint256"
                },
                {
                    "name": "amount1",
                    "type": "uint256",
                    "internalType": "uint256"
                },
                {
                    "name": "liquidityBurned",
                    "type": "uint128",
                    "internalType": "uint128"
                }
            ],
            "stateMutability": "nonpayable"
        },
        {
            "type": "function",
            "name": "removeLiquidityNative",
            "inputs": [
                {
                    "name": "island",
                    "type": "address",
                    "internalType": "contract IKodiakIsland"
                },
                {
                    "name": "burnAmount",
                    "type": "uint256",
                    "internalType": "uint256"
                },
                {
                    "name": "amount0Min",
                    "type": "uint256",
                    "internalType": "uint256"
                },
                {
                    "name": "amount1Min",
                    "type": "uint256",
                    "internalType": "uint256"
                },
                {
                    "name": "receiver",
                    "type": "address",
                    "internalType": "address payable"
                }
            ],
            "outputs": [
                {
                    "name": "amount0",
                    "type": "uint256",
                    "internalType": "uint256"
                },
                {
                    "name": "amount1",
                    "type": "uint256",
                    "internalType": "uint256"
                },
                {
                    "name": "liquidityBurned",
                    "type": "uint128",
                    "internalType": "uint128"
                }
            ],
            "stateMutability": "nonpayable"
        },
        {
            "type": "function",
            "name": "wBera",
            "inputs": [],
            "outputs": [
                {
                    "name": "",
                    "type": "address",
                    "internalType": "contract IWETH"
                }
            ],
            "stateMutability": "view"
        }
    ]
```
````


---

# 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://documentation.kodiak.finance/developers/kodiak-islands/smart-contract-reference/kodiak-island-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.
