Skip to content

DEX Perfect Amount Calculation Utils ​

ts
import {
  borrowPerfect,
  depositPerfect,
  paybackPerfect,
  paybackPerfectMax,
  withdrawPerfect,
  withdrawPerfectMax,
} from "./internal/utils";
import type { INft, IVault } from "../../types";

export interface PerfectValues {
  shares: string;
  token0Amt: string;
  token1Amt: string;
  token0AmtWithSlippage: string;
  token1AmtWithSlippage: string;
}

interface PerfectValuesNumber {
  shares: bigint;
  token0Amt: bigint;
  token1Amt: bigint;
  token0AmtWithSlippage: bigint;
  token1AmtWithSlippage: bigint;
}

function isZero(value: string): boolean {
  return value === "" || value === "0";
}

function convertToString(resp: PerfectValuesNumber): PerfectValues {
  return {
    shares: resp.shares.toString(),
    token0Amt: resp.token0Amt.toString(),
    token1Amt: resp.token1Amt.toString(),
    token0AmtWithSlippage: resp.token0AmtWithSlippage.toString(),
    token1AmtWithSlippage: resp.token1AmtWithSlippage.toString(),
  };
}

function negateIfNeeded(
  resp: PerfectValuesNumber,
  paybackAndWithdraw: boolean,
): PerfectValues {
  if (!paybackAndWithdraw) {
    return convertToString(resp);
  }

  const negated: PerfectValuesNumber = {
    shares: -resp.shares,
    token0Amt: -resp.token0Amt,
    token1Amt: -resp.token1Amt,
    token0AmtWithSlippage: -resp.token0AmtWithSlippage,
    token1AmtWithSlippage: -resp.token1AmtWithSlippage,
  };

  return convertToString(negated);
}

/**
 * Perfect max withdraw amounts for a given NFT collateral position.
 */
export function calculatePerfectMaxWithdrawAmounts(params: {
  nft: INft;
  supplySlippage: number;
}): PerfectValues | undefined {
  const { nft, supplySlippage } = params;

  nft.supplyShares = nft.supply;
  const dex = nft.vault.supplyDexData;
  const supplyToken = nft.vault.supplyToken.token0;
  const supplyToken1 = nft.vault.supplyToken.token1;

  if (!supplyToken1) return;

  const resp = withdrawPerfectMax(
    BigInt(nft.supplyShares),
    BigInt(supplyToken.decimals),
    BigInt(supplyToken1.decimals),
    supplySlippage,
    BigInt(dex.totalShares),
    {
      token0RealReserves: BigInt(dex.token0RealReserves),
      token1RealReserves: BigInt(dex.token1RealReserves),
    },
  );

  if (!resp?.token0AmtWithSlippage && !resp?.token1AmtWithSlippage) return;
  return {
    shares: nft.supplyShares,
    token0Amt: resp.token0Amt.toString(),
    token1Amt: resp.token1Amt.toString(),
    token0AmtWithSlippage: resp.token0AmtWithSlippage.toString(),
    token1AmtWithSlippage: resp.token1AmtWithSlippage.toString(),
  };
}

/**
 * Perfect max payback amounts for a given NFT debt position.
 */
export function calculatePerfectMaxPaybackAmounts(params: {
  nft: INft;
  borrowSlippage: number;
}): PerfectValues | undefined {
  const { nft, borrowSlippage } = params;

  nft.borrowShares = nft.borrow;
  const dex = nft.vault.borrowDexData;
  const borrowToken = nft.vault.borrowToken.token0;
  const borrowToken1 = nft.vault.borrowToken.token1;

  if (!borrowToken1) return;

  const resp = paybackPerfectMax(
    BigInt(nft.borrowShares),
    BigInt(borrowToken.decimals ?? 0),
    BigInt(borrowToken1.decimals ?? 0),
    borrowSlippage,
    BigInt(dex.totalShares),
    {
      token0Debt: BigInt(dex.token0Debt),
      token1Debt: BigInt(dex.token1Debt),
    },
  );

  if (!resp?.token0AmtWithSlippage && !resp?.token1AmtWithSlippage) return;
  return {
    shares: nft.borrowShares,
    token0Amt: resp.token0Amt.toString(),
    token1Amt: resp.token1Amt.toString(),
    token0AmtWithSlippage: resp.token0AmtWithSlippage.toString(),
    token1AmtWithSlippage: resp.token1AmtWithSlippage.toString(),
  };
}

/**
 * Perfect collateral amounts for LP-style deposits/withdraws (T2/T4).
 * You only need to pass supply0AmountWei (or supply1AmountWei); the other amount is
 * automatically computed from the pool proportion.
 */
export function calculatePerfectColAmounts(params: {
  vault: IVault;
  supplySlippage: number;
  depositAndBorrow: boolean;
  paybackAndWithdraw: boolean;
  proportion: string;
  supply0AmountWei: string;
  supply1AmountWei: string;
}): PerfectValues | undefined {
  const {
    vault,
    supplySlippage,
    depositAndBorrow,
    paybackAndWithdraw,
    proportion,
    supply0AmountWei,
    supply1AmountWei,
  } = params;

  if (proportion === "variable") return;
  const supplyToken = vault.supplyToken.token0;
  const supplyToken1 = vault.supplyToken.token1;
  if (!supplyToken1 || (isZero(supply0AmountWei) && isZero(supply1AmountWei)))
    return;

  const dex = vault.supplyDexData;
  const fn = depositAndBorrow ? depositPerfect : withdrawPerfect;

  const resp = fn(
    BigInt(supply0AmountWei || "0"),
    BigInt(supply1AmountWei || "0"),
    BigInt(supplyToken.decimals),
    BigInt(supplyToken1.decimals),
    supplySlippage,
    BigInt(dex.totalShares),
    {
      token0ImaginaryReserves: BigInt(dex.token0ImaginaryReserves),
      token0RealReserves: BigInt(dex.token0RealReserves),
      token1ImaginaryReserves: BigInt(dex.token1ImaginaryReserves),
      token1RealReserves: BigInt(dex.token1RealReserves),
    },
  ) as PerfectValuesNumber;

  if (resp.shares && resp.token0AmtWithSlippage) {
    return negateIfNeeded(resp, paybackAndWithdraw);
  }
}

/**
 * Perfect debt amounts for multi-token borrow/payback (T3/T4).
 * You only need to pass borrow0AmountWei (or borrow1AmountWei); the other amount is
 * automatically computed from the pool proportion.
 */
export function calculatePerfectDebtAmounts(params: {
  vault: IVault;
  borrowSlippage: number;
  paybackAndWithdraw: boolean;
  proportion: string;
  borrow0AmountWei: string;
  borrow1AmountWei: string;
}): PerfectValues | undefined {
  const {
    vault,
    borrowSlippage,
    paybackAndWithdraw,
    proportion,
    borrow0AmountWei,
    borrow1AmountWei,
  } = params;

  if (proportion === "variable") return;

  const borrowToken = vault.borrowToken.token0;
  const borrowToken1 = vault.borrowToken.token1;

  if (!borrowToken1 || (isZero(borrow0AmountWei) && isZero(borrow1AmountWei)))
    return;
  const dex = vault.borrowDexData;
  const fn = paybackAndWithdraw ? paybackPerfect : borrowPerfect;

  const resp = fn(
    BigInt(borrow0AmountWei || "0"),
    BigInt(borrow1AmountWei || "0"),
    BigInt(borrowToken.decimals),
    BigInt(borrowToken1.decimals),
    borrowSlippage,
    BigInt(dex.totalShares),
    {
      token0Debt: BigInt(dex.token0Debt),
      token1Debt: BigInt(dex.token1Debt),
      token0ImaginaryReserves: BigInt(dex.token0ImaginaryReserves),
      token0RealReserves: BigInt(dex.token0RealReserves),
      token1ImaginaryReserves: BigInt(dex.token1ImaginaryReserves),
      token1RealReserves: BigInt(dex.token1RealReserves),
    },
  ) as PerfectValuesNumber;

  if (resp.shares && resp.token0AmtWithSlippage) {
    return negateIfNeeded(resp, paybackAndWithdraw);
  }
}