Skip to content

Liquidity Book

Overview

In liquidity book AMM type, liquidity providers (LPs) have the flexibility to add liquidity in any shapes in a single call. Whereas liquidity are added in a uniform manner across the chosen price range for concentrated liquidity type.

To add liquidity, LPs will specify a byte32 liquidityConfigs array. Each item in the array would indicate which bin and the percentage of amountIn would be added to this bin. For more details, see guides section on liquidity book.

struct MintParams {
  // see more at LiquidityConfigurations.sol
  //   [0 - 24[: id
  //   [24 - 88[: distributionY - 1e18 represent 100% of token into this bin
  //   [88 - 152[: distributionX - 1e18 represent 100% of token into this bin 
  //   [152 - 256[: empty
  bytes32[] liquidityConfigs;
 
  // amountIn intended
  bytes32 amountIn;
  
  // the salt to distinguish different mint from the same owner
  bytes32 salt;
}
 
/// @dev see more at BinPoolManager.sol 
function mint(PoolKey memory key, IBinPoolManager.MintParams calldata params, bytes calldata hookData);

Concepts

Bin

Liquidity book

Think of bin as bucket where liquidity reside and there are a finite number of bins for each pool. Each bin has a price and id associated.

Bin Step

Bin step define the minimum price movement between two adjacent bin. This is one of the parameter in PoolKey. Keep in mind that there are trade off. While smaller bin steps allow for more precise positioning of liquidity, it might come with higher gas cost potentially (in liquidity and swap operation). Larger bin step could be cheaper on gas but comes with higher slippage in trade execution potentially.

Bin StepPrice movement between bin
10.01%
100.1%
200.2%
1001%

Upon initial deployment, the largest BIN_STEP for a pool can be 100. The limit can be raised in the future through governance process.

Bin Active id

Think of activeId as the current bin id. To retrieve the activeId of the bin

// ref: BinPoolManager.sol
binPoolManager.getSlot0(PoolId) external 
  returns (uint24 activeId, uint16 protocolFee, uint24 swapFee);

Bin Pricing

Given the current activeId and binStep, we can calculate the current price of the pool.

price: (1 + binStep / 10_000) ** (activeId - 8388608)

Example: USDT/USDC pool with bin step = 1 and activeId = 8388608

price: (1 + 1 / 10_000) ** (8388608 - 8388608) = 1
// this means 1 USDT = 1 USDC 

Bin Liquidity

Liquidity is concentrated in discrete price bins, unlike the continuous price ranges in other AMM types like concentrated liquidity type. Each bin holds a constant sum of assets, allowing for zero slippage trades.

// price is the price of the bin.
// x is amount of tokenX 
// y is amount of tokenY 
Liquidity = price * x + y

Example: USDT/USDC pool with bin step = 1 and activeId = 8388608 with 100 usdc and 50 usdt (assume 6 decimal place).

// price from above example
Liquidity = price * x + y
          = 1 * 100e6 + 50e6
          = 150e6

To retrieve the liquidity and reserve of a bin, take note of the result within the code, it is a 128.128-binary fixed-point number.

// ref: BinPoolManager.sol
binPoolManager.getBin(PoolId id, uint24 binId)
  returns (uint128 binReserveX, uint128 binReserveY, uint256 binLiquidity);

Composition fee

When adding liquidity to the current active bin, if the bin's tokenY and tokenX ratio does not match the liquidity being added, a "swap" will occur. This swap will incur a composition fee, which is the same as the typical swap (LP + protocol) fee.

The composition fee prevents users from exploiting the add/remove liquidity process to perform a "free swap."

Example:
  1. Current active bin reserve is [1,000e18 token0, 1000e18 token1]
  2. User adds: [500e18 token0, 400e18 token1]
  3. Since the user is adding liquidity with a higher ratio of token0, a composition fee will be applied. This fee arises from swapping some token0 to token1 in the active bin.

For more details, see the calculation in BinHelper.sol

Fungible liquidity

An advantage of Liquidity book is the fungible liquidity. When user add lp, they get a share of the current bin. As more swaps happen, that same share will grow in token amount.

Fungible liquidity also provides flexibility in downstream defi application. When LPs add liquidity via BinFungiblePositionManager.sol, they get ERC1155 token.

/// Ref BinFungibleToken.sol (similar to ERC1155)
function _mint(address to, uint256 id, uint256 amount) internal {
  totalSupply[id] += amount;
  unchecked {
    balanceOf[to][id] += amount;
  }
}

See BinFungiblePositionManager on how a token is minted to the liquidity provider.