Directly implementing this logic, with individual tick reserves, updates for each swap and liquidity change, and Uniswap V2-style fee calculations, would lead to a significant number of variables and high gas consumption.
Therefore, we need a mathematical approach to make cross-tick swaps and liquidity providing/fees calculations cheap. Our goal is to achieve near-constant gas costs and minimal storage usage. These requirements are the main cause for using liquidity(
L and
ΔL) and
sqrtPrice*(
√P and
Δ√P) instead of direct token amounts (as it was done in Uniswap v2).
The first important aspect of using single liquidity(
L) and squarePrice(
√P) is that only one of these values changes during the swaps and minting/burning liquidity.
√P changes when swap is performed within a tick,
L changes when the tick is crossed or there is a mint/burn the liquidity.
In addition, the use of
√P instead of
P avoids the need for the
sqrt() function, which doesn't have a constant gas cost. In Uniswap V2
sqrt() is
implemented by using the Babylonian method that doesn't have a constant gas cost. In Uniswap V3, you will not find the calculation of
sqrt() at all; instead, precalculated computation is used in TickMath.sol (
here).
The usage of
√P and
L allows for the calculation of token amounts received within a swap and the change of the price
√P with very simple formulas: