vToken APIs
Last updated
Was this helpful?
Last updated
Was this helpful?
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)
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)
_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;
};
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.
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>"
}
]
},