Introduction
This guide will cover how to fetch and compute the active liquidity in the specific Tick ranges of a pool. It is based on the Liquidity Density example and can be seen used in production, albeit in a more sophisticated way, in the Uniswap Analytics website.If you need a briefer on the SDK and to learn more about how these guides connect to the examples repository, please visit our background page!
recharts to draw a chart that visualizes our Pool’s liqudity density.
This guide will cover:
- Getting the tickSpacing and currently active Tick from the Pool
- Calculating active liquidity from net liquidity
- Drawing a chart from the Tick data
- Specifics of working with the recharts library. You can read more about that here.
Understanding Active Liquidity
To visualize the distribution of active liquidity in our Pool, we want to draw our Chart around the currently active Tick. For that we have to first understand:- What is an initialized Tick?
- What is the current Tick?
Initialized Ticks
When providing liquidity for a pool, the LP decides the price range in which the liquidity should be provided, and the amount of liquidity to be provided. The pool understands the position as liquidity between the lower and upper Tick. The Tick Index in this context is a representation of the price between the Pool’s assets. Looking at this visualization of multiple positions in a V3 Pool, we can see that the liquidity available for a swap does not change inside a position, but when crossing into the next position. This is what the Initialized Ticks of a Pool represent - they are a representation of the start or end of one or more positions.liquidityNet field.
The change is always stored in relation to the currently active Tick - the current price.
When the price crosses an initialized Tick, it gets updated and liqudity that was previously added when crossing the Tick would now be removed and vice versa.
The liquidityGross value represents the gross value of liquidity referencing the tick.
This is important for the edge case that one position ends at a Tick and a second position with exactly the same liquidity value would start at the Tick.
In this case liquidityNet would be 0 but liquidityGross would still have a value, which ensures that the Tick is not deleted from the Pool.
To visualize liquidity in a graph, we will only need to consider the changes, so it’s sufficient to fetch the Ticks with liquidityNet not 0.
Fetching initialized Ticks
To fetch all ticks of our Pool, we will use the Uniswap V3 subgraph. To visualize active liquidity, we need the tickIdx, the liquidityGross and the liquidityNet. We define our GraphQL query and send a POST request to the V3 subgraph API endpoint:GraphQL is only able to fetch 1000 records at a time. If a pool has more than 1000 initialized ticks, multiple calls are necessary to get all of them.
Current Tick
The current Tick of the Pool represents the current Price after the last swap. Considering that the initialized Ticks only represent positions, we see that it is not necessarily one of the initialized Ticks but can be at any point in between them. The active liqudity at the current Price is also stored in the smart contract - we already fetched it with theliquidity function in the previous guide.
Tickspacing
Only the Ticks with indices that are divisible with 0 remainder by the tickspacing of a Pool are initializable. This is a convention defined by the protocol to save gas. The Tickspacing of the Pool is dependent on the Fee Tier. Pools with lower fees are meant to be used for more stable Token Pairs and allow for more granularity in where LPs position their liquidity. We can get thetickSpacing from the TICK_SPACINGS enum exposed by the v3-sdk:
Pool object, we could just call Pool.tickSpacing().
Putting it all together
For the purpose of visualizing the liquidity density of the Pool, it rarely makes sense to display the full Tick Range of the Pool, as the vast majority of liquidity will be focused in a narrow price range. Instead, we will display a sensible number of Ticks around the current price.Calculating active liquidity
We know the spacing between Ticks and the Initialized Ticks where active liquidity changes. All we have to do is start calculating from the current Tick and iterate outwards. The code mentioned in the following snippets can be found inactive-liquidity.ts.
To draw our chart we want a data structure that looks something like this:
ticks variable in this code snippet is the result we got from the V3 Subgraph earlier.
We want to mark the Tick closest to the current Price and we want to be able to display the prices at a Tick to the user.
We calculate the initializable Tick closest to the current price and create the active Tick that we start from:
v3-sdk exports a handy utility function for that, tickToPrice.
We store the Price as a string as we won’t make any further calculations in this example. We will instead use it to display prices in the tooltip of our chart.
Notice how the price0 is the Price of tokenA in terms of tokenB and the price1 is the Price of tokenB in terms of tokenA at the specified Tick.
If the current Tick is initialized, we also need to set the liquidityNet to correctly handle moving out of the position:
TickProcessed.
We choose an arbitrary number of Ticks we want to display, for this example we calculate 100 Ticks in each direction.
TickMath.MAX_TICK.
Again, we check if our current Tick is initialized and if so, recalculate the active liquidity:
Drawing the Chart
We are done with our calculations and move on to displaying the data. Recharts is not able to handle JSBI, so we need to convert the Array we created to a format it can handle:uint128 format onchain, so the maximum loss of precision will be far smaller than the number of decimals of almost any ERC20 Token.
Finally, we draw the Chart:
