SheepDex Maths Explained
Last updated
Last updated
According to the constant product market maker (standard market-making algorithm adopted by v2), the amount of two tokens of the pair must respect the constant product formula: x*y=k, where x is the reserve of token A, y is the reserve of token B. If k is equal to L^2(L is liquidity), we will get the following formulas which define the relationship between the amount of the two tokens, price and liquidity(P is the price of token A in terms of token B).
Given L and square root (P), the reserve of the two tokens x and y can be calculated by:
According to the formula, assuming that liquidity is constant (without adding or removing), liquidity is defined as the change in the amount of y for a given change in P, or more specifically, the square root P.
So this is the basic formula in v3 that can be used to calculate liquidity for a given y and P, where liquidity is defined as the change in the amount of y for a given change in the square root P. So given a constant trading volume, the change in P is small if there is sufficient liquidity, and change in P is large if there is not enough liquidity in the pool.
V2 Liquidity is “universal”, meaning it has the same liquidity at all price points. V3 liquidity consists of a series of different Positions. There will be more liquidity aggregated in the range near spot price. Liquidity providers (LPs) will only earn trading fees if there are transactions occurring in the ranges where liquidity is added. So if LPs wish to make their capital more efficient and earn more fees, they may select a specific range where they expect to see the most volume and most of the fees to come from that range. In other words, LPs will only earn fees in the ranges where there are swaps. In order to calculate the fee in each range, the Tick concept is introduced.
Providing liquidity in a particular range is complex as it leads to overlaps of different Positions. Since we can not compare liquidity between different Positions, the transaction fee cannot be mixed into the pool and withdrawn when liquidity is removed. In order to solve this problem, v3 introduced the concept of Tick. Transaction fees are settled in real time and recorded separately and are not mixed into the pool. While liquidity of the specific range is different from that of the entire interval, liquidity at different ticks (or between different ticks) will become comparable. Pirce from - ∞ to ∞ can be expressed in sharp enough granularity using a large enough signed integer power of 1.0001, called a tick (price points):
Each tick’s price is exactly 0.01% (1 basis point) away from its neighbor.
Each Tick has a unique id. Position can be represented with two ticks. So transaction fee can be calculated by each Tick and distributed according to the proportion of liquidity on each Tick. Let's take a look at the calculation the range of one Tick.
Suppose there is a small y, and we need to swap it with x. With the following formula we can calculate the price change under the change of y.
Then we get the amount of x using:
There are three fee tiers for each pool in v3: 0.1%, 0.2%, and 0.5%. The swap of y/x can be made after deducting the transaction fee.
So you can only select one tier for the swap, which means the fees will be the same if if there are multiple price ranges. And if you want to add liquidity to a pool with a different fee tier, you may have to add a new position. If you want to add a trading pool with different transaction fees for the same trading pair, you must create a new trading pool.
Now take a closer look at the calculation of adding/removing liquidity and transaction fees.
In v3, you can add or remove liquidity in a range based on the spot price. And your liquidity is added or removed according to the following formula:
Note that the price change does not refer to the width of range, but to the liquidity provision in the range. The required fund change refers to price change based on spot price.
So depending on the spot price, there will be three types of liquidity provision:
● Spot price is in the range where liquidity is provided (il <= ic < iu)
As price moves in this interval, when the sport price approaches il, the funds needs to be provided is y; if the price increases to iu, then x is the amount needed. So, for delta_Y, price change is calculated by square root P - square root P (il); and for delta_X, price change is 1/square root P - 1/square root P (iu).
● Spot price is at the lower bound of the range
Since the spot price is far lower than il, even if it moves towards iu, we only need reserve x instead of reserve y. Therefore, in this case, delta_Y= 0. Since it takes reserve x in the entire Position from il to iu, so for delta_X, the price change is 1/square root P (il) - 1/square root P (iu).
The spot price is above the liquidity price range
● Spot price is at the upper bound of the range
Similar to the first situation, so no need to repeat the analysis.
There could be multiple Positions on one Tick. When calculating the transaction fee, it should be distributed evenly to the total liquidity of all 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 spot 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, you can subtract the rate outside the Position from the total. At a boundary Tick we note down feeGrowthOutside. It 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 of the Tick. Simply put, the transaction rate is the rate of Positions to the opposite direction of where it was heading towards. feeGrowthOutside is noted by fo. Since fo 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 a Position is created, fo of the Tick on the boundary needs initialize:
When spot price is greater than Tick price, since spot 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 the Tick price is greater than the spot price, and the price has not passed through the Tick, since we assumed that all previous fees happen below Tick price, there is no fee above the Tick. Hence, fo is equal to zero. Based on this logic, we can interpret that during swap process, accompanied by the price fluctuation, in a Position, the transaction rate above the maximum Tick and the rate below the minimum Tick can be calculated with formula below:
Take transaction rate that is less than min Tick: if ic>=i (spot price is 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.