StableSwap Overview

StableSwap protocol keywords: Automated market maker (AMM), liquidity pool and swap

Background

As stablecoins evolve, the popular TRC20-USDT is no longer alone on TRON. TRON's expanding stablecoin family has welcomed USDJ, TUSD, USDC, USDD, and other new members. The growing market share and variety of stablecoins have given rise to more complex demand for swapping between different stablecoins. Sun.io's StableSwap protocol is designed to provide swap services for stablecoins or tokens of equivalent value, and it offers lower fees and slippage than competitors in the market in the following ways:

  • Slippage of StableSwap can be reduced to zero when there are sufficient tokens in the liquidity pool;

  • StableSwap will increase the transaction price of a token to avoid depletion of its supply in the liquidity pool.

Mechanism

To maintain the transaction price at 1, the most straightforward way is to use the equation of a straight line with a slope of -1 (i.e. x + y = k). The price should also be able to adjust itself to avoid liquidity depletion, which requires the constant product formula x * y = k. The StableSwap protocol incorporates both constant sum and constant product. Its model can be simply regarded as the weighted sum of a constant sum and a constant product, as described by the market making formula in the SUN WhitePaper.

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 }

According to the above formula, token swaps have an impact on the value of xix_i. Take 2pool (USDD, USDT) as an example: suppose the amount before the transaction is (x1,x2)(x_1, x_2). When a user swaps x1x1x_{1}^{'} - x_1 USDD for USDT, the value of x1x_1 will change to x1x_{1}^{'}. Substitute x1x_{1}^{'} into the above equation and you will get the new x2x_{2}^{'}, and x2x2x_2 - x_{2}^{'} represents the amount of USDT the user will obtain. The values of both AA and DD remain unchanged during the calculation. AA is a constant, and the value of DD can be found with a given xix_i using Newton's Method.

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*}

Let 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} , Substitute these expressions into the above equation and you can get:

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}

The corresponding contract code is shown below:

Newton's Method
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

Liquidity Pool

The StableSwap contract of Sun.io allows users to swap between stablecoins and add or remove liquidity. Its USDD-USDT liquidity pool, USDJ-TUSD-USDT liquidity pool, and USDC liquidity pool are now available on the TRON MainNet.

USDD-USDT pool

MainNet contract address: TNTfaTpkdd4AQDeqr8SGG7tgdkdjdhbP5c

USDJ-TUSD-USDT pool

MainNet contract address: TKcEU8ekq2ZoFzLSGFYCUY6aocJBX9X31b

USDC pool

MainNet contract address: TNTfaTpkdd4AQDeqr8SGG7tgdkdjdhbP5c

Interact with Contract

We utilize TronWeb to facilitate interaction with on-chain contracts. First, you need to initialize the TronWeb instance.

Initialize the 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,
      })

Get Liquidity Pool's Information

  • Get a liquidity's token address

    • Function: coins(uint256)

    • Parameter: The i-th token of the stablecoin pool

>>> let contract = await tronWeb.getContract('TNTfaTpkdd4AQDeqr8SGG7tgdkdjdhbP5c')
>>> await contract.methods.coins(0).call() 
TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t 
  • Get a liquidity pool's size

    • Function: balances(uint256)

    • Parameter: The i-th token of the stablecoin pool

>>> let contract = await tronWeb.getContract('TNTfaTpkdd4AQDeqr8SGG7tgdkdjdhbP5c')
>>> await contract.methods.balances(0).call()
1000000000000
  • Get a liquidity pool's LP token address

    • Function: lp_token()

    • Parameter: No input parameters

>>> let contract = await tronWeb.getContract('TNTfaTpkdd4AQDeqr8SGG7tgdkdjdhbP5c')
>>> await contract.methods.lp_token().call() 
TD3et9gS2pYz46ZC2mkCfYcKQGNwrnBLef 
  • Get A's value

    • Function: A()

    • Parameter: No input parameters

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

Execute Transaction

  • Get the amount of Token to swap for

    • Function: get_dy(uint128,uint128,uint256)

    • Parameters: In-pool sequence number of the token to be sold, in-pool sequence number of the token to be bought, amount of the token to be sold

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

    • Function: exchange(uint128,uint128,uint256,uint256)

    • Parameters: In-pool sequence number of the token to be sold, in-pool sequence number of the token to be bought, amount of the token to be sold, minimum amount expected to get

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

Add & Remove Liquidity

  • Add liquidity

    • Function: add_liquidity(uint256[n],uint256)

    • Parameters: Amount of all tokens to add, minimum LP token amount expected to get

>>> let contract = await tronWeb.getContract('TNTfaTpkdd4AQDeqr8SGG7tgdkdjdhbP5c')
>>> await contract.methods.add_liquidity(100, 100, 299).send() 
  • Remove liquidity

    • Function: remove_liquidity(uint256, uint256[n])

    • Parameters: LP token amount to remove, minimum token amount expected to get

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

Last updated