The total value locked in Uniswap's v3 pool is not always straightforward to get. The liquidity itself not a good measure of the real token amounts in the pool. Uniswap v3 liquidity describes the concentrated liquidity value of the virtual token amounts, not the real amounts.
As the simplest option, you can get the on-chain amounts by calling the balanceOf
function on the token's contract:
balance0 = token0_contract.functions.balanceOf(pool_address).call()
This value is going to also include unclaimed fees. In Uniswap v3, these fees are not part of the liquidity. If you want to get the token amounts that contribute to liquidity, then the balanceOf
call is not sufficient. It leaves you with two different options for on-chain calculations:
a) Iterate over all tick ranges with non-zero liquidity.
b) Iterate over all open positions.
What follows is some quick and unoptimized Python code that implements the approach (a). It needs MIN_TICK
, MAX_TICK
, TICK_SPACING
, as well as URL
, POOL_ADDRESS
and V3_ABI
to be defined.
from collections import namedtuple
from web3 import Web3
web3 = Web3(Web3.HTTPProvider(URL))
pool = Web3.toChecksumAddress(POOL_ADDRESS)
contract = web3.eth.contract(address=POOL_ADDRESS, abi=V3_ABI)
Tick = namedtuple("Tick", "liquidityGross liquidityNet feeGrowthOutside0X128 feeGrowthOutside1X128 tickCumulativeOutside secondsPerLiquidityOutsideX128 secondsOutside initialized")
amounts0 = 0
amounts1 = 0
liquidity = 0
slot0 = contract.functions.slot0().call()
sqrtPriceCurrent = slot0[0] / (1 << 96)
def calculate_token0_amount(liquidity, sp, sa, sb):
sp = max(min(sp, sb), sa)
return liquidity * (sb - sp) / (sp * sb)
def calculate_token1_amount(liquidity, sp, sa, sb):
sp = max(min(sp, sb), sa)
return liquidity * (sp - sa)
for tick in range(MIN_TICK, MAX_TICK, TICK_SPACING):
tickRange = Tick(*contract.functions.ticks(tick).call())
liquidity += tickRange.liquidityNet
sqrtPriceLow = 1.0001 ** (tick // 2)
sqrtPriceHigh = 1.0001 ** ((tick + TICK_SPACING) // 2)
amounts0 += calculate_token0_amount(liquidity, sqrtPriceCurrent, sqrtPriceLow, sqrtPriceHigh)
amounts1 += calculate_token1_amount(liquidity, sqrtPriceCurrent, sqrtPriceLow, sqrtPriceHigh)
print(amounts0, amounts1) # for better output, should correct for the amount of decimals before printing
The value of TICK_SPACING can be read from the tickSpacing()
function of the pool's contract. Alteratively, if you know swap fee levels of the pool, you can use a constant: 1% pools have always have 200 as the tick spacing, etc.
The values of MIN_TICK and MAX_TICK can be obtained from
tickBitmap()
calls and looking at the lowest and the highest initialized tick respectively. It's quite complex and a better fit for a separate question. At the worst case if you may need to cover the whole tick range, which spans between -887272 and +887272. So for a start you can use these values rounded down / up to the tick spacing value.
Edit: the square root of 1.0001 ^ tick
is equal to 1.0001 ^ (tick / 2), a fact that use in these lines to make the computation simples:
sqrtPriceLow = 1.0001 ** (tick // 2)
sqrtPriceHigh = 1.0001 ** ((tick + TICK_SPACING) // 2)
MIN_TICK
,MAX_TICK
andTICK_SPACING
– Kurtiskurtosis