Uniswap — Deep Dive into V3 technical white paper
uniswap V3 has drown wide attention upon its publication to the world. Compared to V2, V3 has more complexity in logic and code. V3 core’s liquidity(Centralized liquidity) is driven by its handicap range. It deals with the fund utilization rate issue that occurs when LP provides liuquidity. “What is fund utilization rate?” you might ask. How does V3 come up the calculation formula for interval liquidity? How do we understand liquidity? How do we assess swap fee? These will be covered in this article, starting from detailed analysis of V3 technical white paper.
First here lists some official documentation about V3:
- V3 official documentation
https://uniswap.org/blog/uniswap-v3/
- technical white paper
https://uniswap.org/whitepaper-v3.pdf
- Smart Contract Code
https://github.com/Uniswap/uniswap-v3-core
https://github.com/Uniswap/uniswap-v3-periphery
I recommend you to follow the reading order of white paper -> Smart Contract Code -> official documentation. WIth understanding of the framework covered in white paper, the code will be easy to follow. The first chaper of white paper covers the technical features of uniswap V3, with the core concept of “Concentrated Liquidity”.
Fund Utilization Rate
First let us look at the fund utilization rate of uniswap V2:
The above figure shows the curve of x/y variation in the fund pool. Say the current price is at c point in the pool, and assume that the fluctuation is within range [a, b]. Moving from point c to a, the max consumption would be y_real; moving from point c to b, the max consumption would be x_real. In theory, we only need to provide x_real and y_real. However, in reality, as shown in above figure, when the price is at point c, the x and y provided separately are greater than x_real and y_real. It is also clear to see that, x-x_real and y-y_real, in this case, will never be used, thus referred as “Idle funds”.
In this case, the fund utilization rate is either x_real/x or y_real/y. If the price fluctuation is rather small, the fund utilization rate would also be very low. uniswap V3 attempts to solve this low fund utlization issue. The logic is rather simple. All funds can be added to a certain Position, which falls in the possible price fluctuation range. The highlight feature of uniswap V3 is how to increase liquidity in certain Position and to provide swap functionality — let us start with Virtual Reserves.
Virtual Reserves
uniswap transaction takes product fixed model (x*y=k). The Virtual Reserves refers to the funds pool with interval liquidity provided on the product fixed curve.
In the above graph, the green curve refers to the product fixed curve satisfied by the virtual reserves. The orange curve stands for the fund that is actually needed. The formula for orange curve shows as below:
You can vision this as the virtual reserves curve shifted along x/y axis, so that a/b point intersects with x/y axis. That is the effect we can achieve with certain amount of fund that moves the “virtual” transaction curve.
How to calcaulte the virtual fund pool provided within certain Position? We need to have a thorough understanding of liquidity first.
Liquidity — L
With the product fixed model, the two token values in the funds pool satisfies: x*y = K. Say we set K=L², we will then have x*y = L². L would be the so called “liquidity”. From the product fixed model, we obtain following formula:
With known L and sqrt(P), we can get the fund demand value x and y.
With formula 6.6, under a fixed liquidity (no addition or deletion on liquidity), the liquidity can be seen as the unit “price fluctuation” and change of y fund value. The reason “price fluctuation” here is put inside quotation is that we are actually talking about the change of sqrt(P).
This is the core formula of uniswap V3, to calculate liquidity with relative value (fund related to price). Liquidity, in other words, would be the unit “price fluctuation” fund value. Under certain amount of transaction, the better the liquidity is, the less price fluctuates. And vice versa.
Note that, liquidity on certain Position is different from the universal liquidity of V2. The liquidity of one Position, point is it is “interval”. We cannot simply compare liquidity of one Position to another. The common liquidity of V2 vs. interval liquidity is shown below:
V2 liquidity is “universal”, meaning it has the same liquidity on all price points. V3 liquidity is made up from liquidity on a series of different Positions. Relatively speaking, the liquidity near current price would be higher. Liquidity provider (LP) can only obtain transaction fee if it provides valid liquidity for the transaction. In order to get more transaction fee, and higher fund utilization rate, LP will provide fund within a raional range of price fluctuation. That is to say, for a specific Position, ,only the provider of that Position can obtain the transaction fee generated from swap transaction inside that Position. In order to calculate transaction fee collected from each Position, we introduce the concept of Tick and the calculation methodology.
Tick
Providing liquidity to a Position brings could introduce complexity: the overlaps between Positions. Since we cannot compare liquidity between different Positions, one transaction fee cannot be mixed into the working capital to be withdrawn when the liquidity is removed. To solve this problem, uniswap V3 introduces the concept of Tick. Transaction fees are settled in real time and recorded individually, without mixed into working capital. Though the liquidity cannot be compared in overall Positions to Positions, at certain price point (a specific price piece) the liquidity is comparable. uniswap V3 divides the whole price range (negative infinity to positive infinity) into indivisual Ticks (price point):
Value of the later price point fluctuates 0.01% on the base of previous price point.
Each Tick has a unique id. Position can be represented with two Ticks. In this logic, transaction fee can be calculated by each indivisual Tick, and transaction allocated accordingly to liquidity on each Tick. Let us ta ke a look how to calculate the range of oneTick.
SWAP Inside a Tick
Suppose there is a small y, and we need to swap it with x. With Formula 6.13, we can calculate the price change under influence of the change of y value.
Then we get the swap target x value through formula 6.15.
uniswap V3 sets up different transaction fees to same type of transaction: 0.05%, 0.3% and 1% (plus other potential fees). If we are swapping X with Y, we deduct the transaction in prior to the swapping.
Only one rate is supported in a single transaction pool. That is also to say, the different price range supported in one transaction pool comes with the same rate of transaction fee. If it’s needed to add transaction pool with different rate on the same transaction pair, creation of a new transaction pool is required.
Next, let us look at the calculation logic of addition/deletion of liquidity and how to come up with its transaction fee.
Add/Remove Liquidity
V3 addtion/deletion of liquidity is to add/delete in certain Position at current price. All liquidity addition/deletion follows below formula:
Note that, the price fluctuation does not refer to the size of Position, but to provide liquidity on certain Position. The “required fund change” refers to price fluctuation in relative to current price.
There are 3 different cases when it comes to the relationship between the range of liquidity to be added and the current price.
- When current price belongs to the liquidity price range (il <= ic < iu)
Since the price falls in the range, if price moves to il, then fund needs to be provided is y. And if price moves to ui, fund needs to be provided is x. Therefore, for delta_Y, price change is sqrt(P) — sqrt(p(il));for delta_X, price change is 1/sqrt(P) — 1/sqrt(p(iu)).
- When current price is below the liquidity price range
Becuase current price is far lower than il, even if current price moves towards iu, we only need fund of x, not y. In this case, delta_Y = 0. Since il going to iu requires fund of f in entire Position, for delta_X, the price change is 1/sqrt(il) — 1/sqrt(p(iu)).
- Current price is above the liquidity price range
Similar to the first situation, so no need to repeat the analysis.
Swap Transaction Fee
The most complicated logic is uniswap is the calculation of transaction fee and its allocation. Before adding or removing liquidity, the associated transaction fee needs to be withdrawn.
- Total liquidity on Lick
There could be multiple Positions on one Tick. When calculating the transaction fee, it should be distributed evenly to the total liquidity of multiple Positions on the Tick. At the boundary of each Tick, we record delta_L (sum of all Positions with this Tick as boundary).
There exists a global state: liquidity, save the sum of corresponding Tick of current price. When the price fluctuates and passes one certain Tick, the liquidity will increase or decrease (depends on which way the price fluctuates). For example, price crosses one Position from left to right; as it crosses the first Tick in the Position, the liquidity needs to increase. As it passes through last Tick, the liquidity goes down. Ticks in the middle does not increase or decrease liquidity (delta_L=0).
- Transaction fee rate in Position
To calculate the transaction fee rate in a Position, we subtract the rate outside the Position from the total. At a boundary Tick we note down feeGrowthOutside. feeGrowthOutside means the total fee of the “opposite” direction. The “opposite” direction is relative to the direction passing through the current Tick. When price passes through a Tick from left to right, feeGrowthOutside refers to the transaction rate of all Positions to the left of Tick. Simply speaking, the transaction rate is the rate of Positions to the opposite direction of where it was heading towards. feeGrowthOutside is noted by fo. Since it is the total transaction rate of both directions of a Tick, the sum of the rate of both directions must be equal to fg (fee_global). Therefore, when passing through a Tick, the fo on this Tick needs to flip:
When one Position is created, fo of the Tick on the boundary needs to initialize:
When currect price is greater than Tick price, since current price is within the set range, even without allocated any of the previous fee, we can think it as all fee happens below the Tick price, which means fo=fg. If Tick price is greater than current price, and price has not passed Tick, since we assuemed that all previous fee happened below Tick price, there is no fee above Tick. Hence, fo=0. With this logic as the base, we can interpret that during swap process, accompanied by the price fluctuation, in a Position, the transaction rate above max Tick and the rate below min Tick can be calculated with below formula:
Take transaction rate that is less than min Tick: if ic>=i (current price greater than min Tick), the total transaction rate below tick would be fo (as defined). If ic<i, fo records the total transaction rate above Tick, so a flip is required, which is fg — fo. After obtaining all fee outside the position, we can calculate the rate within this Position:
Based on obtained rate, we multiply it with position liquidity to get transaction fee received from this position.
Conclusion:
Uniswap V3’s core is liquidity concentration. Liquidity provider can provide liquidity in a certain position to increase the fund utilization rate. Transaction obtained within one position is distributed evenly by all liquidity providers in this position. uniswap V3 has implemented a variable for interval granularity — Tick, and deduced the liquidity addition/deletion, as well as transaction calculation process. Based on all of these, uniswap V3 has also updated the implementation of price oracle.