vToken APIs
There are three ways to query vToken exchange price:
Use the Bifrost Runtime API, which is the most up-to-date and accurate method.
Call EVM contract on Moonbeam.
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
getCurrencyAmountByVCurrencyAmount
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.
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?