import BigNumber from 'bignumber.js' import moment from 'moment' import { ASSETS } from 'constants/assets' import { BN_ZERO } from 'constants/math' import { ORACLE_DENOM } from 'constants/oracle' import { BNCoin } from 'types/classes/BNCoin' import { byDenom } from 'utils/array' import { getAllAssets } from 'utils/assets' import { BN } from 'utils/helpers' export function truncate(text = '', [h, t]: [number, number] = [6, 6]): string { const head = text.slice(0, h) if (t === 0) return text.length > h + t ? head + '...' : text const tail = text.slice(-1 * t, text.length) if (h === 0) return text.length > h + t ? '...' + tail : text return text.length > h + t ? [head, tail].join('...') : text } export interface FormatOptions { decimals?: number minDecimals?: number maxDecimals?: number thousandSeparator?: boolean prefix?: string suffix?: string rounded?: boolean abbreviated?: boolean } export const produceCountdown = (remainingTime: number) => { const duration = moment.duration(remainingTime, 'milliseconds') const days = formatValue(duration.asDays(), { minDecimals: 0, maxDecimals: 0 }) duration.subtract(days, 'days') const hours = formatValue(duration.asHours(), { minDecimals: 0, maxDecimals: 0 }) duration.subtract(hours, 'hours') const minutes = formatValue(duration.asMinutes(), { minDecimals: 0, maxDecimals: 0 }) return `${days}d ${hours}h ${minutes}m` } export const formatValue = (amount: number | string, options?: FormatOptions): string => { let numberOfZeroDecimals: number | null = null const minDecimals = options?.minDecimals ?? 2 const maxDecimals = options?.maxDecimals ?? 2 let enforcedDecimals = maxDecimals const thousandSeparator = options?.thousandSeparator ?? true if (typeof amount === 'string') { const decimals = amount.split('.')[1] ?? null if (decimals && Number(decimals) === 0) { numberOfZeroDecimals = decimals.length } } let convertedAmount: BigNumber | string = BN(amount).dividedBy(10 ** (options?.decimals ?? 0)) const amountSuffix = options?.abbreviated ? convertedAmount.isGreaterThanOrEqualTo(1_000_000_000) ? 'B' : convertedAmount.isGreaterThanOrEqualTo(1_000_000) ? 'M' : convertedAmount.isGreaterThanOrEqualTo(1_000) ? 'K' : false : '' if (amountSuffix === 'B') { convertedAmount = convertedAmount.dividedBy(1_000_000_000) } if (amountSuffix === 'M') { convertedAmount = convertedAmount.dividedBy(1_000_000) } if (amountSuffix === 'K') { convertedAmount = convertedAmount.dividedBy(1_000) } if (options?.rounded) { convertedAmount = convertedAmount.toFixed(maxDecimals) } else { const amountFractions = convertedAmount.toString().split('.') if (maxDecimals > 0) { if (typeof amountFractions[1] !== 'undefined') { if (amountFractions[1].length >= maxDecimals) { convertedAmount = `${amountFractions[0]}.${amountFractions[1].substr(0, maxDecimals)}` } if (amountFractions[1].length < minDecimals) { convertedAmount = `${amountFractions[0]}.${amountFractions[1].padEnd(minDecimals, '0')}` } } } else { convertedAmount = amountFractions[0] } } if (amountSuffix === 'B' || amountSuffix === 'M' || amountSuffix === 'K') { enforcedDecimals = 2 } if (thousandSeparator) { convertedAmount = BN(convertedAmount).toNumber().toLocaleString('en', { useGrouping: true, minimumFractionDigits: minDecimals, maximumFractionDigits: enforcedDecimals, }) } let returnValue = '' if (numberOfZeroDecimals) { if (numberOfZeroDecimals < enforcedDecimals) { returnValue = Number(returnValue).toFixed(numberOfZeroDecimals) } else { returnValue = Number(returnValue).toFixed(enforcedDecimals) } } if (options?.prefix) { returnValue = `${options.prefix}${returnValue}` } returnValue = `${returnValue}${convertedAmount}` if (amountSuffix) { returnValue = `${returnValue}${amountSuffix}` } if (options?.suffix) { returnValue = `${returnValue}${options.suffix}` } return returnValue } export function formatHealth(health: number) { return formatValue(health, { minDecimals: 0, maxDecimals: 2, }) } export function formatLeverage(leverage: number) { return formatValue(leverage, { minDecimals: 2, suffix: 'x', }) } export function formatPercent(percent: number | string, minDecimals?: number) { return formatValue(+percent * 100, { minDecimals: minDecimals ?? 0, suffix: '%', }) } export function formatAmountWithSymbol(coin: Coin) { const asset = ASSETS.find((asset) => asset.denom === coin.denom) return formatValue(coin.amount, { decimals: asset?.decimals, maxDecimals: asset?.decimals, minDecimals: 0, suffix: ` ${asset?.symbol}`, abbreviated: true, rounded: true, }) } export function formatAmountToPrecision(amount: number | string, decimals: number) { return Number(BN(amount).toPrecision(decimals)) } export const convertPercentage = (percent: number) => { let percentage = percent if (percent >= 100) percentage = 100 if (percent !== 0 && percent < 0.01) percentage = 0.01 return Number(formatValue(percentage, { minDecimals: 0, maxDecimals: 0 })) } export function magnify(amount: number | string, asset: Asset | PseudoAsset) { const _amount = BN(amount) return _amount.isZero() ? _amount : _amount.shiftedBy(asset.decimals) } export function demagnify(amount: number | string | BigNumber, asset: Asset | PseudoAsset) { const _amount = BN(amount) return _amount.isZero() ? 0 : _amount.shiftedBy(-1 * asset.decimals).toNumber() } export function getCoinValue(coin: BNCoin, prices: BNCoin[]) { const asset = getAllAssets().find(byDenom(coin.denom)) const coinPrice = prices.find(byDenom(coin.denom)) if (!coinPrice || !asset) return BN_ZERO const decimals = asset.denom === ORACLE_DENOM ? 0 : asset.decimals * -1 return coin.amount.shiftedBy(decimals).multipliedBy(coinPrice.amount) } export function getCoinAmount(denom: string, value: BigNumber, prices: BNCoin[]) { const asset = getAllAssets().find(byDenom(denom)) const coinPrice = prices.find(byDenom(denom)) if (!coinPrice || !asset) return BN_ZERO const decimals = asset.denom === ORACLE_DENOM ? 0 : asset.decimals return value.dividedBy(coinPrice.amount).shiftedBy(decimals).integerValue() } export function convertLiquidityRateToAPR(rate: number) { const rateMulHundred = rate * 100 return rateMulHundred >= 0.01 ? rateMulHundred : 0.0 }