UniV3 Share

  • bixia1994
  • 发布于 2023-10-07 11:14
  • 阅读 1576

UniV3Share

UniV3 Share

核心公式:

(x+LPa)(y+LPb)=L2(x + \frac{L}{\sqrt{P_a}}) * (y + L*\sqrt{P_b}) = L^2

核心公式可以看作是xy=Kx*y=K曲线的平移。偏移量xoffset=LPax_{offset}=\frac{L}{\sqrt{P_a}}yoffset=LPby_{offset}=L\cdot \sqrt{P_b}
偏移量事实上反映了这个仓位的杠杆大小。偏移量越大,说明加的杠杆越大。

衍生公式:

tick的定义:

ic=log1.0001P,向下取整i_c=\lfloor log_{\sqrt{1.0001}}\sqrt{P} \rfloor, 向下取整

*Price可以为任何值,但是tick只能是离散的值。

L=xyL = \sqrt{xy}

*x,y为包含虚拟流动性的部分,总的x,y

P=yx\sqrt{P} = \sqrt{\frac{y}{x}}

已知L,P1,P2L,\sqrt{P_1},\sqrt{P_2},求δx,δy\delta x, \delta y

δx=LP1LP2\delta x=\frac{L}{\sqrt{P_1}} - \frac{L}{\sqrt{P_2}}
δy=LP1LP2\delta y = L \cdot \sqrt{P_1} - L\cdot \sqrt{P_2}

已知δx,L,P1\delta x, L,\sqrt{P_1}, 求P2\sqrt{P_2}:

P2=LP1L+δxP1\sqrt{P_2} = \frac{L \cdot \sqrt{P_1}}{L + \delta x \cdot \sqrt{P_1}}

已知δy,L,P1\delta y, L, \sqrt{P_1}, 求P2\sqrt{P_2}:

P2=P1δyL\sqrt{P_2} = \sqrt{P_1} - \frac{\delta y}{L}

再一次SWAP过程中,L保持不变,手续费的积累不会影响到L的值。

已知δx,P1,P2\delta x, \sqrt{P_1},\sqrt{P_2}, 求LL:

L=δx1P11P2L = \frac{\delta x}{\frac{1}{\sqrt{P_1}} - \frac{1}{\sqrt{P_2}}}

已知δy,P1,P2\delta y, \sqrt{P_1},\sqrt{P_2},求LL:

L=δyP1P2L = \frac{\delta y}{\sqrt{P_1}-\sqrt{P_2}}

公式推导

L的含义: L反应的是价格变化的阻力,也就是流动性深度。L越大,价格变化的阻力也就越大。

L=dydPL=\frac{dy}{d\sqrt{P}}

推导过程:
第一步:在一次swap过程中,L保持不变,公式可以写为如下

(x+xoffset)(y+yoffset)=L2(x + x_{offset}) \cdot (y + y_{offset}) = L^2

则:

x=L2y+yoffsetxoffsetx = \frac{L^2}{y + y_{offset}} - x_{offset}
y=L2x+xoffsetyoffsety = \frac{L^2}{x + x_{offset}} - y_{offset}

价格P的定义为:

P=dydx,dy<0P = -\frac{dy}{dx}, dy < 0

则:

P=L2(x+xoffset)2P = \frac{L^2}{(x+x_{offset})^2}

则:

P=L(x+xoffset)\sqrt{P} = \frac{L}{(x+x_{offset})}

同理,带入x+xoffset=L2y+yoffsetx+x_{offset} = \frac{L^2}{y+y_{offset}}得到:

P=y+yoffsetL\sqrt {P} = \frac{y + y_{offset}}{L}

则y等于:

y=LPyoffsety = L*\sqrt{P} - y_{offset}

当实际流动性y=0时,价格达到PAP_A,此时yoffset=LPAy_{offset}=L*\sqrt{P_A}
当实际流动性x=0时,价格达到PBP_B,此时xoffset=L/PBx_{offset}=L/\sqrt{P_B}

则同时求导数可得:

dydP=L\frac{dy}{d\sqrt{P}} = L

Tick的定义与模型

整个UniV3的核心逻辑都是围绕着价格展开,即Price。用户提供流动性时,需要指定价格区间;用户swap时,要么指定价格,要么指定数量。具体的swap过程中也是一个tick,一个tick的去swap。
tick可以看成是一条坐标轴,坐标轴上的每一个刻度都是一个tick。坐标轴的刻度是有限的。

tick的定义如下:

ic=log1.0001P,向下取整i_c=\lfloor log_{\sqrt{1.0001}}\sqrt{P} \rfloor, 向下取整

tick的取值范围为:

(887272,887271)(-887272,887271)

相对应的P\sqrt{P}的取值范围:

(4295128739,1461446703485210103287273052203988822378723970342),1018精度(4295128739, 1461446703485210103287273052203988822378723970342), 10^{18}精度

在tick的模型设计里,其与流动性深度相关。在每一个已被初始化的tick上,都维护了如下的变量:

TypeVariable NameNotation
int128liquidityNetΔL\Delta L
uint128liquidityGrossLgL_g
uint256feeGrowthOutside0X128fo0f_o0
uint256feeGrowthOutside1X128fo1f_o1
uint256secondsOutsidesos_o
uint256tickCumulativeOutsideioi_o
uint256secondsPerLiquidityOutsideX128slos_{lo}
  1. liqudityNet与Liquidity Gross
    这个值记录的是当cross 过一个tick时,流动性应该增减的部分。因为用户添加流动性时,指定了price range,也即是两个tick。
    如下图所示,alice和bob分别在a-c和b-d的范围内添加了流动性L1=500,L2=700.
    则 UniV3更新tickA时,其ΔL\Delta L = +500, LgL_g = 500;
    针对tickC,其ΔL\Delta L = -500, LgL_g = 500;
    针对tickB,其ΔL\Delta L = +700, LgL_g = 700;
    针对tickD,其ΔL\Delta L = -700, LgL_g = 700; 左右皆为闭区间。即左右的tick都会被初始化。

    每一个tick都对应于一个全局变量liquidity,该全局变量liquidity是当前tick的总流动性。用于swap的计算
    比如:
    当tick < a时,liquidity = 0;
    当tick > a, tick < b时,liquidity = 0+500;即cross tickA
    当tick > b, tick < c时,liquidity = 500+700; 即cross tickB
    当tick > c, tick < d时,liquidity = 1200+(-500);即cross tickC
    当tick > d时,liquidity = 700+(-700), 即cross tickD
    如果price反方向移动,即从右到左移动,则将加 liquidityDelta变为 减 liquidityDelta即可;

liquidity Gross的作用主要是用来判断当一个用户移除流动性之后,该tick上对应的流动性是不是已经为0;如果该tick上对应的流动为0,则需要删除该tick。

  1. feeGrowthOutside0x128和feeGrowthOutside1X128
    这两个量主要是用于帮助收取手续费的。
    UniV3里面的手续费与如下几个因素有关:1. 提供的price range;2. 提供的流动性;3. swap过程中是否经过了所提供的price range;4. 做LP的时间长度;
    可以看到UniV3的手续费收取,跟UniV2的手续费收取的模式完全不一样。事实上,UniV3使用的是类似于MasterChef的模式来收取手续费。
Rlt=t1t2{1L(t),ilower<ic<iupper0,ic<ilower0,ic>iuppper}R\cdot l \cdot \sum_{t=t_1}^{t_2} \left\{ \begin{aligned} \frac{1}{L(t)} ,& & i_{lower} < i_c < i_{upper} \\ 0,& & i_c < i_{lower}\\ 0,& & i_c > i_{uppper}\\ \end{aligned} \right\}

上面的式子可以转换为:

Rl(t=t1t21L(t)t=t1t2{1L(t),ic<ilower0,ic>=ilower}t=t1t2{1L(t),ic>=ilower0,ic<ilower})R \cdot l \cdot ( \sum_{t=t_1}^{t_2} \frac{1}{L(t)} - \sum_{t=t_1}^{t_2}\left\{ \begin{aligned} \frac{1}{L(t)}, & & i_c < i_{lower} \\ 0, & & i_c >= i_{lower} \end{aligned} \right\} - \sum_{t=t_1}^{t_2}\left\{ \begin{aligned} \frac{1}{L(t)}, & & i_c >= i_{lower} \\ 0, & & i_c < i_{lower} \end{aligned} \right\})

即:

secondsPerLiquidityInside=secondsPerLiquiditysecondsPerLiquidityBelow(lowerTick)secondsPerLiquidityAbove(upperTick)secondsPerLiquidityInside = secondsPerLiquidity - secondsPerLiquidityBelow(lowerTick) - secondsPerLiquidityAbove(upperTick)

其中,secondsPerLiquidity作为全局变量在oracle中存储,每一次操作,如swap,mint,burn都会更新该值。
对于secondsPerLiquidityBelow(lowerTick),UniV3的解决思路是,在tick中保存一个名为secondsPerLiquidityOutsideX128的变量。其具体的outside含义如下:
tick中保存的值,如secondsPerLiquidityOutsideX128,feeGrowthOutside0x128和feeGrowthOutside1X128等都是保存的outside的值。“outside”的含义是,当前价格的另一侧。

上图中,currentTick < tickI, 所以outside保存的是右侧的所有手续费与流动性之比之和fee
即:

fo(i)=i=i0imaxfeeLf_o(i) = \sum_{i=i_0}^{i_{max}}\frac{fee}{L}


上图中,currentTick > tickI, 所以outside保存的是左侧的所有手续费之和fee
即:

fo(i)=i=imini0feeLf_o(i) = \sum_{i=i_{min}}^{i_0}\frac{fee}{L}


即:

fr=fgfb(il)fa(iu)fg=i=iminimaxfeeLf_r = f_g-f_b(i_l)-f_a(i_u) \\ f_g = \sum_{i=i_{min}}^{i_{max}}\frac{fee}{L}

*针对每一个tick,其L都是定值,可以认为是常量。

对于计算(lowerTick - upperTick)之间的所有手续费fee,此时currentTick在lowerTick和upperTick之间。则可以通过:
全局的fee - feeGrowthOutside0x128(lowerTick) - feeGrowthOutside1X128(upperTick)

那么,应该如何计算feeOutside呢?在整个UniV3的设计中,当且仅当currentTick穿过某一个tick时,才会导致该tick上记录的feeOutside翻转。
其公式为:

fo(i):=fgfo(i)f_o(i) := f_g-f_o(i)

即:
全局的手续费/L-左侧的所有手续费/L=右侧的所有手续费/L

Position的定义与模型

当一个用户像UniV3中添加流动性时,都会生成一个唯一的POSITION KEY:

key = keccak256(address(user),int24(leftTick),int24(rightTick))

每一个POSITION上,都会track如下变量:

TypeVariable NameNotation
uint128liquidityll
uint256feeGrowthInside0LastX128fr,0(t0)f_{r,0}(t_0)
uint256feeGrowthInside1LastX128fr,1(t0)f_{r,1}(t_0)
uint256tokensOwed0t0t_0
uint256tokensOwed1t1t_1

其中,liquidity可以看作是用户mint时得到的lp token数量,也可以认为是xy\sqrt{x \cdot y}, 即L

POSITION的一大作用是,用于记录用户提供的流动性所应该收取的手续费数量。具体的手续费token数值会存储在tokensOwned中。

其中,feeGrowthInside0LastX128即为frf_r, 其计算过程如下:

fr=fgfb(il)fa(iu)fb(il)={fgfb(il),ic<ilowerfb(il),ic>=ilower}fa(iu)={fb(il),ic<iupperfgfb(il),ic>=iupper}f_r = f_g-f_b(i_l)-f_a(i_u) \\ f_b(i_l) = \left\{ \begin{aligned} f_g-f_b(i_l), & & i_c < i_{lower} \\ f_b(i_l), & & i_c >= i_{lower} \end{aligned} \right\}\\ f_a(i_u) = \left\{ \begin{aligned} f_b(i_l), & & i_c < i_{upper} \\ f_g-f_b(i_l), & & i_c >= i_{upper} \end{aligned} \right\}\\

具体的tokenOwned则为:

tokensOwned=(frfr)ltokensOwned = (f_r - f_r`) \cdot l

Swap 过程


当在同一个tick里面进行swap时,会进行如下计算:

  1. 找到下一个tick(initialized) ticknexttick_{next}
  2. 根据当前tick tickcurrtick_{curr}ticknexttick_{next}及L,使用公式计算出δx,δy\delta x, \delta y
    即:已知L,P1,P2L,\sqrt{P_1},\sqrt{P_2},求δx,δy\delta x, \delta y
δx=LP1LP2\delta x=\frac{L}{\sqrt{P_1}} - \frac{L}{\sqrt{P_2}}
δy=LP1LP2\delta y = L \cdot \sqrt{P_1} - L\cdot \sqrt{P_2}
  1. 判断token remaining是否大于δx\delta x,
  • 如果大于,则
tokenremaining=tokenremainingδxtoken_{remaining}=token_{remaining} - \delta x

在进入下一个循环。

  • 如果小于,则:
    已知δx=tokenremaining,L,P1\delta x = token_{remaining}, L,\sqrt{P_1}, 求P2\sqrt{P_2}:
P2=LP1L+δxP1\sqrt{P_2} = \frac{L \cdot \sqrt{P_1}}{L + \delta x \cdot \sqrt{P_1}}
  • 然后根据求出的P2\sqrt{P_2}, 再次利用上述公式,求的δx,δy\delta x, \delta y
    即:已知L,P1,P2L,\sqrt{P_1},\sqrt{P_2},求δx,δy\delta x, \delta y
δx=LP1LP2\delta x=\frac{L}{\sqrt{P_1}} - \frac{L}{\sqrt{P_2}}
δy=LP1LP2\delta y = L \cdot \sqrt{P_1} - L\cdot \sqrt{P_2}
  1. 手续费扣除:
    UniV3里面扣除手续费也是在tokenIn中,直接减去相应的手续费部分,然后剩余的参与计算。
  2. 全局变量更新:
  • fgf_g更新:
state.feeGrowthGlobalX128 += FullMath.mulDiv(step.feeAmount, FixedPoint128.Q128, state.liquidity);
  • tick更新:
    如果不需要cross tick:
    则:
ic=log1.0001P2,向下取整i_c=\lfloor log_{\sqrt{1.0001}}\sqrt{P_2} \rfloor, 向下取整

如果需要cross tick:
则:

L=L+δLL = L + \delta L
tick=ticknext1tick = tick_{next} -1
  • 学分: 7
  • 分类: DeFi
  • 标签:
点赞 1
收藏 1
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论