With the router contract thoroughly investigated, we shift focus to an accessory contract called TickLens.
Before we explore TickLens, let us review the basics covered in the Pool contract lesson:
A pool contract is divided into ticks, which represent edge boundaries — token price intervals where virtual liquidity can be provided
A tick is related mathematically to
sqrtPriceX96
A tick occurs at regular intervals called
tickSpacing
A tick may be initialized or uninitialized
Swaps may cross tick boundaries depending on their input size
Liquidity and price information may be retrieved for a single tick from the pool, which stores values inside a mapping called
ticks
Ticks are very useful markers, but it is impossible to know anything about the initialization of a particular pool’s ticks without querying multiple possible values. This can be done with an expensive multicall lookup, by reading the pool’s tickBitmap
, or by using the TickLens accessory contract. The multicall is impractical and wasteful so we will skip it, but will review how to retrieve and decode the tickBitmap
before moving to querying the tickLens.
Initialized Ticks
Each pool holds a record of all initialized ticks into a special mapping named tickBitmap
, which is described in the documentation as:
… a packed mapping of tick index to its initialized state.
The mapping uses int16 for keys since ticks are represented as int24 and there are 256 (2^8) values per word.
The UniswapV3Pool.sol source defines:
using TickBitmap for mapping(int16 => uint256);
Which means simply that any reference to TickBitmap
instructs Solidity to reference the mapping of int16 values to uint256 values.
But What is a Bitmap?
A bitmap is an efficient data structure that uses binary numbers to represent status. Recall that TickBitmap
is a mapping of int16
numbers to uint256
numbers. The int16
number is the index, and it maps to uint256
values.
A uint256
number has a maximum value of 2**256-1. The decimal value of this number is 115792089237316195423570985008687907853269984665640564039457584007913129639935
What about that same number in binary?
>>> bin(2**256-1)
'0b1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111'
And how long is that string of digits?
>>> len(bin(2**256-1)[2:])
256
NOTE: the slice [2:]
removes the '0b'
prefix, which functions like the '0x'
prefix for hexadecimal numbers.
A 256 bit number has a string length of 256, and the maximum value of that uint256
number has all of the bit positions set to 1.
If we use this 256-bit number to indicate status and position, it becomes a bitmap. Imagine that I wanted to keep track of the 0 or 1 status of 256 items that were always in the same order.
I could just work from the 1st value to the 256th value, set the 0 or 1 appropriately, and then store that single binary number.