vToken APIs

There are three ways to query vToken exchange price:

  1. Use the Bifrost Runtime API, which is the most up-to-date and accurate method.

  2. Call EVM contract on Moonbeam.

  3. API for Frontend (with more vToken related storages)

Bifrost Runtime API (Get vToken Exchange Rate)

https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Fbifrost-polkadot-rpc.dwellir.com#/runtime

vtokenMintingRuntimeApi

  1. getCurrencyAmountByVCurrencyAmount

  2. getVCurrencyAmountByCurrencyAmount

As the showcase above, token2:0 is DOT and vtoken2:0 is vDOT on Bifrost Polkadot, getCurrencyAmountByVCurrencyAmount is to input vDOT amount (with decimals) to query the exchange price in DOT.

Check the Token indexs via here.

Bifrost Runtime API (Get vToken Price from Stable Swap)

Bifrost Runtime API (Get vToken Price from Stable Swap)

_calcOutGivenIn/_calcInGivenOut is the method we use to query vToken prices from the Stable Pool, querying the price from DOT/vDOT stable pool as shown in the example below:

export const postStablePrice = ({
  currentStablePool,
  amount,
  sourceToken,
  type,
}) => {
  const amountIn = bn(amount).multipliedBy(getDecimalsWithZero(sourceToken));
  const { poolBalances, precision, futureA } = currentStablePool;

  const isToken = currentStablePool?.token0 === sourceToken;
  const rateToken = isToken ? currentStablePool.rate0 : currentStablePool.rate1;
  const rateVToken = isToken
    ? currentStablePool.rate1
    : currentStablePool.rate0;

  if (type === 'amountIn') {
    const price = _calcOutGivenIn(
      bn(futureA),
      [bn(poolBalances[0]), bn(poolBalances[1])],
      isToken ? 0 : 1,
      isToken ? 1 : 0,
      amountIn.multipliedBy(rateToken[1]).div(rateToken[0]),
    );

    return price
      .multipliedBy(rateVToken[0])
      .div(rateVToken[1])
      .multipliedBy(0.997)
      .div(precision)
      .toString();
  } else {
    const price = _calcInGivenOut(
      bn(futureA),
      [bn(poolBalances[0]), bn(poolBalances[1])],
      isToken ? 0 : 1,
      isToken ? 1 : 0,
      amountIn.multipliedBy(rateVToken[1]).div(rateVToken[0]),
    );

    return price
      .multipliedBy(rateToken[0])
      .div(rateToken[1])
      .div(0.997)
      .div(precision)
      .toString();
  }
};

Some parameter you need to get from Bifrost storage as explained below:

You can find poolBalances and futureA from stableAsset.pool:

You can find rateToken from stableAsset.tokenRateCaches:

Below is the full example of how we get vDOT-DOT price from Stable Pool:

1. DOT -> vDOT
poolBalances: [6,552,833,658,045,7199,130,072,564,817,861] 
precision: 10,000,000,000 
futureA: 5,000

so  const price = _calcOutGivenIn(
      bn(futureA),
      [bn(poolBalances[0]), bn(poolBalances[1])],
      isToken ? 0 : 1,
      isToken ? 1 : 0,
      amountIn.multipliedBy(rateToken[1]).div(rateToken[0]),
    ) // 10034398626 ;
    
  and final  price
      .multipliedBy(rateVToken[0])
      .div(rateVToken[1])
      .multipliedBy(0.997)
      .div(precision)
      .toString();  // 0.679974070...
      
      
2. if vDOT-> DOT 

price === 14662323117
// price
      .multipliedBy(rateVToken[0])
      .div(rateVToken[1])
      .multipliedBy(0.997)
      .div(precision)
      .toString(); //1.4618336147649
// Computes how many tokens can be taken out of a pool if `tokenAmountIn` are sent, given the current balances.
// The amplification parameter equals: A n^(n-1)
export const _calcOutGivenIn = (
  amplificationParameter: BigNumber,
  balances: BigNumber[],
  tokenIndexIn: number,
  tokenIndexOut: number,
  tokenAmountIn: BigNumber,
  options?: {
    swapFeePercentage: BigNumber;
    tokenInDecimals: number;
  },
): BigNumber => {
  /**************************************************************************************************************
   // outGivenIn token x for y - polynomial equation to solve                                                   //
   // ay = amount out to calculate                                                                              //
   // by = balance token out                                                                                    //
   // y = by - ay (finalBalanceOut)                                                                             //
   // D = invariant                                               D                     D^(n+1)                 //
   // A = amplification coefficient               y^2 + ( S - ----------  - D) * y -  ------------- = 0         //
   // n = number of tokens                                    (A * n^n)               A * n^2n * P              //
   // S = sum of final balances but y                                                                           //
   // P = product of final balances but y                                                                       //
   **************************************************************************************************************/
  // Amount out, so we round down overall.
  if (options) {
    // Fees are subtracted before scaling
    const scalingFactor = 18 - options.tokenInDecimals;
    const scaledAmountIn = scale(tokenAmountIn, -scalingFactor);
    const scaledAmountInWithoutFees = fp.sub(
      scaledAmountIn,
      fp.mulUp(scaledAmountIn, options.swapFeePercentage),
    );

    tokenAmountIn = scale(scaledAmountInWithoutFees, scalingFactor);
  }
  // Given that we need to have a greater final balance out, the invariant needs to be rounded up
  const invariant = _calculateInvariant(amplificationParameter, balances, true);

  balances[tokenIndexIn] = fp.add(balances[tokenIndexIn], tokenAmountIn);

  const finalBalanceOut = _getTokenBalanceGivenInvariantAndAllOtherBalances(
    amplificationParameter,
    balances,
    invariant,
    tokenIndexOut,
  );

  balances[tokenIndexIn] = fp.sub(balances[tokenIndexIn], tokenAmountIn);

  return fp.sub(fp.sub(balances[tokenIndexOut], finalBalanceOut), math.ONE);
};

// Computes how many tokens must be sent to a pool if `tokenAmountOut` are sent given the
// current balances, using the Newton-Raphson approximation.
// The amplification parameter equals: A n^(n-1)
export const _calcInGivenOut = (
  amplificationParameter: BigNumber,
  balances: BigNumber[],
  tokenIndexIn: number,
  tokenIndexOut: number,
  tokenAmountOut: BigNumber,
  options?: {
    swapFeePercentage: BigNumber;
    tokenInDecimals: number;
  },
): BigNumber => {
  /**************************************************************************************************************
   // inGivenOut token x for y - polynomial equation to solve                                                   //
   // ax = amount in to calculate                                                                               //
   // bx = balance token in                                                                                     //
   // x = bx + ax (finalBalanceIn)                                                                              //
   // D = invariant                                                D                     D^(n+1)                //
   // A = amplification coefficient               x^2 + ( S - ----------  - D) * x -  ------------- = 0         //
   // n = number of tokens                                     (A * n^n)               A * n^2n * P             //
   // S = sum of final balances but x                                                                           //
   // P = product of final balances but x                                                                       //
   **************************************************************************************************************/

  // Amount in, so we round up overall.

  // Given that we need to have a greater final balance in, the invariant needs to be rounded up
  const invariant = _calculateInvariant(amplificationParameter, balances, true);

  balances[tokenIndexOut] = fp.sub(balances[tokenIndexOut], tokenAmountOut);

  const finalBalanceIn = _getTokenBalanceGivenInvariantAndAllOtherBalances(
    amplificationParameter,
    balances,
    invariant,
    tokenIndexIn,
  );

  balances[tokenIndexOut] = fp.add(balances[tokenIndexOut], tokenAmountOut);

  let amountIn = fp.add(
    fp.sub(finalBalanceIn, balances[tokenIndexIn]),
    math.ONE,
  );

  if (options) {
    // Fees are added after scaling
    const scalingFactor = 18 - options.tokenInDecimals;
    const scaledAmountIn = scale(amountIn, -scalingFactor);
    const scaledAmountInWithFees = fp.divUp(
      scaledAmountIn,
      fp.sub(fp.ONE, options.swapFeePercentage),
    );
    amountIn = scale(scaledAmountInWithFees, scalingFactor);
  }

  return amountIn;
};

EVM contract on Moonbeam

Query the exchange rate from SLPx contract on Moonbeam:

https://github.com/bifrost-io/slpx-contracts?tab=readme-ov-file#xcmoracle

Check more details at here.

API for Frontend

exchangeRatio is to get Token by vToken

Api 1: Includes the following queryable interfaces

    {
      "contractAddress": "",
      "symbol": "vDOT",
      "slug": "voucher-dot",
      "baseSlug": "polkadot",
      "unstakingTime": 2592000,
      "users": 4455,
      "apr": 15.34,
      "fee": 10,
      "price": 15.0575651785682,
      "exchangeRatio": 1.43132748845706,
      "supply": 7072122.86098662
    },

Api 2: Includes the following queryable interfaces

{
  "tvl": 128783099,
  "addresses": 89168,
  "revenue": 3886150,
    "vDOT": {
    "apy": "15.34",
    "apyBase": "15.17",
    "apyReward": "0.17",
    "tvl": 74398732.4975793,
    "tvm": 7072122.86098662,
    "totalIssuance": 4940953.70767365,
    "holders": 4455,
    "holdersList": [
      {
        "name": "vDOT",
        "holders": 2551,
        "unique_id": "asset_registry/9658598ef1eace56a0662d4a067a260e42b36f2a",
        "network": "bifrost",
        "url": "<https://bifrost.subscan.io/custom_token?unique_id=asset_registry/9658598ef1eace56a0662d4a067a260e42b36f2a>"
      },
      {
        "name": "vDOT",
        "holders": 389,
        "unique_id": "standard_assets/29085784439601774464560083082574142143",
        "network": "moonbeam",
        "url": "<https://moonbeam.subscan.io/assets/29085784439601774464560083082574142143>"
      },
      {
        "name": "vDOT",
        "holders": 223,
        "unique_id": "standard_assets/18446744073709551624",
        "network": "astar",
        "url": "<https://astar.subscan.io/assets/18446744073709551624>"
      },
      {
        "name": "vDOT",
        "holders": 9,
        "unique_id": "standard_assets/313524628741076911470961827389955394913",
        "network": "polkadex",
        "url": "<https://polkadex.subscan.io/assets/313524628741076911470961827389955394913>"
      },
      {
        "name": "vDOT",
        "holders": 1283,
        "unique_id": "asset_registry/37444e63907d968b4a4947cb38ce9c019e6b6310",
        "network": "hydration",
        "url": "<https://hydration.subscan.io/custom_token?unique_id=asset_registry/37444e63907d968b4a4947cb38ce9c019e6b6310>"
      }
    ]
  },

Last updated

Was this helpful?