Skip to content

Add function to get amount of asset locked per tick in pool #283

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions tests/test_uniswap.py
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,32 @@ def test_get_tvl_in_pool_on_chain(self, client: Uniswap, tokens, token0, token1)
assert tvl_0 > 0
assert tvl_1 > 0

@pytest.mark.parametrize("token0, token1", [("DAI", "USDC")])
def test_asset_locked_per_tick_sums_to_tvl(self, client: Uniswap, tokens, token0, token1):
if client.version != 3:
pytest.skip("Not supported in this version of Uniswap")

pool = client.get_pool_instance(tokens[token0], tokens[token1])
asset_locked_per_tick_dict = client.get_asset_locked_per_tick_in_pool(pool)

# check TVL adds up correctly
token0_total = 0
token1_total = 0
ticks = asset_locked_per_tick_dict['ticks']
token0_arr = asset_locked_per_tick_dict['token0']
token1_arr = asset_locked_per_tick_dict['token1']

for i, tick in enumerate(ticks):
token0_total += token0_arr[i]
token1_total += token1_arr[i]

tvl_0, tvl_1 = client.get_tvl_in_pool(pool)

# assert on values rounded to nearest million for now TODO: fix
assert round(tvl_0 / 1e6) == round(token0_total / 1e6)
assert round(tvl_1 / 1e6) == round(token1_total / 1e6)
assert round((tvl_0 + tvl_1) / 1e6) == round((token0_total + token1_total) / 1e6)

@pytest.mark.skip
@pytest.mark.parametrize(
"token, max_eth",
Expand Down
4 changes: 2 additions & 2 deletions uniswap/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
MAX_TICK = -MIN_TICK

# Source: https://github.com/Uniswap/v3-core/blob/v1.0.0/contracts/UniswapV3Factory.sol#L26-L31
_tick_spacing = {100:1, 500: 10, 3_000: 60, 10_000: 200}
_tick_spacing = {100: 1, 500: 10, 3_000: 60, 10_000: 200}

# Derived from (MIN_TICK//tick_spacing) >> 8 and (MAX_TICK//tick_spacing) >> 8
_tick_bitmap_range = {100:(-3466, 3465), 500: (-347, 346), 3_000: (-58, 57), 10_000: (-18, 17)}
_tick_bitmap_range = {100: (-3466, 3465), 500: (-347, 346), 3_000: (-58, 57), 10_000: (-18, 17)}
95 changes: 95 additions & 0 deletions uniswap/uniswap.py
Original file line number Diff line number Diff line change
Expand Up @@ -1385,6 +1385,101 @@ def get_tvl_in_pool(self, pool: Contract) -> Tuple[float, float]:
token1_liquidity = token1_liquidity // (10**token1_decimals)
return (token0_liquidity, token1_liquidity)

def get_asset_locked_per_tick_in_pool(self, pool: Contract) -> Dict:
"""
Iterate through each tick in a pool and calculate the amount of asset
locked on-chain per tick

Note: the output of this function may differ from what is returned by the
UniswapV3 subgraph api (https://github.com/Uniswap/v3-subgraph/issues/74)

Params
------
pool: Contract
pool contract instance to find TVL
"""
pool_tick_output_types = (
"uint128",
"int128",
"uint256",
"uint256",
"int56",
"uint160",
"uint32",
"bool",
)

pool_immutables = self.get_pool_immutables(pool)
pool_state = self.get_pool_state(pool)
fee = pool_immutables["fee"]
sqrtPrice = pool_state["sqrtPriceX96"] / (1 << 96)

TICK_SPACING = _tick_spacing[fee]
BITMAP_SPACING = _tick_bitmap_range[fee]

_max_tick = self.find_tick_from_bitmap(BITMAP_SPACING, pool, TICK_SPACING, fee, True)
_min_tick = self.find_tick_from_bitmap(BITMAP_SPACING, pool, TICK_SPACING, fee, False)

assert _max_tick != False, "Error finding max tick"
assert _min_tick != False, "Error finding min tick"

# # Correcting for each token's respective decimals
token0_decimals = (
_load_contract_erc20(self.w3, pool_immutables["token0"])
.functions.decimals()
.call()
)
token1_decimals = (
_load_contract_erc20(self.w3, pool_immutables["token1"])
.functions.decimals()
.call()
)
Batch = namedtuple("Batch", "ticks batchResults")
ticks = []
# Batching pool.functions.tick() calls as these are the major bottleneck to performance
for batch in list(chunks(range(_min_tick, _max_tick, TICK_SPACING), 1000)):
_batch = []
_ticks = []
for tick in batch:
_batch.append(
(
pool.address,
HexBytes(pool.functions.ticks(tick)._encode_transaction_data()),
)
)
_ticks.append(tick)
ticks.append(Batch(_ticks, self.multicall(_batch, pool_tick_output_types)))

liquidity_total = 0
liquidity_per_tick_dict: Dict = {
'ticks': [],
'token0': [],
'token1': []
}
for tickBatch in ticks:
tick_arr = tickBatch.ticks
for i in range(len(tick_arr)):
tick = tick_arr[i]
tickData = tickBatch.batchResults[i]
# source: https://stackoverflow.com/questions/71814845/how-to-calculate-uniswap-v3-pools-total-value-locked-tvl-on-chain
liquidityNet = tickData[1]
liquidity_total += liquidityNet
sqrtPriceLow = 1.0001 ** (tick // 2)
sqrtPriceHigh = 1.0001 ** ((tick + TICK_SPACING) // 2)
token0_liquidity = self.get_token0_in_pool(
liquidity_total, sqrtPrice, sqrtPriceLow, sqrtPriceHigh
)
token1_liquidity = self.get_token1_in_pool(
liquidity_total, sqrtPrice, sqrtPriceLow, sqrtPriceHigh
)
token0_liquidity = token0_liquidity // (10**token0_decimals)
token1_liquidity = token1_liquidity // (10**token1_decimals)
liquidity_per_tick_dict['ticks'].append(tick)
liquidity_per_tick_dict['token0'].append(token0_liquidity)
liquidity_per_tick_dict['token1'].append(token1_liquidity)

return liquidity_per_tick_dict

# ------ Approval Utils ------------------------------------------------------------
def approve(self, token: AddressLike, max_approval: Optional[int] = None) -> None:
"""Give an exchange/router max approval of a token."""
Expand Down
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy