Uniswap V3 Fee Accumulation Demystified: From FeeGrowth to Liquidity Management

Apr 22
10
min of reading

In UniswapV3, the fee accumulation is measured on a fee perliquidity base. On a high-level overview, this is very trivial if one goes notinto detail, it is as simple as:

>(feeGrowthNew - feeGrowthLast) * liquidity

This is a very simple representation of a user's feeaccumulation. The NonfungiblePositionManager handles the management of the feeexactly that way. This is illustrated during the position mint as follows:

As one can see, during the minting, the updated feefeeGrowthLast is passed to the _positions struct. If the position nowaccumulates fee, this will increase feeGrowthInsideLast and can be claimed asfollows:

which is a simple multiplication of liquidity with the feedelta.

When applying this methodology, it is important to ensure youcannot manipulate your own liquidity share without adjusting feeGrowthLast, asthis would essentially result in a theft of fees from the system.

Within the underlying pool, the feeGrowth calculation is a bitmore complicated. For that, feeGrowthGlobal0 and feeGrowthGlobal1 variables areused which serve as global accumulators for the fee. The goal for a position[pa; pb]; liquidity, is then to calculate how much feeGrowth this position hasexperienced.

To simplify the explanation, we refer to feeGrowthInside andfeeGrowthOutside, whereas:

feeGrowthInside: fee accumulated b/w a position

feeGrowthOutside: fees that accumulate below tickLower and abovetickUpper

Continuing with the simplified explanation, we can now derivethe actual feeGrowthInside by subtracting the outside feeGrowthOutside fromfeeGrowthInside which then in fact returns how much has been accumulated by thetick range. The actual implementation is within the getFeeGrowthInsidecalculation which calculates the correct fee based on the currentTick and therange ticks:

That if for example the position range is around thecurrentTick, this will yield the following calculation

feeGrowthInside= feeGrowthGlobal - lower.feeGrowthOutSide - upper.feeGrowthOutside

There are furthermore a few caveats when initializing the ticksfor a position or when crossing a tick, which are:

feeGrowthOutside is only set to feeGrowthGlobal if the tick tobe initialized is equal/below the currentTick

Whenever a tick is crossed, feeGrowthOutside is set tofeeGrowthGlobal - tick.feeGrowthOutside to ensure the feeGrowthOutside value infact corresponds to what has been accumulated outside of the range.

If we have thus created a new position with all ticks below thecurrentTick and feeGrowthGlobal = 100, the outer boundaries for this positionbecome both 100. If we now swap downwards and the upper tick is crossed,upper.outerGrowth will be set to feeGrowthGlobal - upper.feeGrowthOuter whichis zero. If the position accumulates fees, feeGrowthGlobal will increase. If wenow apply the above calculation on the following state:

upper.feeGrowth= 0 (due to cross)

lower.feeGrowth= 100 (due to init state)

feeGrowthGlobal= 200 (due to accumulation within range)

feeGrowthInside= 200 - 100 - 0

feeGrowthInside= 100

If we now continue swapping down (without any fee fordemonstration purposes) while crossing the lowerTick, lower.FeeGrowth will beset to feeGrowthGlobal - lower.FeeGrowthOuter this will yield the followingstate:

upper.feeGrowth= 0 (due to cross)

lower.feeGrowth= 100 (due to cross)

feeGrowthGlobal= 200 (due to accumulation within range)

The formula if the currentTick is below the range is as follows:

>feeGrowthGlobal - (feeGrowthGlobal - lower.feeGrowthOutside) -upper.feeGrowthOutside

>200 - (200 - 100) - 0

>100

Read the original article

Related articles