这篇文章的第一部分讨论了StableSwap不变量。
StableSwap
放大不变量是一个数学概念,旨在通过减少交易中的滑点来提高流动性池的效率,同时仍然允许价格失衡的灵活性。它结合了恒定和恒定乘积不变量的特点,创造了一个在低滑点(接近平衡)和任何价格下的可追溯性之间取得平衡的公式。
感谢您阅读Verichains!免费订阅以接收新帖并支持我的工作。
恒定乘积不变量(∏x_i=k):用于Uniswap类型的池。它非常灵活,支持任何价格的交易,但大交易的滑点会显著增加。
恒定和不变量(∑x_i=k):用于稳定币。它对平衡交易没有滑点,但无法处理大的价格失衡。
放大不变量结合了两者的优点:接近平衡时的低滑点和失衡交易的灵活性。StableSwap中放大系数参数的特点是,系数越低,不变量就越接近恒定乘积。
引入了一个杠杆因子χ来减少滑点。这个因子调整了不变量的形状,使其能够融合恒定乘积和恒定和不变量的特性,提高其在不同交易场景下的效率。
一般公式为:
其中:
χ:控制不变量曲率的杠杆因子。
D:平衡时代币价值之和的常数。
n:池中代币的数量。
∑x_i:代币数量之和。
∏x_i:代币数量之积。
如果这个方程式一直成立,用户将获得杠杆χ的交易。但是,它不会支持价格远离理想价格1.0的情况。不变量应该支持任何价格(以确保在任何时候都有一些流动性)。所以Stable Swap使χ动态化。当投资组合完全平衡时,它等于一个常数A,但当失衡时会降至0:
StableSwap不变量将χ加入到一般公式(1)中:
有两个常见的数学运算需要计算:
给定固定的A值和储备x_1,x_2,...x_n(其中n是池支持的代币数量,在部署池时就固定了),计算D。
给定D,用户希望增加一个储备xi的值到x'_i,计算另一个储备x_j需要减少多少,以保持方程平衡。
在Curve V1(StableSwap)中,D的行为类似于Uniswap V2中的k - D越大,储备越多,价格曲线就越"远离"。在添加或移除流动性,或费用变化导致池平衡发生变化后,D都需要重新计算。
定义S=∑x_i和P=∏x_i,方程(3)现在变为:
例如,如果池中只有两种代币,不变量将变为:
在StableSwap源代码中,它通过计算D_next来计算D:
这段代码中的方程可以写为:
例如,我们有100,000 USDC和1,000,000 USDT在池中。使用(3)或(4),我们可以计算出D约为1,094,540.84。这是一个使用(5)(因为它是(4)的示例)计算D的简单Python脚本:
我们可以再次使用该常量D来计算USDT或USDC的数量,使用(6)。
StableSwap使用牛顿法数值求解方程,因为它无法代数求解。StableSwap创建f(D),当方程(4)平衡时等于0。此外,它还计算导数f′(D)。
StableSwap使用牛顿法求解D,其中D可以理解为当前的D值:
首先,我们可以通过将所有元素合并成一个分数,并将分数的分子和分母都乘以D来重写(8):
通过使分母等于f'(D)来重写牛顿法,然后用(10)代替:
将所有项分配到分子中的括号中:
在(11)中消除所有项后,我们可以定义一个元素D_p为:
方程变为:
这个方程等同于(6)中定义的方程。
此外,在Viper代码中,D_p的计算方式为:
D_P: uint256 = D # D_P = Sfor _x in xp:D_P = D_P * D / (_x * N_COINS)
xp
是代币数量,所以循环将运行n
次。因此,我们在分母中有D
乘以自身n
次。
然后我们可以计算y或x'_j(即x_j的新值)当用户增加一个储备x_i的值到x'_i时。
使用S=∑x_i和P=∏x_i.假设要计算代币y的值,给定D, A, n;让我们稍微调整一下S和P的公式:
S′和P′是除了我们要计算的代币y之外的所有代币的余额之和和乘积。
公式从(4)变为:
同样,StableSwap使用牛顿法数值求解方程。这次它创建了f(y)和导数f′(y)。
StableSwap的牛顿法求解y为:
将(13)和(14)组合成该方程,可以转换为:
再次重写(16)方法,使分母等于(14):
另一方面,(12)可以重写为f(ADn^n):
将其代入(17),然后消除所有项:
我们将分子和分母都乘以y/(A*n^n):
再次重写不变量(12),并将其代入(20):
公式(23)可以执行为:
在Viper源代码中,定义了两个变量:
将其代入公式(24),我们得到:
Curve源代码中有两个计算y的函数,get_y
和get_Y_D
。`get_Y_D`函数根据D计算y,与上述方法相同。此外,`get_y`函数在一个代币x的数量从x变为x'时计算新的y。回到上面的例子,如果我们想将10,000 USDT兑换成USDC,使用get_y
函数,用户可以获得9,253.70 USDC。这是因为,在该示例中,USDT的数量是USDC的10倍。因此,当用户将USDT兑换为USDC时,会产生滑点。
需要注意的一点是,Curve的白皮书使用的不变量是An^n (A * n ** n
),但在Viper源代码中,不变量是Ann (A * n * n
)。
代码库反而计算A_ * n
。这种差异是因为代码库将A_存储为: