UNPKG

goosefx-amm-sdk

Version:

SDK for the GooseFx AMM

83 lines (67 loc) 2.9 kB
import BN from "bn.js"; import { RoundDirection, SwapWithoutFeesResult, TradingTokenResult } from "./calculator"; import { checkedCeilDiv, checkedRem, ZERO } from "./common"; export class ConstantProductCurve { static swapWithoutFees(sourceAmount: BN, swapSourceAmount: BN, swapDestinationAmount: BN): SwapWithoutFeesResult { const invariant = swapSourceAmount.mul(swapDestinationAmount); const newSwapSourceAmount = swapSourceAmount.add(sourceAmount); const [newSwapDestinationAmount, _newSwapSourceAmount] = checkedCeilDiv(invariant, newSwapSourceAmount); const sourceAmountSwapped = _newSwapSourceAmount.sub(swapSourceAmount); const destinationAmountSwapped = swapDestinationAmount.sub(newSwapDestinationAmount); if (destinationAmountSwapped.isZero()) throw Error("destinationAmountSwapped is zero"); return { sourceAmountSwapped, destinationAmountSwapped, }; } static swapWithoutFeesBaseOut( destinationAmount: BN, swapSourceAmount: BN, swapDestinationAmount: BN, ): SwapWithoutFeesResult { // Ensure inputs are valid if (destinationAmount.isZero()) { throw new Error("destinationAmount is zero"); } if (destinationAmount.gt(swapDestinationAmount)) { throw new Error("destinationAmount exceeds available destination reserve"); } // Numerator: x * Δy const numerator = swapSourceAmount.mul(destinationAmount); // Denominator: y - Δy const denominator = swapDestinationAmount.sub(destinationAmount); if (denominator.isZero()) { throw new Error("denominator is zero"); } // Ceiling division: Δx = ceil((x * Δy) / (y - Δy)) const [sourceAmountSwapped] = checkedCeilDiv(numerator, denominator); return { sourceAmountSwapped, destinationAmountSwapped: destinationAmount, }; } static lpTokensToTradingTokens( lpTokenAmount: BN, lpTokenSupply: BN, swapTokenAmount0: BN, swapTokenAmount1: BN, roundDirection: RoundDirection, ): TradingTokenResult { let tokenAmount0 = lpTokenAmount.mul(swapTokenAmount0).div(lpTokenSupply); let tokenAmount1 = lpTokenAmount.mul(swapTokenAmount1).div(lpTokenSupply); if (roundDirection === RoundDirection.Floor) { return { tokenAmount0, tokenAmount1 }; } else if (roundDirection === RoundDirection.Ceiling) { const tokenRemainder0 = checkedRem(lpTokenAmount.mul(swapTokenAmount0), lpTokenSupply); if (tokenRemainder0.gt(ZERO) && tokenAmount0.gt(ZERO)) { tokenAmount0 = tokenAmount0.add(new BN(1)); } const token1Remainder = checkedRem(lpTokenAmount.mul(swapTokenAmount1), lpTokenSupply); if (token1Remainder.gt(ZERO) && tokenAmount1.gt(ZERO)) { tokenAmount1 = tokenAmount1.add(new BN(1)); } return { tokenAmount0, tokenAmount1 }; } throw Error("roundDirection value error"); } }