UNPKG

@quantara/sdk

Version:

JavaScript/TypeScript SDK for interacting with Quantara Protocol on Neura Testnet

153 lines (152 loc) 6.58 kB
import { BASIS_POINTS_DIVISOR } from "../configs/factors"; import { getTokenVisualMultiplier, NATIVE_TOKEN_ADDRESS } from "../configs/tokens"; import { applyFactor, PRECISION } from "./numbers"; import { getByKey } from "./objects"; import { convertToContractTokenPrices, convertToUsd, getMidPrice } from "./tokens"; export function getMarketFullName(p) { const { indexToken, longToken, shortToken, isSpotOnly } = p; return `${getMarketIndexName({ indexToken, isSpotOnly })} [${getMarketPoolName({ longToken, shortToken })}]`; } export function getMarketIndexName(p) { const { isSpotOnly } = p; const firstToken = "indexToken" in p ? p.indexToken : p.qntlvToken; if (isSpotOnly) { return `SWAP-ONLY`; } const prefix = getTokenVisualMultiplier(firstToken); return `${prefix}${firstToken.baseSymbol || firstToken.symbol}/USD`; } export function getMarketPoolName(p, separator = "-") { const { longToken, shortToken } = p; return `${longToken.symbol}${separator}${shortToken.symbol}`; } export function getContractMarketPrices(tokensData, market) { const indexToken = getByKey(tokensData, market.indexTokenAddress); const longToken = getByKey(tokensData, market.longTokenAddress); const shortToken = getByKey(tokensData, market.shortTokenAddress); if (!indexToken || !longToken || !shortToken) { return undefined; } return { indexTokenPrice: indexToken && convertToContractTokenPrices(indexToken.prices, indexToken.decimals), longTokenPrice: longToken && convertToContractTokenPrices(longToken.prices, longToken.decimals), shortTokenPrice: shortToken && convertToContractTokenPrices(shortToken.prices, shortToken.decimals), }; } /** * Apart from usual cases, returns `long` for single token backed markets. */ export function getTokenPoolType(marketInfo, tokenAddress) { const { longToken, shortToken } = marketInfo; if (longToken.address === shortToken.address && tokenAddress === longToken.address) { return "long"; } if (tokenAddress === longToken.address || (tokenAddress === NATIVE_TOKEN_ADDRESS && longToken.isWrapped)) { return "long"; } if (tokenAddress === shortToken.address || (tokenAddress === NATIVE_TOKEN_ADDRESS && shortToken.isWrapped)) { return "short"; } return undefined; } export function getPoolUsdWithoutPnl(marketInfo, isLong, priceType) { const poolAmount = isLong ? marketInfo.longPoolAmount : marketInfo.shortPoolAmount; const token = isLong ? marketInfo.longToken : marketInfo.shortToken; let price; if (priceType === "minPrice") { price = token.prices?.minPrice; } else if (priceType === "maxPrice") { price = token.prices?.maxPrice; } else { price = getMidPrice(token.prices); } return convertToUsd(poolAmount, token.decimals, price); } export function getCappedPoolPnl(p) { const { marketInfo, poolUsd, poolPnl, isLong } = p; if (poolPnl < 0) { return poolPnl; } const maxPnlFactor = isLong ? marketInfo.maxPnlFactorForTradersLong : marketInfo.maxPnlFactorForTradersShort; const maxPnl = applyFactor(poolUsd, maxPnlFactor); return poolPnl > maxPnl ? maxPnl : poolPnl; } export function getMaxLeverageByMinCollateralFactor(minCollateralFactor) { if (minCollateralFactor === undefined) return 100 * BASIS_POINTS_DIVISOR; if (minCollateralFactor === 0n) return 100 * BASIS_POINTS_DIVISOR; const x = Number(PRECISION / minCollateralFactor); const rounded = Math.round(x / 10) * 10; return rounded * BASIS_POINTS_DIVISOR; } export function getMaxAllowedLeverageByMinCollateralFactor(minCollateralFactor) { return getMaxLeverageByMinCollateralFactor(minCollateralFactor) / 2; } export function getOppositeCollateral(marketInfo, tokenAddress) { const poolType = getTokenPoolType(marketInfo, tokenAddress); if (poolType === "long") { return marketInfo.shortToken; } if (poolType === "short") { return marketInfo.longToken; } return undefined; } export function getAvailableUsdLiquidityForCollateral(marketInfo, isLong) { const poolUsd = getPoolUsdWithoutPnl(marketInfo, isLong, "minPrice"); if (marketInfo.isSpotOnly) { return poolUsd; } const reservedUsd = getReservedUsd(marketInfo, isLong); const maxReserveFactor = isLong ? marketInfo.reserveFactorLong : marketInfo.reserveFactorShort; if (maxReserveFactor === 0n) { return 0n; } const minPoolUsd = (reservedUsd * PRECISION) / maxReserveFactor; const liquidity = poolUsd - minPoolUsd; return liquidity; } export function getReservedUsd(marketInfo, isLong) { const { indexToken } = marketInfo; if (isLong) { return convertToUsd(marketInfo.longInterestInTokens, marketInfo.indexToken.decimals, indexToken.prices.maxPrice); } else { return marketInfo.shortInterestUsd; } } export function getMarketDivisor({ longTokenAddress, shortTokenAddress, }) { return longTokenAddress === shortTokenAddress ? 2n : 1n; } export function getMarketPnl(marketInfo, isLong, forMaxPoolValue) { const maximize = !forMaxPoolValue; const openInterestUsd = getOpenInterestUsd(marketInfo, isLong); const openInterestInTokens = getOpenInterestInTokens(marketInfo, isLong); if (openInterestUsd === 0n || openInterestInTokens === 0n) { return 0n; } const price = getPriceForPnl(marketInfo.indexToken.prices, isLong, maximize); const openInterestValue = convertToUsd(openInterestInTokens, marketInfo.indexToken.decimals, price); const pnl = isLong ? openInterestValue - openInterestUsd : openInterestUsd - openInterestValue; return pnl; } export function getOpenInterestUsd(marketInfo, isLong) { return isLong ? marketInfo.longInterestUsd : marketInfo.shortInterestUsd; } export function getOpenInterestInTokens(marketInfo, isLong) { return isLong ? marketInfo.longInterestInTokens : marketInfo.shortInterestInTokens; } export function getPriceForPnl(prices, isLong, maximize) { // for long positions, pick the larger price to maximize pnl // for short positions, pick the smaller price to maximize pnl if (isLong) { return maximize ? prices.maxPrice : prices.minPrice; } return maximize ? prices.minPrice : prices.maxPrice; } export function getIsMarketAvailableForExpressSwaps(marketInfo) { return [marketInfo.indexToken, marketInfo.longToken, marketInfo.shortToken].every((token) => token.hasPriceFeedProvider); }