@quantara/sdk
Version:
JavaScript/TypeScript SDK for interacting with Quantara Protocol on Neura Testnet
148 lines (147 loc) • 7.02 kB
JavaScript
import { BASIS_POINTS_DIVISOR_BIGINT, DEFAULT_ALLOWED_SWAP_SLIPPAGE_BPS } from "../configs/factors";
import { NATIVE_TOKEN_ADDRESS } from "../configs/tokens";
import { bigMath } from "./bigmath";
import { adjustForDecimals, expandDecimals, PRECISION } from "./numbers";
export function parseContractPrice(price, tokenDecimals) {
return price * expandDecimals(1, tokenDecimals);
}
export function convertToContractPrice(price, tokenDecimals) {
return (price / expandDecimals(1, tokenDecimals));
}
export function convertToContractTokenPrices(prices, tokenDecimals) {
return {
min: convertToContractPrice(prices.minPrice, tokenDecimals),
max: convertToContractPrice(prices.maxPrice, tokenDecimals),
};
}
export function convertToTokenAmount(usd, tokenDecimals, price) {
if (usd === undefined || typeof tokenDecimals !== "number" || price === undefined || price <= 0) {
return undefined;
}
return (usd * expandDecimals(1, tokenDecimals)) / price;
}
export function convertToUsd(tokenAmount, tokenDecimals, price) {
if (tokenAmount == undefined || typeof tokenDecimals !== "number" || price === undefined) {
return undefined;
}
return (tokenAmount * price) / expandDecimals(1, tokenDecimals);
}
export function convertBetweenTokens(tokenAmount, fromToken, toToken, maximize) {
if (tokenAmount === undefined || fromToken === undefined || toToken === undefined) {
return undefined;
}
if (getIsEquivalentTokens(fromToken, toToken)) {
return tokenAmount;
}
const fromPrice = maximize ? fromToken.prices.maxPrice : fromToken.prices.minPrice;
const toPrice = maximize ? toToken.prices.minPrice : toToken.prices.maxPrice;
const usd = convertToUsd(tokenAmount, fromToken.decimals, fromPrice);
const amount = convertToTokenAmount(usd, toToken.decimals, toPrice);
return amount;
}
export function getMidPrice(prices) {
return (prices.minPrice + prices.maxPrice) / 2n;
}
export function getIsEquivalentTokens(token1, token2) {
if (token1.address === token2.address) {
return true;
}
if (token1.wrappedAddress === token2.address || token2.wrappedAddress === token1.address) {
return true;
}
if ((token1.isSynthetic || token2.isSynthetic) && token1.symbol === token2.symbol) {
return true;
}
return false;
}
export function getTokenData(tokensData, address, convertTo) {
if (!address || !tokensData?.[address]) {
return undefined;
}
const token = tokensData[address];
if (convertTo === "wrapped" && token.isNative && token.wrappedAddress) {
return tokensData[token.wrappedAddress];
}
if (convertTo === "native" && token.isWrapped) {
return tokensData[NATIVE_TOKEN_ADDRESS];
}
return token;
}
/**
* Even though its not a generic function, it return the same type as the input.
* If `TokenData` is passed, it returns `TokenData`, if `Token` is passed, it returns `Token`.
*/
export function getTokensRatioByAmounts(p) {
const { fromToken, toToken, fromTokenAmount, toTokenAmount } = p;
const adjustedFromAmount = (fromTokenAmount * PRECISION) / expandDecimals(1, fromToken.decimals);
const adjustedToAmount = (toTokenAmount * PRECISION) / expandDecimals(1, toToken.decimals);
const [smallestToken, largestToken, largestAmount, smallestAmount] = adjustedFromAmount > adjustedToAmount
? [fromToken, toToken, adjustedFromAmount, adjustedToAmount]
: [toToken, fromToken, adjustedToAmount, adjustedFromAmount];
const ratio = smallestAmount > 0 ? (largestAmount * PRECISION) / smallestAmount : 0n;
return { ratio, largestToken, smallestToken };
}
export function getTokensRatioByMinOutputAmountAndTriggerPrice(p) {
const { fromToken, toToken, fromTokenAmount, toTokenAmount, triggerPrice, minOutputAmount } = p;
let allowedSwapSlippageBps = DEFAULT_ALLOWED_SWAP_SLIPPAGE_BPS;
let smallestToken = fromToken;
let largestToken = toToken;
let largestAmount = fromTokenAmount;
let smallestAmount = toTokenAmount;
let acceptablePrice = 0n;
let ratio = 0n;
const adjustedFromAmount = (fromTokenAmount * PRECISION) / expandDecimals(1, fromToken.decimals);
const adjustedToAmount = (minOutputAmount * PRECISION) / expandDecimals(1, toToken.decimals);
const adjustedMinOutputAmount = (minOutputAmount * PRECISION) / expandDecimals(1, toToken.decimals);
[smallestToken, largestToken, largestAmount, smallestAmount] =
adjustedFromAmount > adjustedToAmount
? [fromToken, toToken, adjustedFromAmount, adjustedToAmount]
: [toToken, fromToken, adjustedToAmount, adjustedFromAmount];
ratio = smallestAmount > 0 ? (largestAmount * PRECISION) / smallestAmount : 0n;
if (triggerPrice === 0n) {
allowedSwapSlippageBps = DEFAULT_ALLOWED_SWAP_SLIPPAGE_BPS;
acceptablePrice = ratio;
}
else {
const outputAtTriggerPrice = adjustedFromAmount > adjustedToAmount
? (adjustedFromAmount * PRECISION) / triggerPrice
: (adjustedFromAmount * triggerPrice) / PRECISION;
allowedSwapSlippageBps =
((outputAtTriggerPrice - adjustedMinOutputAmount) * BASIS_POINTS_DIVISOR_BIGINT) / outputAtTriggerPrice;
acceptablePrice = ratio;
ratio = triggerPrice;
}
return { ratio, largestToken, smallestToken, allowedSwapSlippageBps, acceptablePrice };
}
export function getAmountByRatio(p) {
const { fromToken, toToken, fromTokenAmount, ratio, shouldInvertRatio, allowedSwapSlippageBps } = p;
if (getIsEquivalentTokens(fromToken, toToken) || fromTokenAmount === 0n) {
return p.fromTokenAmount;
}
const _ratio = shouldInvertRatio ? (PRECISION * PRECISION) / ratio : ratio;
const adjustedDecimalsRatio = adjustForDecimals(_ratio, fromToken.decimals, toToken.decimals);
const amount = (p.fromTokenAmount * adjustedDecimalsRatio) / PRECISION;
const swapSlippageAmount = allowedSwapSlippageBps !== undefined
? bigMath.mulDiv(amount, allowedSwapSlippageBps, BASIS_POINTS_DIVISOR_BIGINT)
: 0n;
return amount - swapSlippageAmount;
}
export function getIsWrap(token1, token2) {
return token1.isNative && token2.isWrapped;
}
export function getIsUnwrap(token1, token2) {
return token1.isWrapped && token2.isNative;
}
export function getIsStake(token1, token2) {
return (token1.isWrapped || token1.isNative) && token2.isStaking;
}
export function getIsUnstake(token1, token2) {
// can't unstake straight to native token
return token1.isStaking && token2.isWrapped;
}
export function getTokensRatioByPrice(p) {
const { fromToken, toToken, fromPrice, toPrice } = p;
const [largestToken, smallestToken, largestPrice, smallestPrice] = fromPrice > toPrice ? [fromToken, toToken, fromPrice, toPrice] : [toToken, fromToken, toPrice, fromPrice];
const ratio = (largestPrice * PRECISION) / smallestPrice;
return { ratio, largestToken, smallestToken };
}