稳定币兑换简介

稳定币兑换协议核心概念: 做市商原理、流动性池及兑换

背景

随着稳定币的发展,TRON公链上除主流的USDT以外,同时还存在其他稳定币,如USDJ、TUSD、USDC、USDD等。 稳定币市场份额的扩大和种类的增加,催生了稳定币之间兑换的复杂的需求,SUN.io的StableSwap协议定位在稳定币或者等价币之间的交换, 并通过以下两个两种途径,相较于竞品有更低的费率和滑点。

  • 在流动性池中的代币充足的情况下,StableSwap滑点尽可能的小甚至没有;

  • 避免流动性池中某一种代币被全部兑换,StableSwap在某一种代币数量很少的时候会提升兑换价格;

原理简介

如果让交易价格稳定为1,最简单直接的恒等式就是采用一个斜率为-1的直线,即恒定求和公式 x + y = k; 为防止流动性枯竭的问题,需要一个价格自调整的能力,即恒定乘积公式 x * y = k。 为了达到以上途径,StableSwap协议融合了恒定求和与恒定乘积两种方式。可以先简单将其看做恒定求和以及恒定乘积加权求和,即为SUN白皮书中所阐述的做市公式:

Anni=0nxi+D=ADnn+Dn+1nni=1nxiA 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 }

​根据以上作市公式,代币的兑换会影响 xix_i 的值, 以2pool(USDD, USDT)为例,假设交易前数量为 (x1,x2)(x_1, x_2) , 当投入 x1x1x_{1}^{'} - x_1个USDD换取USDT时, x1x_1 的值会变成 x1x_{1}^{'}, 将 x1x_{1}^{'} 代入以上公式可以计算出新的 x2x_{2}^{'}, x2x2x_2 - x_{2}^{'} 即为换取到的USDT的个数,在这个过程中, AADD 都是不变的, AA 值是常量,在给定 xix_i 的情况下,采用牛顿迭代法求解 DD

D1=D0f(D0)f(D0)=D0D0n+1nni=1nxi+D0(Ann1)Anni=1nxi(n+1)D0nnni=1nxi+Ann1=nD0n+1nni=1nxi+Anni=1nxi(n+1)D0nnni=1nxi+Ann1\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*}
S=i=1nxi,Dp=D0D0nnni=1nxi,代入上式得:令 S = \sum_{i=1}^{n} x_i , D_p = \frac{D_0 D_0^n}{ n^n \prod_{i=1}^{n} x_i} , 代入上式得:
D1=(nDp+AnnS)D0(n+1)Dp+(Ann1)D0D_1 =\frac{(n D_p + A n^n S) D_0}{ (n + 1) D_p + (A n^n - 1) D_0}

​对应的合约代码如下:

牛顿迭代法求解D值
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实例后, 就能很方便的与线上合约交互。

初始化TronWeb实例
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,
      })

获取流动性池信息

  • 获取流动性池代币地址

    • 函数名称:coins(uint256)

    • 参数:稳定池中第i个币

>>> let contract = await tronWeb.getContract('TNTfaTpkdd4AQDeqr8SGG7tgdkdjdhbP5c')
>>> await contract.methods.coins(0).call()
TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t
  • 获取流动性池大小

    • 函数名称:balances(uint256)

    • 参数:稳定池中第i个币

>>> let contract = await tronWeb.getContract('TNTfaTpkdd4AQDeqr8SGG7tgdkdjdhbP5c')
>>> await contract.methods.balances(0).call()
1000000000000
  • 获取代币流动性池Token地址

    • 函数名称:lp_token()

    • 参数:无输入参

>>> let contract = await tronWeb.getContract('TNTfaTpkdd4AQDeqr8SGG7tgdkdjdhbP5c')
>>> await contract.methods.lp_token().call()
TD3et9gS2pYz46ZC2mkCfYcKQGNwrnBLef
  • 获取A值

    • 函数名称:A()

    • 参数:无输入参

>>> let contract = await tronWeb.getContract('TNTfaTpkdd4AQDeqr8SGG7tgdkdjdhbP5c')
>>> await contract.methods.A().call()
300

执行交易

  • 获取可兑换出的Token数量

    • 函数名称:get_dy(uint128,uint128,uint256)

    • 参数:出售币在池子中的序号,购买币在池子中的序号,出售币的金额

>>> let contract = await tronWeb.getContract('TNTfaTpkdd4AQDeqr8SGG7tgdkdjdhbP5c')
>>> await contract.methods.get_dy(0, 1, 100).send()
103
  • 兑换

    • 函数名称:exchange(uint128,uint128,uint256,uint256)

    • 参数:出售币在池子中的序号,购买币在池子中的序号,出售币的金额,预期获取最小金额

>>> let contract = await tronWeb.getContract('TNTfaTpkdd4AQDeqr8SGG7tgdkdjdhbP5c')
>>> await contract.methods.exchange(0, 1, 100, 99).send()

增删流动性

  • 增加流动性

    • 函数名称:add_liquidity(uint256[n],uint256)

    • 参数:要添加流动性的所有币的数量,预期获取流动性代币最小额

>>> let contract = await tronWeb.getContract('TNTfaTpkdd4AQDeqr8SGG7tgdkdjdhbP5c')
>>> await contract.methods.add_liquidity(100, 100, 299).send()
  • 删除流动性

    • 函数名称:remove_liquidity(uint256, uint256[n])

    • 参数:要移除流动性代币的数量,预期获取n个币的数量

>>> let contract = await tronWeb.getContract('TNTfaTpkdd4AQDeqr8SGG7tgdkdjdhbP5c')
>>> await contract.methods.remove_liquidity(299.9, 100, 100).send()

Last updated