# Add/Remove Liquidity

## Introduction

Liquidity management is performed through `LBRouter` contract. This contract will abstract some of the complexity of the liquidity management, perform safety checks and will revert if certain conditions were to not be met.

* Liquidity is added or removed to `LBPairs`.
* Liquidity may be distributed to specific `Bins`, with different amounts per `Bin`.

Note:

* The v2.2 `LBRouter` is not backwards compatible with v2.1 `LBPairs`, although ABI stays the same
* The v2.1 `LBRouter` is not backwards compatible with v2.0 `LBPairs`.
* The v2.0 `LBRouter` must be used to remove liquidity from v2.0 `LBPairs` and v2.1 `LBRouter` must be used to remove liquidity from v2.1 `LBPairs`<br>

## Adding Liquidity

To add liquidity, the `LiquidityParameters` struct is as input:

```solidity
function addLiquidity(LiquidityParameters memory liquidityParameters)
    external
    returns (
        uint256 amountXAdded,
        uint256 amountYAdded,
        uint256 amountXLeft,
        uint256 amountYLeft,
        uint256[] memory depositIds,
        uint256[] memory liquidityMinted
    );

function addLiquidityNATIVE(LiquidityParameters memory liquidityParameters)
    external
    payable
    returns (
        uint256 amountXAdded,
        uint256 amountYAdded,
        uint256 amountXLeft,
        uint256 amountYLeft,
        uint256[] memory depositIds,
        uint256[] memory liquidityMinted
    );
```

### Liquidity Parameters

```solidity
struct LiquidityParameters {
    IERC20 tokenX; // Has to be the same as tokenX defined in LBPair contract
    IERC20 tokenY; // Has to be the same as tokenY defined in LBPair contract
    uint256 binStep; // Has to point to existing pair
    uint256 amountX; // Amount of token X that you want to add to liquidity
    uint256 amountY; // Amount of token Y that you want to add to liquidity
    uint256 amountXMin; // Defines amount slippage for token X
    uint256 amountYMin; // Defines amount slippage for token Y
    uint256 activeIdDesired; // The active bin you want. It may change due to slippage
    uint256 idSlippage; // The slippage tolerance in case active bin moves during time it takes to transact
    int256[] deltaIds; // The bins you want to add liquidity to. Each value is relative to the active bin ID
    uint256[] distributionX; // The percentage of X you want to add to each bin in deltaIds
    uint256[] distributionY; // The percentage of Y you want to add to each bin in deltaIds
    address to; // Receiver address
    address refundTo; // Refund Address
    uint256 deadline; // Block timestamp cannot be lower than deadline
}
```

The number of parameters are quite extensive. Here are a few pointers to understand how to construct them better:

* The active bin ID may change from the time you decided to add liquidity to when it is actually added. Therefore, you define `activeIdDesired` and `idSlippage` to account for when the price moves.
* `deltaIds` define which bins liquidity will be added to relative to `activeId`, 0 being the active bin. All positive values are bins with only X and all negative values are bins with only Y.
* `distributionX` (or `distributionY`) is the percentages of `amountX` (or `amountY`) you want to add to each bin.
  * Sum of all values should be less than or equal to 1. If less than, the remaining is refunded back to the user.
  * Trying to add X to a bin below the active bin or Y to a bin above the active bin will cause a revert.
* Maximum number of bins, that can be populated at the same time is around `80` on Avalanche C-chain due to block gas limit (`8M`). Multiple transactions can be used to add liquidity to more bins.

### Code Example

In this example, we add 100 USDC and 100 USDT into three bins: active bin, bin below and bin above.

We define the distributions as follow:

* For asset X (USDC), we add 50 USDC to the active bin and 50 USDC to the bin above.
* For asset Y (USDT), we add 33.3 USDT to the active bin and 66.6 USDT to the bin below.

We also allow a bin ID slippage of 5 just in case bin moves in the time it takes to execute the transaction.

```solidity
uint256 PRECISION = 1e18;
uint256 binStep = 25;
uint256 amountX = 100 * 10e6;
uint256 amountY = 100 * 10e6;
uint256 amountXmin = 99 * 10e6; // We allow 1% amount slippage
uint256 amountYmin = 99 * 10e6; // We allow 1% amount slippage

uint256 activeIdDesired = 2**23; // We get the ID from price using getIdFromPrice()
uint256 idSlippage = 5;

uint256 binsAmount = 3;
int256[] memory deltaIds = new int256[](binsAmount);
deltaIds[0] = -1;
deltaIds[1] = 0;
deltaIds[2] = 1;
uint256[] memory distributionX = new uint256[](binsAmount);
distributionX[0] = 0;
distributionX[1] = PRECISION / 2;
distributionX[2] = PRECISION / 2;

uint256[] memory distributionY = new uint256[](binsAmount);
distributionY[0] = (2 * PRECISION) / 3;
distributionY[1] = PRECISION / 3;
distributionY[2] = 0;


ILBRouter.LiquidityParameters memory liquidityParameters = ILBRouter.LiquidityParameters(
    USDC,
    USDT,
    binStep,
    amountX,
    amountY,
    amountXmin,
    amountYmin,
    activeIdDesired,
    idSlippage,
    deltaIds,
    distributionX,
    distributionY,
    receiverAddress,
    refundAddress,
    block.timestamp
);

USDC.approve(address(router), amountX);
USDT.approve(address(router), amountY);

(
    uint256 amountXAdded,
    uint256 amountYAdded,
    uint256 amountXLeft,
    uint256 amountYLeft,
    uint256[] memory depositIds,
    uint256[] memory liquidityMinted
) = router.addLiquidity(liquidityParameters);
```

## Removing Liquidity

There are some key differences between adding and removing liquidity:

1. We don't use the `LiquidityParameters` struct.
2. We use absolute bin IDs instead of relative bin IDs.
3. Because we use absolute bin IDs, bin slippage is not possible.
4. We define absolute `LBToken` balances to remove from each bin.
   * In bins below active bin, balances consist of only Y.
   * In bins above active bin, balances consist of only X.
   * In the active bin, the balance consists of a share of X and Y.

To remove liquidity, we use one of the router functions below:

```solidity
function removeLiquidity(
    IERC20 tokenX,
    IERC20 tokenY,
    uint16 binStep, // Has to point to existing pair that user has liquidity deposited in
    uint256 amountXMin, // Minimum amount of token X that has to be withdrawn
    uint256 amountYMin, // Minimum amount of token Y that has to be withdrawn
    uint256[] memory ids, // Bin IDs that liquidity should be removed from
    uint256[] memory amounts, // LBToken amount that should be removed
    address to, // Receiver address
    uint256 deadline // Block timestamp cannot be lower than deadline
) external returns (uint256 amountX, uint256 amountY);

function removeLiquidityNATIVE(
    IERC20 token,
    uint16 binStep,
    uint256 amountTokenMin,
    uint256 amountNATIVEMin,
    uint256[] memory ids,
    uint256[] memory amounts,
    address payable to,
    uint256 deadline
) external returns (uint256 amountToken, uint256 amountNATIVE);
```

Here are some pointer for using these functions:

1. Lengths of `ids` and `amounts` must be the same.
2. Values in `amounts` are `LBToken` amounts.
3. Maximum number of bins that can be withdrawn at the same time is around `51` due to Avalanche C-chain block gas limit (`8M`). In this case, multiple transactions can be used to remove more liquidity.

:::note\
For tax tokens, removing liquidity with `removeLiquidityNATIVE()` is not possible, due to double tax accrual.\
This can be circumvented in two ways, depending on tax token implementation:

1. Whitelisting LBRouter and/or LBPair.
2. Removing native liquidity with `removeLiquidity()` function. This will return wrapped native token to user, instead of just native token.\
   :::

### Code Example

```solidity
uint256 numberOfBinsToWithdraw = 3;
uint16 binStep = 25;

uint256[] memory amounts = new uint256[](numberOfBinsToWithdraw);
uint256[] memory ids = new uint256[](numberOfBinsToWithdraw);
ids[0] = 8388608;
ids[1] = 8388611;
ids[2] = 8388605;
uint256 totalXBalanceWithdrawn;
uint256 totalYBalanceWithdrawn;

// To figure out amountXMin and amountYMin, we calculate how much X and Y underlying we have as liquidity
for (uint256 i; i < numberOfBinsToWithdraw; i++) {
    uint256 LBTokenAmount = pair.balanceOf(receiverAddress, ids[i]);
    amounts[i] = LBTokenAmount;
    (uint256 binReserveX, uint256 binReserveY) = pair.getBin(uint24(ids[i]));

    totalXBalanceWithdrawn += LBTokenAmount * binReserveX / pair.totalSupply(ids[i]);
    totalYBalanceWithdrawn += LBTokenAmount * binReserveY / pair.totalSupply(ids[i]);
}

uint256 amountXMin = totalXBalanceWithdrawn * 99 / 100; // Allow 1% slippage
uint256 amountYMin = totalYBalanceWithdrawn * 99 / 100; // Allow 1% slippage

pair.approveForAll(address(router), true);

router.removeLiquidity(
    USDC,
    WAVAX,
    binStep,
    amountXMin,
    amountYMin,
    ids,
    amounts,
    receiverAddress,
    block.timestamp
);
```


---

# 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://developers.lfj.gg/guides/add-remove-liquidity.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.
