How to calculate Uniswap v3 pool's Total Value Locked (TVL) on chain?
Asked Answered
K

1

6

I want to calculate the total value locked in a particular pool in Uniswap v3. I can't use the subgraph API for this.

enter image description here

I can get current liquidity / in range liquidity using uniswapV3pool contract function:

in_range_liquidity = uniswapV3pool_contract.functions.liquidity().call()

I get the result 10608850786221311055 for liquidity. Do I need to process it to get the USD value or something else?

Finally this is just current liquidity, I need total locked value, which includes both active and inactive liquidity in the pool.

Kurtiskurtosis answered 10/4, 2022 at 8:12 Comment(0)
C
12

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)
Canaanite answered 10/4, 2022 at 9:47 Comment(11)
how do I get the value for MIN_TICK, MAX_TICK and TICK_SPACINGKurtiskurtosis
@Kurtiskurtosis I expanded the answerCanaanite
MIN_TICK and MAX_TICK as -887272 and +887272 will take forever to calculate the above script and are very expensive in terms of web3 calls. If I create another question on how to get all non-zero tick ranges then can you help me answer that? Thanks dude!Kurtiskurtosis
#71853512Kurtiskurtosis
kfx what is the definition of TVL, is there any official say in this? is it the sum of tokens amount multiplied by their price or something else?Kurtiskurtosis
@Kurtiskurtosis it's the amount of the tokens multiplied by their value in USD. The best reference on how exactly that is computed is the Uniswap subgraph's source code.Canaanite
So balance does provide us the exact amount of tokens in the contract.. then why were you worried about the fee.Kurtiskurtosis
Why is the tick divided by 2 to calculate sqrt price low and high e.g sqrtPriceLow = 1.0001 ** (tick // 2)Reina
@Reina added explanation to the answer.Canaanite
Excuse me, I'm new to Uniswap, what is magic number 1.0001?Polyandrist
@DmitriiVinokurov it's defined in the v3 whitepaperCanaanite

© 2022 - 2024 — McMap. All rights reserved.