Concentrated Liquidity Pool
Overview
Concentrated Liquidity (from UniswapV3) enhances the traditional automated market maker (AMM) model. It allows liquidity providers (LPs) to concentrate their funds in specific price ranges within a pool, rather than evenly distributing liquidity across the entire trading range in v2.
Concepts
Tick
Tick refers to a specific price point within a price range. The entire price range of a pool is divided into discrete intervals, and each interval is defined by tickLower, tickUpper
.
// From ICLPoolManager.sol
struct ModifyLiquidityParams {
// the lower and upper tick of the position
int24 tickLower;
int24 tickUpper;
// how to modify the liquidity
int256 liquidityDelta;
// a value to set if you want unique liquidity positions at the same range
bytes32 salt;
}
/// @dev see more at CLPoolManager
function modifyLiquidity(PoolKey memory key, ICLPoolManager.ModifyLiquidityParams memory params, bytes calldata hookData);
See below on examples on computing price from tick:
price = 1.0001 ** tick
Example 1: CAKE/WETH pool:
// token0: CAKE (18 decimals)
// token1: WETH (18 decimals)
// tick: -68554 (as of 12 March 2024)
price: 1.0001 ** -68554 = 0.00105411128
// this also imply 1 CAKE = 0.00105411128 WETH
Example 2: WETH/USDT pool with different token decimals pool:
// token0: WETH (18 decimals)
// token1: USDT (6 decimals)
// tick: -193256 (as of 12 March 2024)
price: 1.0001 ** -193256 = 4.04965646e-9
// this also imply 1 WETH = 4.04965646e-9 USDT
// However WETH is 18 decimals and USDT is 6 decimal, we have to multiply by 12 decimals
ratio of WETH/USDT with decimals: = 4.04965646e-9 * (10**18/10**6): 1 WETH = 4049.65646 USDT
Tick Spacing
Tick spacing define the minimum price movement between two adjacent ticks within a pool. This is one of the parameter in PoolKey
.
Keep in mind that there are trade off. While smaller tick spacing allow for more precise positioning of liquidity, it might come with higher gas cost potentially. Larger tick spacing could be cheaper on gas but comes with higher slippage in trade execution potentially.
Tick spacing | Price movement between tick |
---|---|
1 | 0.01% |
10 | 0.1% |
50 | 0.5% |
100 | 1% |
- tickSpacing is 10: adjacent tick to 1000 is 990 and 1010.
- tickSpacing is 100: adjacent tick to 1000 is 900 and 1100
sqrtPriceX96
This uint160 value represent square root price ratio of token0 to token1. Its a binary fixed point number Q64.96
which means 64 bits of integer and 96 bits as decimal.
Hence
sqrtPriceX96 = sqrt(price) * 2**96
// shifting price to the LHS result in the below:
price = (sqrtPriceX96 / 2**96) ** 2
See below on examples on computing price ratio from sqrtPriceX96:
Example 1: CAKE/WETH pool:
// token0: CAKE (18 decimals)
// token1: WETH (18 decimals)
// sqrtPriceX96: 2574020484831874748518739167 (as of 12 March 2024)
// Price ratio of token0 (CAKE) and token1 (WETH):
price = (2574020484831874748518739167 / 2**96) **2 = 0.00105551602
CAKE to WETH: 1 CAKE = 0.00105551602 WETH
WETH to CAKE: 1 / 0.00105551602: 1 WETH = 947.40 CAKE
// CAKE is around $4.3 and ETH is $4100. so 947.40 CAKE = 4073 USDC
Example 2: WETH/USDT pool with different token decimals pool:
// token0: WETH (18 decimals)
// token1: USDT (6 decimals)
// sqrtPriceX96: 5046499860369450237927407 (as of 12 March 2024)
// Price ratio of token0 (WETH) in term of token1 (USDT): 4.0571e-9 ETH for 1 USDC
price = (5046499860369450237927407 / 2**96) **2 = 4.0571e-9
WETH to USDC: 4.0571e-9 / (10**6/10**18): 1 WETH = 4057 USDT
USDT to WETH: 4.0571e-9 / (10**18/10**6): 1 USDT = 4.0571e-21 WETH
Liqudity delta
Liquidity delta, an int
value refers to the differences in liquidity at modifyLiquidity
. When its positive, it means user is adding liquidity, conversely when its negative, it means user is removing liquidity. Do refer to LiquidityAmounts.sol
on calculating the liquidity.