UNPKG

@raydium-io/raydium-sdk-v2

Version:

An SDK for building applications on top of Raydium.

203 lines (172 loc) 6.98 kB
import BN from "bn.js"; import Decimal from "decimal.js"; import { divRoundingUp, mulDivCeil, mulDivFloor } from "./bigNum"; import { BN_ZERO, Q64, RESOLUTION, U64_MAX } from "./constants"; import { SqrtPriceMath } from "./sqrtPriceMath"; export class LiquidityMathUtil { static getDeltaAmountAUnsigned(sqrtPriceX64A: BN, sqrtPriceX64B: BN, liquidity: BN, roundUp: boolean): BN { if (sqrtPriceX64A.gt(sqrtPriceX64B)) { [sqrtPriceX64A, sqrtPriceX64B] = [sqrtPriceX64B, sqrtPriceX64A]; } const numerator1 = liquidity.shln(RESOLUTION); const numerator2 = sqrtPriceX64B.sub(sqrtPriceX64A); if (!sqrtPriceX64A.gt(BN_ZERO)) throw Error("!sqrtPriceX64A.gt(BN_ZERO)"); const result = roundUp ? divRoundingUp(mulDivCeil(numerator1, numerator2, sqrtPriceX64B), sqrtPriceX64A) : mulDivFloor(numerator1, numerator2, sqrtPriceX64B).div(sqrtPriceX64A); if (result.gt(U64_MAX)) throw Error("MaxTokenOverflow"); return result; } static getDeltaAmountBUnsigned(sqrtPriceX64A: BN, sqrtPriceX64B: BN, liquidity: BN, roundUp: boolean): BN { if (sqrtPriceX64A.gt(sqrtPriceX64B)) { [sqrtPriceX64A, sqrtPriceX64B] = [sqrtPriceX64B, sqrtPriceX64A]; } const result = roundUp ? mulDivCeil(liquidity, sqrtPriceX64B.sub(sqrtPriceX64A), Q64) : mulDivFloor(liquidity, sqrtPriceX64B.sub(sqrtPriceX64A), Q64); if (result.gt(U64_MAX)) throw Error("MaxTokenOverflow"); return result; } static addDelta(x: BN, y: BN): BN { if (y.isNeg()) { const absY = y.neg(); if (x.lt(absY)) { throw new Error("Liquidity underflow"); } return x.sub(absY); } else { return x.add(y); } } static getLiquidityFromAmountA(sqrtPriceLowerX64: BN, sqrtPriceUpperX64: BN, amountA: BN): BN { if (sqrtPriceLowerX64.gt(sqrtPriceUpperX64)) { [sqrtPriceLowerX64, sqrtPriceUpperX64] = [sqrtPriceUpperX64, sqrtPriceLowerX64]; } const intermediate = mulDivFloor(sqrtPriceLowerX64, sqrtPriceUpperX64, Q64); const priceDelta = sqrtPriceUpperX64.sub(sqrtPriceLowerX64); return mulDivFloor(amountA, intermediate, priceDelta); } static getLiquidityFromAmountB(sqrtPriceLowerX64: BN, sqrtPriceUpperX64: BN, amountB: BN): BN { if (sqrtPriceLowerX64.gt(sqrtPriceUpperX64)) { [sqrtPriceLowerX64, sqrtPriceUpperX64] = [sqrtPriceUpperX64, sqrtPriceLowerX64]; } const priceDelta = sqrtPriceUpperX64.sub(sqrtPriceLowerX64); return mulDivFloor(amountB, Q64, priceDelta); } static getLiquidityFromAmounts( sqrtPriceCurrentX64: BN, sqrtPriceLowerX64: BN, sqrtPriceUpperX64: BN, amountA: BN, amountB: BN, ): BN { if (sqrtPriceLowerX64.gt(sqrtPriceUpperX64)) { [sqrtPriceLowerX64, sqrtPriceUpperX64] = [sqrtPriceUpperX64, sqrtPriceLowerX64]; } if (sqrtPriceCurrentX64.lte(sqrtPriceLowerX64)) { return this.getLiquidityFromAmountA(sqrtPriceLowerX64, sqrtPriceUpperX64, amountA); } else if (sqrtPriceCurrentX64.lt(sqrtPriceUpperX64)) { const liquidityA = this.getLiquidityFromAmountA(sqrtPriceCurrentX64, sqrtPriceUpperX64, amountA); const liquidityB = this.getLiquidityFromAmountB(sqrtPriceLowerX64, sqrtPriceCurrentX64, amountB); return liquidityA.lt(liquidityB) ? liquidityA : liquidityB; } else { return this.getLiquidityFromAmountB(sqrtPriceLowerX64, sqrtPriceUpperX64, amountB); } } static getAmountForLiquidityA(sqrtPriceLowerX64: BN, sqrtPriceUpperX64: BN, liquidity: BN, roundUp: boolean): BN { return SqrtPriceMath.getAmountADeltaUnsigned(sqrtPriceLowerX64, sqrtPriceUpperX64, liquidity, roundUp); } static getAmountForLiquidityB(sqrtPriceLowerX64: BN, sqrtPriceUpperX64: BN, liquidity: BN, roundUp: boolean): BN { return SqrtPriceMath.getAmountBDeltaUnsigned(sqrtPriceLowerX64, sqrtPriceUpperX64, liquidity, roundUp); } static getAmountsForLiquidity( sqrtPriceCurrentX64: BN, sqrtPriceLowerX64: BN, sqrtPriceUpperX64: BN, liquidity: BN, roundUp: boolean, ) { if (sqrtPriceLowerX64.gt(sqrtPriceUpperX64)) { [sqrtPriceLowerX64, sqrtPriceUpperX64] = [sqrtPriceUpperX64, sqrtPriceLowerX64]; } let amountA = BN_ZERO; let amountB = BN_ZERO; if (sqrtPriceCurrentX64.lte(sqrtPriceLowerX64)) { amountA = this.getAmountForLiquidityA(sqrtPriceLowerX64, sqrtPriceUpperX64, liquidity, roundUp); } else if (sqrtPriceCurrentX64.lt(sqrtPriceUpperX64)) { amountA = this.getAmountForLiquidityA(sqrtPriceCurrentX64, sqrtPriceUpperX64, liquidity, roundUp); amountB = this.getAmountForLiquidityB(sqrtPriceLowerX64, sqrtPriceCurrentX64, liquidity, roundUp); } else { amountB = this.getAmountForLiquidityB(sqrtPriceLowerX64, sqrtPriceUpperX64, liquidity, roundUp); } return { amountA, amountB }; } static getAmountsFromLiquidityWithSlippage( sqrtPriceCurrentX64: BN, sqrtPriceX64A: BN, sqrtPriceX64B: BN, liquidity: BN, amountMax: boolean, roundUp: boolean, amountSlippage: number, ): { amountSlippageA: BN; amountSlippageB: BN } { const { amountA, amountB } = this.getAmountsForLiquidity( sqrtPriceCurrentX64, sqrtPriceX64A, sqrtPriceX64B, liquidity, roundUp, ); const coefficient = amountMax ? 1 + amountSlippage : 1 - amountSlippage; const amount0Slippage = new BN(new Decimal(amountA.toString()).mul(coefficient).toFixed(0)); const amount1Slippage = new BN(new Decimal(amountB.toString()).mul(coefficient).toFixed(0)); return { amountSlippageA: amount0Slippage, amountSlippageB: amount1Slippage, }; } static getLiquidityAndAmountsFromAmount({ sqrtPriceCurrentX64, sqrtPriceLowerX64, sqrtPriceUpperX64, amountInfo, }: { sqrtPriceCurrentX64: BN; sqrtPriceLowerX64: BN; sqrtPriceUpperX64: BN; amountInfo: { type: "liquidity" | "amountA" | "amountB"; amount: BN }; }): { liquidity: BN; amountA: BN; amountB: BN } { let liquidity; if (amountInfo.type === "liquidity") { liquidity = amountInfo.amount; } else if (amountInfo.type === "amountA") { liquidity = sqrtPriceCurrentX64.gte(sqrtPriceUpperX64) ? BN_ZERO : this.getLiquidityFromAmountA( BN.max(sqrtPriceCurrentX64, sqrtPriceLowerX64), sqrtPriceUpperX64, amountInfo.amount, ); } else if (amountInfo.type === "amountB") { liquidity = sqrtPriceCurrentX64.lte(sqrtPriceLowerX64) ? BN_ZERO : this.getLiquidityFromAmountB( sqrtPriceLowerX64, BN.min(sqrtPriceCurrentX64, sqrtPriceUpperX64), amountInfo.amount, ); } else { throw Error("amount info type check error"); } return { liquidity, ...this.getAmountsForLiquidity( sqrtPriceCurrentX64, sqrtPriceLowerX64, sqrtPriceUpperX64, amountInfo.amount, true, ), }; } }