UNPKG

@raydium-io/raydium-sdk-v2

Version:

An SDK for building applications on top of Raydium.

177 lines (151 loc) 6.24 kB
import BN from "bn.js"; import Decimal from "decimal.js"; import { CurrencyAmount, TokenAmount } from "../module/amount"; import { Currency } from "../module/currency"; import { Fraction } from "../module/fraction"; import { Percent } from "../module/percent"; import { Price } from "../module/price"; import { Token } from "../module/token"; import { SplToken, TokenJson } from "../raydium/token/type"; import { ReplaceType } from "../raydium/type"; import { parseBigNumberish } from "./constant"; import { mul } from "./fractionUtil"; import { notInnerObject } from "./utility"; export const BN_ZERO = new BN(0); export const BN_ONE = new BN(1); export const BN_TWO = new BN(2); export const BN_THREE = new BN(3); export const BN_FIVE = new BN(5); export const BN_TEN = new BN(10); export const BN_100 = new BN(100); export const BN_1000 = new BN(1000); export const BN_10000 = new BN(10000); export type BigNumberish = BN | string | number | bigint; export type Numberish = number | string | bigint | Fraction | BN; export function tenExponential(shift: BigNumberish): BN { return BN_TEN.pow(parseBigNumberish(shift)); } /** * * @example * getIntInfo(0.34) => { numerator: '34', denominator: '100'} * getIntInfo('0.34') //=> { numerator: '34', denominator: '100'} */ export function parseNumberInfo(n: Numberish | undefined): { denominator: string; numerator: string; sign?: string; int?: string; dec?: string; } { if (n === undefined) return { denominator: "1", numerator: "0" }; if (n instanceof BN) { return { numerator: n.toString(), denominator: "1" }; } if (n instanceof Fraction) { return { denominator: n.denominator.toString(), numerator: n.numerator.toString() }; } const s = String(n); const [, sign = "", int = "", dec = ""] = s.replace(",", "").match(/(-?)(\d*)\.?(\d*)/) ?? []; const denominator = "1" + "0".repeat(dec.length); const numerator = sign + (int === "0" ? "" : int) + dec || "0"; return { denominator, numerator, sign, int, dec }; } // round up export function divCeil(a: BN, b: BN): BN { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore const dm = a.divmod(b); // Fast case - exact division if (dm.mod.isZero()) return dm.div; // Round up return dm.div.isNeg() ? dm.div.isubn(1) : dm.div.iaddn(1); } export function shakeFractionDecimal(n: Fraction): string { const [, sign = "", int = ""] = n.toFixed(2).match(/(-?)(\d*)\.?(\d*)/) ?? []; return `${sign}${int}`; } export function toBN(n: Numberish, decimal: BigNumberish = 0): BN { if (n instanceof BN) return n; return new BN(shakeFractionDecimal(toFraction(n).mul(BN_TEN.pow(new BN(String(decimal)))))); } export function toFraction(value: Numberish): Fraction { // to complete math format(may have decimal), not int if (value instanceof Percent) return new Fraction(value.numerator, value.denominator); if (value instanceof Price) return value.adjusted; // to complete math format(may have decimal), not BN if (value instanceof TokenAmount) try { return toFraction(value.toExact()); } catch { return new Fraction(BN_ZERO); } // do not ideal with other fraction value if (value instanceof Fraction) return value; // wrap to Fraction const n = String(value); const details = parseNumberInfo(n); return new Fraction(details.numerator, details.denominator); } export function ceilDiv(tokenAmount: BN, feeNumerator: BN, feeDenominator: BN): BN { return tokenAmount.mul(feeNumerator).add(feeDenominator).sub(new BN(1)).div(feeDenominator); } export function floorDiv(tokenAmount: BN, feeNumerator: BN, feeDenominator: BN): BN { return tokenAmount.mul(feeNumerator).div(feeDenominator); } /** * @example * toPercent(3.14) // => Percent { 314.00% } * toPercent(3.14, { alreadyDecimaled: true }) // => Percent {3.14%} */ export function toPercent( n: Numberish, options?: { /* usually used for backend data */ alreadyDecimaled?: boolean }, ): Percent { const { numerator, denominator } = parseNumberInfo(n); return new Percent(new BN(numerator), new BN(denominator).mul(options?.alreadyDecimaled ? new BN(100) : new BN(1))); } export function toTokenPrice(params: { token: TokenJson | Token | SplToken; numberPrice: Numberish; decimalDone?: boolean; }): Price { const { token, numberPrice, decimalDone } = params; const usdCurrency = new Token({ mint: "", decimals: 6, symbol: "usd", name: "usd", skipMint: true }); const { numerator, denominator } = parseNumberInfo(numberPrice); const parsedNumerator = decimalDone ? new BN(numerator).mul(BN_TEN.pow(new BN(token.decimals))) : numerator; const parsedDenominator = new BN(denominator).mul(BN_TEN.pow(new BN(usdCurrency.decimals))); return new Price({ baseToken: usdCurrency, denominator: parsedDenominator.toString(), quoteToken: new Token({ ...token, skipMint: true, mint: "" }), numerator: parsedNumerator.toString(), }); } export function toUsdCurrency(amount: Numberish): CurrencyAmount { const usdCurrency = new Currency({ decimals: 6, symbol: "usd", name: "usd" }); const amountBigNumber = toBN(mul(amount, 10 ** usdCurrency.decimals)!); return new CurrencyAmount(usdCurrency, amountBigNumber); } export function toTotalPrice(amount: Numberish | undefined, price: Price | undefined): CurrencyAmount { if (!price || !amount) return toUsdCurrency(0); return toUsdCurrency(mul(amount, price)!); } export function decimalToFraction(n: Decimal | undefined): Fraction | undefined { if (n == null) return undefined; const { numerator, denominator } = parseNumberInfo(n.toString()); return new Fraction(numerator, denominator); } export function isDecimal(val: unknown): boolean { return val instanceof Decimal; } // export function recursivelyDecimalToFraction<T>(info: T): ReplaceType<T, Decimal, Fraction> { // // @ts-expect-error no need type for inner code // return isDecimal(info) // ? decimalToFraction(info as any) // : Array.isArray(info) // ? info.map((k) => recursivelyDecimalToFraction(k)) // : notInnerObject(info) // ? Object.fromEntries(Object.entries(info as any).map(([k, v]) => [k, recursivelyDecimalToFraction(v)])) // : info; // }