背景
随着稳定币的发展,TRON公链上除主流的USDT以外,同时还存在其他稳定币,如USDJ、TUSD、USDC、USDD等。 稳定币市场份额的扩大和种类的增加,催生了稳定币之间兑换的复杂的需求,SUN.io的StableSwap协议定位在稳定币或者等价币之间的交换, 并通过以下两个两种途径,相较于竞品有更低的费率和滑点。
在流动性池中的代币充足的情况下,StableSwap滑点尽可能的小甚至没有;
避免流动性池中某一种代币被全部兑换,StableSwap在某一种代币数量很少的时候会提升兑换价格;
原理简介
如果让交易价格稳定为1,最简单直接的恒等式就是采用一个斜率为-1的直线,即恒定求和公式 x + y = k
; 为防止流动性枯竭的问题,需要一个价格自调整的能力,即恒定乘积公式 x * y = k
。 为了达到以上途径,StableSwap协议融合了恒定求和与恒定乘积两种方式。可以先简单将其看做恒定求和以及恒定乘积加权求和,即为SUN白皮书 中所阐述的做市公式:
A n n ∑ i = 0 n x i + D = A D n n + D n + 1 n n ∏ i = 1 n x i A n^n \sum_{i=0}^{n} x_i + D = A D n^n + \frac { D^{n + 1} } { n^n \prod_{i=1}^{n} x_i } A n n i = 0 ∑ n x i + D = A D n n + n n ∏ i = 1 n x i D n + 1 根据以上作市公式,代币的兑换会影响 x i x_i x i 的值, 以2pool(USDD, USDT)为例,假设交易前数量为 ( x 1 , x 2 ) (x_1, x_2) ( x 1 , x 2 ) , 当投入 x 1 ′ − x 1 x_{1}^{'} - x_1 x 1 ′ − x 1 个USDD换取USDT时, x 1 x_1 x 1 的值会变成 x 1 ′ x_{1}^{'} x 1 ′ , 将 x 1 ′ x_{1}^{'} x 1 ′ 代入以上公式可以计算出新的 x 2 ′ x_{2}^{'} x 2 ′ , x 2 − x 2 ′ x_2 - x_{2}^{'} x 2 − x 2 ′ 即为换取到的USDT的个数,在这个过程中, A A A 和 D D D 都是不变的, A A A 值是常量,在给定 x i x_i x i 的情况下,采用牛顿迭代法求解 D D D 。
D 1 = D 0 − f ( D 0 ) f ′ ( D 0 ) = D 0 − D 0 n + 1 n n ∏ i = 1 n x i + D 0 ( A n n − 1 ) − A n n ∑ i = 1 n x i ( n + 1 ) D 0 n n n ∏ i = 1 n x i + A n n − 1 = n D 0 n + 1 n n ∏ i = 1 n x i + A n n ∑ i = 1 n x i ( n + 1 ) D 0 n n n ∏ i = 1 n x i + A n n − 1 \begin{align*} D_1 &= D_0 - \frac{f(D_0)}{f'(D_0)} \\ &= D_0 - \frac{\frac{D_0^{n + 1}}{n^n \prod_{i=1}^{n} x_i} + D_0 (A n^n - 1) - A n^n \sum_{i=1}^{n} x_i } {\frac{(n + 1) D_0^n}{n^n \prod_{i=1}^{n} x_i} + A n^n - 1} \\ &= \frac{ \frac{n D_0^{n + 1}}{n^n \prod_{i=1}^{n} x_i} + A n^n \sum_{i=1}^{n} x_i} {\frac{(n + 1) D_0^n}{n^n \prod_{i=1}^{n} x_i} + A n^n - 1} \end{align*} D 1 = D 0 − f ′ ( D 0 ) f ( D 0 ) = D 0 − n n ∏ i = 1 n x i ( n + 1 ) D 0 n + A n n − 1 n n ∏ i = 1 n x i D 0 n + 1 + D 0 ( A n n − 1 ) − A n n ∑ i = 1 n x i = n n ∏ i = 1 n x i ( n + 1 ) D 0 n + A n n − 1 n n ∏ i = 1 n x i n D 0 n + 1 + A n n ∑ i = 1 n x i 令 S = ∑ i = 1 n x i , D p = D 0 D 0 n n n ∏ i = 1 n x i , 代入上式得 : 令 S = \sum_{i=1}^{n} x_i , D_p = \frac{D_0 D_0^n}{ n^n \prod_{i=1}^{n} x_i} , 代入上式得: 令 S = i = 1 ∑ n x i , D p = n n ∏ i = 1 n x i D 0 D 0 n , 代入上式得 : 对应的合约代码如下:
Copy def get_D(xp: uint256[N_COINS], amp: uint256) -> uint256:
S: uint256 = 0
for _x in xp:
S += _x
if S == 0:
return 0
Dprev: uint256 = 0
D: uint256 = S
Ann: uint256 = amp * N_COINS
for _i in range(255):
D_P: uint256 = D
for _x in xp:
D_P = D_P * D / (_x * N_COINS)
Dprev = D
D = (Ann * S + D_P * N_COINS) * D / ((Ann - 1) * D + (N_COINS + 1) * D_P)
if D > Dprev:
if D - Dprev <= 1:
break
else:
if Dprev - D <= 1:
break
return D
流动性池
SUN.io的StableSwap合约实现了稳定币兑换和流动性增删的功能, 能支撑多种稳定币之间的兑换,目前在TRON主网上主要部署了USDD-USDT流动性池、USDJ-TUSD-USDT流动性池、USDC流动性池。
USDD-USDT流动性池
主网合约地址: TNTfaTpkdd4AQDeqr8SGG7tgdkdjdhbP5c
USDJ -TUSD-USDT流动性池
主网合约地址: TKcEU8ekq2ZoFzLSGFYCUY6aocJBX9X31b
USDC流动性池
主网合约地址: TNTfaTpkdd4AQDeqr8SGG7tgdkdjdhbP5c
与合约交互
我们利用TronWeb与合约交互, 初始化TronWeb实例后, 就能很方便的与线上合约交互。
Copy const TronWeb = require('tronweb')
const privateKey = process.env.PRIVATE_KEY
const apiKey = process.env.API_KEY
var tronWeb = new TronWeb({
fullHost: "https://api.trongrid.io",
headers: { "TRON-PRO-API-KEY": apiKey },
privateKey: privateKey,
})
获取流动性池信息
Copy >>> let contract = await tronWeb.getContract('TNTfaTpkdd4AQDeqr8SGG7tgdkdjdhbP5c')
>>> await contract.methods.coins(0).call()
TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t
Copy >>> let contract = await tronWeb.getContract('TNTfaTpkdd4AQDeqr8SGG7tgdkdjdhbP5c')
>>> await contract.methods.balances(0).call()
1000000000000
Copy >>> let contract = await tronWeb.getContract('TNTfaTpkdd4AQDeqr8SGG7tgdkdjdhbP5c')
>>> await contract.methods.lp_token().call()
TD3et9gS2pYz46ZC2mkCfYcKQGNwrnBLef
Copy >>> let contract = await tronWeb.getContract('TNTfaTpkdd4AQDeqr8SGG7tgdkdjdhbP5c')
>>> await contract.methods.A().call()
300
执行交易
获取可兑换出的Token数量
函数名称:get_dy(uint128,uint128,uint256)
参数:出售币在池子中的序号,购买币在池子中的序号,出售币的金额
Copy >>> let contract = await tronWeb.getContract('TNTfaTpkdd4AQDeqr8SGG7tgdkdjdhbP5c')
>>> await contract.methods.get_dy(0, 1, 100).send()
103
兑换
函数名称:exchange(uint128,uint128,uint256,uint256)
参数:出售币在池子中的序号,购买币在池子中的序号,出售币的金额,预期获取最小金额
Copy >>> let contract = await tronWeb.getContract('TNTfaTpkdd4AQDeqr8SGG7tgdkdjdhbP5c')
>>> await contract.methods.exchange(0, 1, 100, 99).send()
增删流动性
增加流动性
函数名称:add_liquidity(uint256[n],uint256)
参数:要添加流动性的所有币的数量,预期获取流动性代币最小额
Copy >>> let contract = await tronWeb.getContract('TNTfaTpkdd4AQDeqr8SGG7tgdkdjdhbP5c')
>>> await contract.methods.add_liquidity(100, 100, 299).send()
删除流动性
函数名称:remove_liquidity(uint256, uint256[n])
参数:要移除流动性代币的数量,预期获取n个币的数量
Copy >>> let contract = await tronWeb.getContract('TNTfaTpkdd4AQDeqr8SGG7tgdkdjdhbP5c')
>>> await contract.methods.remove_liquidity(299.9, 100, 100).send()