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);
}
}
