DEX Internal Utils ​
js
/**
* Calculates the shares to be minted for a deposit operation.
* @param {bigint} token0Amt - The amount of token0 to deposit.
* @param {bigint} token1Amt - The amount of token1 to deposit.
* @param {bigint} token0Decimals - The decimals of token0.
* @param {bigint} token1Decimals - The decimals of token1.
* @param {number} slippage - The slippage tolerance as a bigint representing a percentage (e.g., 1 for 100%).
* @param {bigint} dexFee - The DEX fee as a bigint representing a percentage (1e6 for 100%)
* @param {bigint} totalSupplyShares - The total supply of shares before the deposit.
* @param {object} colReserves - The current collateral reserves.
* @param {bigint} colReserves.token0RealReserves - Real reserves of token0 in the collateral pool.
* @param {bigint} colReserves.token1RealReserves - Real reserves of token1 in the collateral pool.
* @param {bigint} colReserves.token0ImaginaryReserves - Imaginary reserves of token0 in the collateral pool.
* @param {bigint} colReserves.token1ImaginaryReserves - Imaginary reserves of token1 in the collateral pool.
* @returns {object} An object containing {shares, sharesWithSlippage, success}.
*/
export function deposit(
token0Amt,
token1Amt,
token0Decimals,
token1Decimals,
slippage,
dexFee,
totalSupplyShares,
colReserves,
) {
// Adjust token amounts to 12 decimal places for internal calculations
const SCALE_FACTOR = 12n;
const token0AmtAdjusted =
(token0Amt * 10n ** SCALE_FACTOR) / 10n ** token0Decimals;
const token1AmtAdjusted =
(token1Amt * 10n ** SCALE_FACTOR) / 10n ** token1Decimals;
// Call depositAdjusted with the adjusted token amounts
return helpers._depositAdjusted(
token0AmtAdjusted,
token1AmtAdjusted,
slippage,
dexFee,
totalSupplyShares,
colReserves,
);
}
/**
* Calculates the shares to be burned for a withdraw operation.
* @param {bigint} token0Amt - The amount of token0 to withdraw.
* @param {bigint} token1Amt - The amount of token1 to withdraw.
* @param {bigint} token0Decimals - The decimals of token0.
* @param {bigint} token1Decimals - The decimals of token1.
* @param {number} slippage - The slippage tolerance as a bigint representing a percentage (e.g., 1 for 100%).
* @param {bigint} dexFee - The DEX fee as a bigint representing a percentage (1e6 for 100%)
* @param {bigint} totalSupplyShares - The total supply of shares before the withdraw.
* @param {object} colReserves - The current collateral reserves.
* @param {bigint} colReserves.token0RealReserves - Real reserves of token0 in the collateral pool.
* @param {bigint} colReserves.token1RealReserves - Real reserves of token1 in the collateral pool.
* @param {bigint} colReserves.token0ImaginaryReserves - Imaginary reserves of token0 in the collateral pool.
* @param {bigint} colReserves.token1ImaginaryReserves - Imaginary reserves of token1 in the collateral pool.
* @param {object} pex - The current price exponent.
* @param {bigint} pex.geometricMean - The geometric mean price.
* @param {bigint} pex.upperRange - The upper range price.
* @param {bigint} pex.lowerRange - The lower range price.
* @returns {object} An object containing {shares, sharesWithSlippage, success}.
*/
export function withdraw(
token0Amt,
token1Amt,
token0Decimals,
token1Decimals,
slippage,
dexFee,
totalSupplyShares,
colReserves,
pex,
) {
// Adjust token amounts to 12 decimal places for internal calculations
const SCALE_FACTOR = 12n;
const token0AmtAdjusted =
(token0Amt * 10n ** SCALE_FACTOR) / 10n ** token0Decimals;
const token1AmtAdjusted =
(token1Amt * 10n ** SCALE_FACTOR) / 10n ** token1Decimals;
return helpers._withdrawAdjusted(
token0AmtAdjusted,
token1AmtAdjusted,
slippage,
dexFee,
totalSupplyShares,
colReserves,
pex,
);
}
/**
* Calculates the output amount for a given input amount and reserves
* @param {bigint} shares - The number of shares to withdraw.
* @param {bigint} withdrawToken0Or1 - The token to withdraw in (0 for token0, 1 for token1).
* @param {bigint} decimals0Or1 - The decimals of the token to withdraw.
* @param {number} slippage - The slippage tolerance. (e.g., 1n for 100%).
* @param {bigint} dexFee - The DEX fee.(1e6 for 100%)
* @param {bigint} totalSupplyShares - The total supply of shares before the withdraw.
* @param {object} colReserves - The current collateral reserves.
* @returns {object} An object containing {tokenAmount, tokenAmountWithSlippage, success}.
*/
export function withdrawMax(
shares,
withdrawToken0Or1,
decimals0Or1,
slippage,
dexFee,
totalSupplyShares,
colReserves,
) {
return helpers._withdrawPerfectInOneToken(
shares,
withdrawToken0Or1,
decimals0Or1,
slippage,
dexFee,
totalSupplyShares,
colReserves,
);
}
/**
* Calculates the shares to be minted for a borrow operation.
* @param {bigint} token0Amt - The amount of token0 to borrow.
* @param {bigint} token1Amt - The amount of token1 to borrow.
* @param {bigint} token0Decimals - The decimals of token0.
* @param {bigint} token1Decimals - The decimals of token1.
* @param {number} slippage - The slippage tolerance. (e.g., 1n for 100%).
* @param {bigint} dexFee - The DEX fee.(1e6 for 100%)
* @param {bigint} totalBorrowShares - The total supply of shares before the borrow.
* @param {object} debtReserves - The current debt reserves.
* @param {object} pex - The current price exponent.
* @returns {object} An object containing {shares, sharesWithSlippage, success}.
*/
export function borrow(
token0Amt,
token1Amt,
token0Decimals,
token1Decimals,
slippage,
dexFee,
totalBorrowShares,
debtReserves,
pex,
) {
// Adjust token amounts to 12 decimal places for internal calculations
const TWELVE_DECIMALS = 12n;
const token0AmtAdjusted =
(token0Amt * 10n ** TWELVE_DECIMALS) / 10n ** BigInt(token0Decimals);
const token1AmtAdjusted =
(token1Amt * 10n ** TWELVE_DECIMALS) / 10n ** BigInt(token1Decimals);
return helpers._borrowAdjusted(
token0AmtAdjusted,
token1AmtAdjusted,
slippage,
dexFee,
totalBorrowShares,
debtReserves,
pex,
);
}
/**
* Calculates the shares to be burned for a payback operation.
* @param {bigint} token0Amt - The amount of token0 to payback.
* @param {bigint} token1Amt - The amount of token1 to payback.
* @param {bigint} token0Decimals - The decimals of token0.
* @param {bigint} token1Decimals - The decimals of token1.
* @param {number} slippage - The slippage tolerance. (e.g., 1n for 100%).
* @param {bigint} dexFee - The DEX fee.(1e6 for 100%)
* @param {bigint} totalBorrowShares - The total supply of shares before the payback.
* @param {object} debtReserves - The current debt reserves.
* @returns {object} An object containing {shares, sharesWithSlippage, success}.
*/
export function payback(
token0Amt,
token1Amt,
token0Decimals,
token1Decimals,
slippage,
dexFee,
totalBorrowShares,
debtReserves,
) {
// Adjust token amounts to 12 decimal places for internal calculations
const TWELVE_DECIMALS = 12n;
const token0AmtAdjusted =
(token0Amt * 10n ** TWELVE_DECIMALS) / 10n ** BigInt(token0Decimals);
const token1AmtAdjusted =
(token1Amt * 10n ** TWELVE_DECIMALS) / 10n ** BigInt(token1Decimals);
return helpers._paybackAdjusted(
token0AmtAdjusted,
token1AmtAdjusted,
slippage,
dexFee,
totalBorrowShares,
debtReserves,
);
}
/**
* Calculates the token amount to be paid back for a payback operation.
* @param {bigint} shares - The amount of shares to burn.
* @param {bigint} paybackToken0Or1 - The token to payback in (0 for token0, 1 for token1).
* @param {bigint} decimals0Or1 - The decimals of the token to payback.
* @param {number} slippage - The slippage tolerance as a decimal (e.g., 1n for 100%).
* @param {bigint} dexFee - The DEX fee as a decimal (1e6 for 100%)
* @param {bigint} totalBorrowShares - The total supply of shares before the payback.
* @param {object} debtReserves - The current debt reserves.
* @param {bigint} debtReserves.token0Debt - Debt of token0 in the debt pool.
* @param {bigint} debtReserves.token1Debt - Debt of token1 in the debt pool.
* @param {bigint} debtReserves.token0RealReserves - Real reserves of token0 in the debt pool.
* @param {bigint} debtReserves.token1RealReserves - Real reserves of token1 in the debt pool.
* @param {bigint} debtReserves.token0ImaginaryReserves - Imaginary reserves of token0 in the debt pool.
* @param {bigint} debtReserves.token1ImaginaryReserves - Imaginary reserves of token1 in the debt pool.
* @returns {object} An object containing {tokenAmount, tokenAmountWithSlippage, success}.
*/
export function paybackMax(
shares,
paybackToken0Or1,
decimals0Or1,
slippage,
dexFee,
totalBorrowShares,
debtReserves,
) {
return helpers._paybackPerfectInOneToken(
shares,
paybackToken0Or1,
decimals0Or1,
slippage,
dexFee,
totalBorrowShares,
debtReserves,
);
}
/**
* Calculates the shares to be minted for a deposit operation without slippage.
* @param {bigint} token0Amt - The amount of token0 to deposit. If > 0, token1Amt must be 0.
* @param {bigint} token1Amt - The amount of token1 to deposit. If > 0, token0Amt must be 0.
* @param {bigint} token0Decimals - The decimals of token0.
* @param {bigint} token1Decimals - The decimals of token1.
* @param {number} slippage - The slippage tolerance in 12 decimal fixed point (e.g., 1n for 100%).
* @param {bigint} totalSupplyShares - The total supply of shares before the deposit.
* @param {object} colReserves - The current collateral reserves.
* @returns {object} An object containing:
* @returns {bigint} shares - The amount of shares to be minted for the deposit.
* @returns {bigint} token0Amt - The actual amount of token0 needed for the deposit to maintain the pool ratio.
* @returns {bigint} token1Amt - The actual amount of token1 needed for the deposit to maintain the pool ratio.
* @returns {bigint} token0AmtWithSlippage - The maximum amount of token0 that could be needed accounting for slippage.
* @returns {bigint} token1AmtWithSlippage - The maximum amount of token1 that could be needed accounting for slippage.
*/
export function depositPerfect(
token0Amt,
token1Amt,
token0Decimals,
token1Decimals,
slippage,
totalSupplyShares,
colReserves,
) {
const r_ = helpers._depositOrWithdrawPerfect(
token0Amt,
token1Amt,
token0Decimals,
token1Decimals,
slippage,
totalSupplyShares,
colReserves,
);
const shares = r_.shares;
token0Amt = r_.token0Amt;
token1Amt = r_.token1Amt;
// Calculate amounts with slippage
const token0AmtWithSlippage = BigInt(
Math.floor(Number(token0Amt) * (1 + slippage)),
);
const token1AmtWithSlippage = BigInt(
Math.floor(Number(token1Amt) * (1 + slippage)),
);
return {
shares,
token0Amt,
token1Amt,
token0AmtWithSlippage,
token1AmtWithSlippage,
};
}
/**
* Calculates the shares to be burned for a withdraw operation without slippage.
* @param {bigint} token0Amt - The amount of token0 to withdraw. If > 0, token1Amt must be 0.
* @param {bigint} token1Amt - The amount of token1 to withdraw. If > 0, token0Amt must be 0.
* @param {bigint} token0Decimals - The decimals of token0.
* @param {bigint} token1Decimals - The decimals of token1.
* @param {number} slippage - The slippage tolerance in 12 decimal fixed point (e.g., 1n for 100%).
* @param {bigint} totalSupplyShares - The total supply of shares before the withdraw.
* @param {object} colReserves - The current collateral reserves.
* @returns {object} An object containing:
* @returns {bigint} shares - The amount of shares to be burned for the withdraw.
* @returns {bigint} token0Amt - The actual amount of token0 needed for the withdraw to maintain the pool ratio.
* @returns {bigint} token1Amt - The actual amount of token1 needed for the withdraw to maintain the pool ratio.
* @returns {bigint} token0AmtWithSlippage - The minimum amount of token0 that could be needed accounting for slippage.
* @returns {bigint} token1AmtWithSlippage - The minimum amount of token1 that could be needed accounting for slippage.
*/
export function withdrawPerfect(
token0Amt,
token1Amt,
token0Decimals,
token1Decimals,
slippage,
totalSupplyShares,
colReserves,
) {
const r_ = helpers._depositOrWithdrawPerfect(
token0Amt,
token1Amt,
token0Decimals,
token1Decimals,
slippage,
totalSupplyShares,
colReserves,
);
const shares = r_.shares;
token0Amt = r_.token0Amt;
token1Amt = r_.token1Amt;
// Calculate amounts with slippage
const token0AmtWithSlippage = BigInt(
Math.floor(Number(token0Amt) * (1 - slippage)),
);
const token1AmtWithSlippage = BigInt(
Math.floor(Number(token1Amt) * (1 - slippage)),
);
return {
shares,
token0Amt,
token1Amt,
token0AmtWithSlippage,
token1AmtWithSlippage,
};
}
/**
* Calculates the token amount to be withdrawn for a withdraw operation without slippage.
* @param {bigint} shares - The amount of shares to burn.
* @param {bigint} token0Decimals - The decimals of token0.
* @param {bigint} token1Decimals - The decimals of token1.
* @param {number} slippage - The slippage tolerance in basis points (e.g., 1n for 100%).
* @param {bigint} totalSupplyShares - The total supply of shares before the withdraw.
* @param {object} colReserves - The current collateral reserves containing BigInt values:
* @param {bigint} colReserves.token0RealReserves - The actual reserves of token0
* @param {bigint} colReserves.token1RealReserves - The actual reserves of token1
* @returns {object} An object containing BigInt values:
* @returns {bigint} token0Amt - The actual amount of token0 needed for the withdraw to maintain the pool ratio.
* @returns {bigint} token1Amt - The actual amount of token1 needed for the withdraw to maintain the pool ratio.
* @returns {bigint} token0AmtWithSlippage - The minimum amount of token0 that could be needed accounting for slippage.
* @returns {bigint} token1AmtWithSlippage - The minimum amount of token1 that could be needed accounting for slippage.
*/
export function withdrawPerfectMax(
shares,
token0Decimals,
token1Decimals,
slippage,
totalSupplyShares,
colReserves,
) {
const PRECISION = 12n; // Standard precision used in the original calculation
// Calculate token amounts adjusted for share ratio
// Multiply before division to maintain precision
const token0AmtAdjusted =
(shares * colReserves.token0RealReserves) / totalSupplyShares;
const token1AmtAdjusted =
(shares * colReserves.token1RealReserves) / totalSupplyShares;
// Adjust for decimals difference
// Using BigInt exponential calculation
const token0Amt =
(token0AmtAdjusted * 10n ** token0Decimals) / 10n ** PRECISION;
const token1Amt =
(token1AmtAdjusted * 10n ** token1Decimals) / 10n ** PRECISION;
const token0AmtWithSlippage = BigInt(
Math.floor(Number(token0Amt) * (1 - slippage)),
);
const token1AmtWithSlippage = BigInt(
Math.floor(Number(token1Amt) * (1 - slippage)),
);
return {
token0Amt,
token1Amt,
token0AmtWithSlippage,
token1AmtWithSlippage,
};
}
/**
* Calculates the shares to be minted for a borrow operation without slippage.
* @param {bigint} token0Amt - The amount of token0 to borrow. If > 0n, token1Amt must be 0n.
* @param {bigint} token1Amt - The amount of token1 to borrow. If > 0n, token0Amt must be 0n.
* @param {bigint} token0Decimals - The decimals of token0.
* @param {bigint} token1Decimals - The decimals of token1.
* @param {number} slippage - The slippage tolerance in basis points (e.g., 1n for 100%).
* @param {bigint} totalBorrowShares - The total supply of shares before the borrow.
* @param {object} debtReserves - The current debt reserves (assumed to contain BigInt values).
* @returns {object} An object containing BigInt values:
* @returns {bigint} shares - The amount of shares to be minted for the borrow.
* @returns {bigint} token0Amt - The actual amount of token0 needed for the borrow to maintain the pool ratio.
* @returns {bigint} token1Amt - The actual amount of token1 needed for the borrow to maintain the pool ratio.
* @returns {bigint} token0AmtWithSlippage - The minimum amount of token0 that could be needed accounting for slippage.
* @returns {bigint} token1AmtWithSlippage - The minimum amount of token1 that could be needed accounting for slippage.
*/
export function borrowPerfect(
token0Amt,
token1Amt,
token0Decimals,
token1Decimals,
slippage,
totalBorrowShares,
debtReserves,
) {
// Call helper export function (assumed to be updated to work with BigInt)
const r_ = helpers._borrowOrPaybackPerfect(
token0Amt,
token1Amt,
token0Decimals,
token1Decimals,
totalBorrowShares,
debtReserves,
);
// No need for toFixed() as we're working with BigInt
const shares = r_.shares;
const finalToken0Amt = r_.token0Amt;
const finalToken1Amt = r_.token1Amt;
// Calculate slippage amounts
const token0AmtWithSlippage = BigInt(
Math.floor(Number(finalToken0Amt) * (1 - slippage)),
);
const token1AmtWithSlippage = BigInt(
Math.floor(Number(finalToken1Amt) * (1 - slippage)),
);
return {
shares,
token0Amt: finalToken0Amt,
token1Amt: finalToken1Amt,
token0AmtWithSlippage,
token1AmtWithSlippage,
};
}
/**
* Calculates the shares to be burned for a payback operation without slippage.
* @param {bigint} token0Amt - The amount of token0 to payback. If > 0n, token1Amt must be 0n.
* @param {bigint} token1Amt - The amount of token1 to payback. If > 0n, token0Amt must be 0n.
* @param {bigint} token0Decimals - The decimals of token0.
* @param {bigint} token1Decimals - The decimals of token1.
* @param {number} slippage - The slippage tolerance in basis points (e.g., 1n for 100%).
* @param {bigint} totalBorrowShares - The total supply of shares before the payback.
* @param {object} debtReserves - The current debt reserves (assumed to contain BigInt values).
* @returns {object} An object containing BigInt values:
* @returns {bigint} shares - The amount of shares to be burned for the payback.
* @returns {bigint} token0Amt - The actual amount of token0 needed for the payback to maintain the pool ratio.
* @returns {bigint} token1Amt - The actual amount of token1 needed for the payback to maintain the pool ratio.
* @returns {bigint} token0AmtWithSlippage - The maximum amount of token0 that could be needed accounting for slippage.
* @returns {bigint} token1AmtWithSlippage - The maximum amount of token1 that could be needed accounting for slippage.
*/
export function paybackPerfect(
token0Amt,
token1Amt,
token0Decimals,
token1Decimals,
slippage,
totalBorrowShares,
debtReserves,
) {
// Call helper export function (assumed to be updated to work with BigInt)
const r_ = helpers._borrowOrPaybackPerfect(
token0Amt,
token1Amt,
token0Decimals,
token1Decimals,
totalBorrowShares,
debtReserves,
);
// No need for toFixed() as we're working with BigInt
const shares = r_.shares;
const finalToken0Amt = r_.token0Amt;
const finalToken1Amt = r_.token1Amt;
// Calculate slippage amounts
const token0AmtWithSlippage = BigInt(
Math.floor(Number(finalToken0Amt) * (1 + slippage)),
);
const token1AmtWithSlippage = BigInt(
Math.floor(Number(finalToken1Amt) * (1 + slippage)),
);
return {
shares,
token0Amt: finalToken0Amt,
token1Amt: finalToken1Amt,
token0AmtWithSlippage,
token1AmtWithSlippage,
};
}
/**
* Calculates the token amount to be paid back for a payback operation without slippage.
* All numeric inputs and outputs are BigInt.
* @param {bigint} shares - The amount of shares to burn.
* @param {bigint} token0Decimals - The decimals of token0.
* @param {bigint} token1Decimals - The decimals of token1.
* @param {number} slippage - The slippage tolerance (e.g., 1n for 100%)
* @param {bigint} totalBorrowShares - The total supply of shares before the payback.
* @param {object} debtReserves - The current debt reserves.
* @param {bigint} debtReserves.token0Debt - The current debt of token0.
* @param {bigint} debtReserves.token1Debt - The current debt of token1.
* @returns {object} An object containing BigInt values:
* @returns {bigint} token0Amt - The actual amount of token0 needed for the payback to maintain the pool ratio.
* @returns {bigint} token1Amt - The actual amount of token1 needed for the payback to maintain the pool ratio.
* @returns {bigint} token0AmtWithSlippage - The maximum amount of token0 that could be needed accounting for slippage.
* @returns {bigint} token1AmtWithSlippage - The maximum amount of token1 that could be needed accounting for slippage.
*/
export function paybackPerfectMax(
shares,
token0Decimals,
token1Decimals,
slippage,
totalBorrowShares,
debtReserves,
) {
// Constants
const PRECISION_DECIMALS = 12n;
const PRECISION_SCALE = 10n ** PRECISION_DECIMALS;
// Calculate base amounts with high precision
const token0AmtAdjusted =
(shares * debtReserves.token0Debt) / totalBorrowShares;
const token1AmtAdjusted =
(shares * debtReserves.token1Debt) / totalBorrowShares;
// Scale to proper token decimals
const token0Scale = 10n ** token0Decimals;
const token1Scale = 10n ** token1Decimals;
const token0Amt = (token0AmtAdjusted * token0Scale) / PRECISION_SCALE;
const token1Amt = (token1AmtAdjusted * token1Scale) / PRECISION_SCALE;
// Calculate amounts with slippage
const token0AmtWithSlippage = BigInt(
Math.floor(Number(token0Amt) * (1 + slippage)),
);
const token1AmtWithSlippage = BigInt(
Math.floor(Number(token1Amt) * (1 + slippage)),
);
return {
token0Amt,
token1Amt,
token0AmtWithSlippage,
token1AmtWithSlippage,
};
}
// ##################### INTERNAL HELPERS #####################
function _getUpdatedColReserves(
newShares,
totalOldShares,
colReserves,
mintOrBurn,
) {
const updatedReserves = {};
if (mintOrBurn) {
// If minting, increase reserves proportionally to new shares
updatedReserves.token0RealReserves =
colReserves.token0RealReserves +
(colReserves.token0RealReserves * newShares) / totalOldShares;
updatedReserves.token1RealReserves =
colReserves.token1RealReserves +
(colReserves.token1RealReserves * newShares) / totalOldShares;
updatedReserves.token0ImaginaryReserves =
colReserves.token0ImaginaryReserves +
(colReserves.token0ImaginaryReserves * newShares) / totalOldShares;
updatedReserves.token1ImaginaryReserves =
colReserves.token1ImaginaryReserves +
(colReserves.token1ImaginaryReserves * newShares) / totalOldShares;
} else {
// If burning, decrease reserves proportionally to burned shares
updatedReserves.token0RealReserves =
colReserves.token0RealReserves -
(colReserves.token0RealReserves * newShares) / totalOldShares;
updatedReserves.token1RealReserves =
colReserves.token1RealReserves -
(colReserves.token1RealReserves * newShares) / totalOldShares;
updatedReserves.token0ImaginaryReserves =
colReserves.token0ImaginaryReserves -
(colReserves.token0ImaginaryReserves * newShares) / totalOldShares;
updatedReserves.token1ImaginaryReserves =
colReserves.token1ImaginaryReserves -
(colReserves.token1ImaginaryReserves * newShares) / totalOldShares;
}
return updatedReserves;
}
// ##################### DEPOSIT #####################
/**
* Calculates swap and deposit amounts
* @param {bigint} c First input parameter
* @param {bigint} d Second input parameter
* @param {bigint} e Third input parameter
* @param {bigint} f Fourth input parameter
* @param {bigint} i Fifth input parameter
* @returns {bigint} Calculated shares
*/
function _getSwapAndDeposit(c, d, e, f, i) {
const SIX_DECIMALS = 1000000n; // 10^6 as BigInt
// temp_ => B/i
let temp = (c * d + d * f + e * i - c * i) / i;
let temp2 = 4n * c * e;
const amtToSwap = (calculateSquareRoot(temp2 + temp * temp) - temp) / 2n;
// Ensure the amount to swap is within reasonable bounds
if (
amtToSwap > (c * (SIX_DECIMALS - 1n)) / SIX_DECIMALS ||
amtToSwap < c / SIX_DECIMALS
) {
throw new Error("SwapAndDepositTooLowOrTooHigh");
}
// temp_ => amt0ToDeposit
temp = c - amtToSwap;
// temp2_ => amt1ToDeposit_
temp2 = (d * amtToSwap) / (e + amtToSwap);
// temp_ => shares1
temp = (temp * 10n ** 18n) / (f + amtToSwap);
// temp2_ => shares1
temp2 = (temp2 * 10n ** 18n) / (i - temp2);
// Return the smaller of temp and temp2
return temp > temp2 ? temp2 : temp;
}
function _depositAdjusted(
token0AmtAdjusted,
token1AmtAdjusted,
slippage,
dexFee,
totalSupplyShares,
colReserves,
) {
const PRECISION = 10n ** 18n;
let temp = 0n;
let temp2 = 0n;
let shares = 0n;
let sharesWithSlippage = 0n;
if (token0AmtAdjusted > 0n && token1AmtAdjusted > 0n) {
// mint shares in equal proportion
// temp_ => expected shares from token0 deposit
temp = (token0AmtAdjusted * PRECISION) / colReserves.token0RealReserves;
// temp2_ => expected shares from token1 deposit
temp2 = (token1AmtAdjusted * PRECISION) / colReserves.token1RealReserves;
if (temp > temp2) {
// use temp2_ shares
shares = (temp2 * totalSupplyShares) / PRECISION;
// temp_ => token0 to swap
temp = ((temp - temp2) * colReserves.token0RealReserves) / PRECISION;
temp2 = 0n;
} else if (temp2 > temp) {
// use temp shares
shares = (temp * totalSupplyShares) / PRECISION;
// temp2 => token1 to swap
temp2 = ((temp2 - temp) * colReserves.token1RealReserves) / PRECISION;
temp = 0n;
} else {
// if equal then throw error as swap will not be needed anymore which can create some issue, better to use depositPerfect in this case
return { shares: 0n, sharesWithSlippage: 0n, success: false };
}
// User deposited in equal proportion here. Hence updating col reserves and the swap will happen on updated col reserves
colReserves = _getUpdatedColReserves(
shares,
totalSupplyShares,
colReserves,
true,
);
totalSupplyShares += shares;
} else if (token0AmtAdjusted > 0n) {
temp = token0AmtAdjusted;
temp2 = 0n;
} else if (token1AmtAdjusted > 0n) {
temp = 0n;
temp2 = token1AmtAdjusted;
} else {
// user sent both amounts as 0
return { shares: 0n, sharesWithSlippage: 0n, success: false };
}
if (temp > 0n) {
// swap token0
temp = _getSwapAndDeposit(
temp, // token0 to divide and swap
colReserves.token1ImaginaryReserves, // token1 imaginary reserves
colReserves.token0ImaginaryReserves, // token0 imaginary reserves
colReserves.token0RealReserves, // token0 real reserves
colReserves.token1RealReserves, // token1 real reserves
);
} else if (temp2 > 0n) {
// swap token1
temp = _getSwapAndDeposit(
temp2, // token1 to divide and swap
colReserves.token0ImaginaryReserves, // token0 imaginary reserves
colReserves.token1ImaginaryReserves, // token1 imaginary reserves
colReserves.token1RealReserves, // token1 real reserves
colReserves.token0RealReserves, // token0 real reserves
);
} else {
// maybe possible to happen due to some precision issue that both are 0
return { shares: 0n, sharesWithSlippage: 0n, success: false };
}
// new shares minted from swap & deposit
temp = (temp * totalSupplyShares) / PRECISION;
// adding fee in case of swap & deposit
// 1 - fee. If fee is 1% then without fee will be BigInt(1e6) - 1e4
// temp => withdraw fee
// const HUNDRED_PERCENT = 10n ** 6n;
temp = (temp * (BigInt(1e6) - dexFee)) / BigInt(1e6);
// final new shares to mint for user
shares += temp;
// Calculate shares with slippage
sharesWithSlippage = BigInt(Math.floor(Number(shares) * (1 - slippage)));
return {
shares,
sharesWithSlippage,
success: true,
};
}
// ##################### DEPOSIT END #####################
// ##################### WITHDRAW #####################
function _getWithdrawAndSwap(c_, d_, e_, f_, g_) {
// Constants
const SIX_DECIMALS = 1000000n;
const EIGHTEEN_DECIMALS = 10n ** 18n;
// temp_ = B/2A = (d * e + 2 * c * d + c * f) / (2 * d)
const temp = (d_ * e_ + 2n * c_ * d_ + c_ * f_) / (2n * d_);
// temp2_ = (((c * f) / d) + c) * g
const temp2 = ((c_ * f_) / d_ + c_) * g_;
// tokenAxa = temp - calculateSquareRoot((temp * temp) - temp2)
const tempSquared = temp * temp;
const tokenAxa = temp - calculateSquareRoot(tempSquared - temp2);
// Ensure the amount to withdraw is within reasonable bounds
const upperBound = (g_ * (SIX_DECIMALS - 1n)) / SIX_DECIMALS;
const lowerBound = g_ / SIX_DECIMALS;
if (tokenAxa > upperBound || tokenAxa < lowerBound) {
throw new Error("WithdrawAndSwapTooLowOrTooHigh");
}
// shares_ = (tokenAxa * 1e18) / c
const shares = (tokenAxa * EIGHTEEN_DECIMALS) / c_;
return shares;
}
/**
* Calculates reserves outside range for a liquidity pool
* @param {bigint} geometricMeanPrice - Geometric mean of upper and lower price bounds
* @param {bigint} priceAtRange - Price at range boundary (upper or lower)
* @param {bigint} reserveX - Real reserves of token X
* @param {bigint} reserveY - Real reserves of token Y
* @returns {[bigint, bigint]} - [reserveXOutside, reserveYOutside]
*/
function _calculateReservesOutsideRange(
geometricMeanPrice,
priceAtRange,
reserveX,
reserveY,
) {
// Scale factor for price precision (equivalent to 1e27 in Solidity)
const SCALE = 10n ** 27n;
// Calculate the three parts of the quadratic equation solution
const part1 = priceAtRange - geometricMeanPrice;
// Calculate part2 with BigInt division and multiplication
const part2 =
(geometricMeanPrice * reserveX + reserveY * SCALE) / (2n * part1);
// Handle potential overflow like in Solidity
let part3 = reserveX * reserveY;
part3 =
part3 < 10n ** 50n ? (part3 * SCALE) / part1 : (part3 / part1) * SCALE;
// Calculate square root for BigInt
// Note: This is an approximate integer square root
// Calculate reserveXOutside
const reserveXOutside = part2 + calculateSquareRoot(part3 + part2 * part2);
// Calculate yb (reserveYOutside)
const reserveYOutside = (reserveXOutside * geometricMeanPrice) / SCALE;
return { reserveXOutside, reserveYOutside };
}
function _withdrawAdjusted(
token0AmtAdjusted,
token1AmtAdjusted,
slippage,
dexFee,
totalSupplyShares,
colReserves,
pex,
) {
const PRECISION = 10n ** 18n;
// const HUNDRED_PERCENT = 10n ** 6n
let temp = 0n;
let temp2 = 0n;
let shares = 0n;
let sharesWithSlippage = 0n;
if (token0AmtAdjusted > 0n && token1AmtAdjusted > 0n) {
// Calculate expected shares for each token
temp = (token0AmtAdjusted * PRECISION) / colReserves.token0RealReserves;
temp2 = (token1AmtAdjusted * PRECISION) / colReserves.token1RealReserves;
if (temp > temp2) {
shares = (temp2 * totalSupplyShares) / PRECISION;
temp = ((temp - temp2) * colReserves.token0RealReserves) / PRECISION;
temp2 = 0n;
} else if (temp2 > temp) {
shares = (temp * totalSupplyShares) / PRECISION;
temp2 = ((temp2 - temp) * colReserves.token1RealReserves) / PRECISION;
temp = 0n;
} else {
return { shares: 0n, sharesWithSlippage: 0n, success: false };
}
// Update reserves and total supply shares
colReserves.token0RealReserves -=
(colReserves.token0RealReserves * shares) / totalSupplyShares;
colReserves.token1RealReserves -=
(colReserves.token1RealReserves * shares) / totalSupplyShares;
colReserves.token0ImaginaryReserves -=
(colReserves.token0ImaginaryReserves * shares) / totalSupplyShares;
colReserves.token1ImaginaryReserves -=
(colReserves.token1ImaginaryReserves * shares) / totalSupplyShares;
totalSupplyShares -= shares;
} else if (token0AmtAdjusted > 0n) {
temp = token0AmtAdjusted;
temp2 = 0n;
} else if (token1AmtAdjusted > 0n) {
temp = 0n;
temp2 = token1AmtAdjusted;
} else {
return { shares: 0n, sharesWithSlippage: 0n, success: false };
}
let token0ImaginaryReservesOutsideRange;
let token1ImaginaryReservesOutsideRange;
// Using BigInt-compatible large number representation
const LARGE_PRECISION = 10n ** 54n;
if (pex.geometricMean < LARGE_PRECISION) {
const ob_ = _calculateReservesOutsideRange(
pex.geometricMean,
pex.upperRange,
colReserves.token0RealReserves - temp,
colReserves.token1RealReserves - temp2,
);
token0ImaginaryReservesOutsideRange = ob_.reserveXOutside;
token1ImaginaryReservesOutsideRange = ob_.reserveYOutside;
} else {
const ob_ = _calculateReservesOutsideRange(
LARGE_PRECISION / pex.geometricMean,
LARGE_PRECISION / pex.lowerRange,
colReserves.token1RealReserves - temp2,
colReserves.token0RealReserves - temp,
);
token0ImaginaryReservesOutsideRange = ob_.reserveYOutside;
token1ImaginaryReservesOutsideRange = ob_.reserveXOutside;
}
if (temp > 0n) {
temp = _getWithdrawAndSwap(
colReserves.token0RealReserves,
colReserves.token1RealReserves,
token0ImaginaryReservesOutsideRange,
token1ImaginaryReservesOutsideRange,
temp,
);
} else if (temp2 > 0n) {
temp = _getWithdrawAndSwap(
colReserves.token1RealReserves,
colReserves.token0RealReserves,
token1ImaginaryReservesOutsideRange,
token0ImaginaryReservesOutsideRange,
temp2,
);
} else {
return { shares: 0n, sharesWithSlippage: 0n, success: false };
}
// Calculate shares to burn from withdraw & swap
temp = (temp * totalSupplyShares) / PRECISION;
// Add fee (using BigInt percentage calculation)
temp = (temp * (BigInt(1e6) + dexFee)) / BigInt(1e6);
// Update shares to burn for user
shares += temp;
// Calculate shares with slippage
sharesWithSlippage = BigInt(Math.floor(Number(shares) * (1 + slippage)));
return {
shares,
sharesWithSlippage,
success: true,
};
}
// ##################### WITHDRAW END #####################
// ##################### WITHDRAW PERFECT IN ONE TOKEN #####################
/**
* Calculates the output amount for a given input amount and reserves
* @param {bigint} amountIn - The amount of input asset
* @param {bigint} iReserveIn - Imaginary token reserve of input amount
* @param {bigint} iReserveOut - Imaginary token reserve of output amount
* @returns {bigint} The calculated output amount
*/
function _getAmountOut(amountIn, iReserveIn, iReserveOut) {
// Calculate numerator and denominator
const numerator = amountIn * iReserveOut;
const denominator = iReserveIn + amountIn;
// Calculate and return the output amount
// Note: Using BigInt division to mimic Solidity's behavior
return numerator / denominator;
}
function _withdrawPerfectInOneToken(
shares,
withdrawToken0Or1,
decimals0Or1,
slippage, // Expecting slippage as fixed point number with 12 decimals
dexFee, // Expecting dexFee as fixed point number with 12 decimals
totalSupplyShares,
colReserves,
) {
let tokenAmount = 0n;
let tokenAmountWithSlippage = 0n;
// Constants for calculations
const PRECISION = 12n;
const BASE = 10n ** PRECISION;
if (
colReserves.token0RealReserves === 0n ||
colReserves.token1RealReserves === 0n
) {
return {
tokenAmount: 0n,
tokenAmountWithSlippage: 0n,
success: false,
};
}
const updatedReserves = _getUpdatedColReserves(
shares,
totalSupplyShares,
colReserves,
false,
);
const token0Amount =
colReserves.token0RealReserves - updatedReserves.token0RealReserves - 1n;
const token1Amount =
colReserves.token1RealReserves - updatedReserves.token1RealReserves - 1n;
if (withdrawToken0Or1 === 0n) {
// Withdraw in token0
tokenAmount = token0Amount;
tokenAmount += _getAmountOut(
token1Amount,
updatedReserves.token1ImaginaryReserves,
updatedReserves.token0ImaginaryReserves,
);
} else if (withdrawToken0Or1 === 1n) {
// Withdraw in token1
tokenAmount = token1Amount;
tokenAmount += _getAmountOut(
token0Amount,
updatedReserves.token0ImaginaryReserves,
updatedReserves.token1ImaginaryReserves,
);
} else {
return {
tokenAmount: 0n,
tokenAmountWithSlippage: 0n,
success: false,
};
}
// Apply DEX fee
tokenAmount = (tokenAmount * (BigInt(1e6) - dexFee)) / BigInt(1e6);
// Adjust decimals
const decimalAdjustment = 10n ** decimals0Or1;
tokenAmount = (tokenAmount * decimalAdjustment) / BASE;
// Apply slippage
tokenAmountWithSlippage = BigInt(
Math.floor(Number(tokenAmount) * (1 - slippage)),
);
return {
tokenAmount,
tokenAmountWithSlippage,
success: true,
};
}
// ##################### WITHDRAW PERFECT IN ONE TOKEN END #####################
// ##################### BORROW #####################
function _getBorrowAndSwap(c, d, e, f, g) {
const E18 = 1n * 10n ** 18n;
// Calculate temp_ = B/2A
const temp = (c * f + d * e + d * g) / (2n * d);
// Calculate temp2_ = C / A
const temp2 = (c * f * g) / d;
// Calculate square root using Newton's method
const sqrtPart = calculateSquareRoot(temp * temp - temp2);
// Calculate tokenAxa = (-B - (B^2 - 4AC)^0.5) / 2A
const tokenAxa = temp - sqrtPart;
// Rounding up borrow shares to mint for user
const shares = ((tokenAxa + 1n) * E18) / c;
return shares;
}
/**
* Calculate square root for BigInt using Newton's method
* @param {bigint} n - Number to calculate square root for
* @returns {bigint} Integer square root
*/
function calculateSquareRoot(n) {
if (n <= 1n) return n;
let x = n;
let y = (x + 1n) / 2n;
while (y < x) {
x = y;
y = (x + n / x) / 2n;
}
return x;
}
function _getUpdateDebtReserves(shares, totalShares, debtReserves, mintOrBurn) {
const updatedDebtReserves = {
token0Debt: 0,
token1Debt: 0,
token0RealReserves: 0,
token1RealReserves: 0,
token0ImaginaryReserves: 0,
token1ImaginaryReserves: 0,
};
if (mintOrBurn) {
updatedDebtReserves.token0Debt =
debtReserves.token0Debt +
(debtReserves.token0Debt * shares) / totalShares;
updatedDebtReserves.token1Debt =
debtReserves.token1Debt +
(debtReserves.token1Debt * shares) / totalShares;
updatedDebtReserves.token0RealReserves =
debtReserves.token0RealReserves +
(debtReserves.token0RealReserves * shares) / totalShares;
updatedDebtReserves.token1RealReserves =
debtReserves.token1RealReserves +
(debtReserves.token1RealReserves * shares) / totalShares;
updatedDebtReserves.token0ImaginaryReserves =
debtReserves.token0ImaginaryReserves +
(debtReserves.token0ImaginaryReserves * shares) / totalShares;
updatedDebtReserves.token1ImaginaryReserves =
debtReserves.token1ImaginaryReserves +
(debtReserves.token1ImaginaryReserves * shares) / totalShares;
} else {
updatedDebtReserves.token0Debt =
debtReserves.token0Debt -
(debtReserves.token0Debt * shares) / totalShares;
updatedDebtReserves.token1Debt =
debtReserves.token1Debt -
(debtReserves.token1Debt * shares) / totalShares;
updatedDebtReserves.token0RealReserves =
debtReserves.token0RealReserves -
(debtReserves.token0RealReserves * shares) / totalShares;
updatedDebtReserves.token1RealReserves =
debtReserves.token1RealReserves -
(debtReserves.token1RealReserves * shares) / totalShares;
updatedDebtReserves.token0ImaginaryReserves =
debtReserves.token0ImaginaryReserves -
(debtReserves.token0ImaginaryReserves * shares) / totalShares;
updatedDebtReserves.token1ImaginaryReserves =
debtReserves.token1ImaginaryReserves -
(debtReserves.token1ImaginaryReserves * shares) / totalShares;
}
return updatedDebtReserves;
}
/**
* Calculates debt reserves for both tokens in a pool
* @param {bigint} geometricMean - Geometric mean of upper and lower price ranges (in 1e27 decimals)
* @param {bigint} lowerPrice - Lower price range (in 1e27 decimals)
* @param {bigint} debtA - Debt amount of token A
* @param {bigint} debtB - Debt amount of token B
* @returns {object} Object containing real and imaginary reserves for both tokens
*/
function _calculateDebtReserves(geometricMean, lowerPrice, debtA, debtB) {
const E27 = 1n * 10n ** 27n;
const SIX_DECIMALS = 1n * 10n ** 6n;
const E25 = 1n * 10n ** 25n;
const E50 = 1n * 10n ** 50n;
// Calculate realDebtReserveB (ry_)
// part1 = ((debtA * geometricMean) - (debtB * 1e27)) / (2 * 1e27)
const part1 = (debtA * geometricMean - debtB * E27) / (2n * E27);
// part2 = (debtA * debtB * lowerPrice) / 1e27
let part2 = debtA * debtB;
part2 = part2 < E50 ? (part2 * lowerPrice) / E27 : (part2 / E27) * lowerPrice;
// Calculate square root of part2 + part1^2 using Newton's method
const realDebtReserveB = calculateSquareRoot(part2 + part1 * part1) + part1;
// Calculate imaginaryDebtReserveB (iry_)
// iry_ = ((ry_ * 1e27) - (debtA * lowerPrice))
let imaginaryDebtReserveB = realDebtReserveB * E27 - debtA * lowerPrice;
if (imaginaryDebtReserveB < SIX_DECIMALS) {
throw new Error("Debt reserves too low");
}
// Adjust imaginaryDebtReserveB based on realDebtReserveB size
if (realDebtReserveB < E25) {
imaginaryDebtReserveB =
(realDebtReserveB * realDebtReserveB * E27) / imaginaryDebtReserveB;
} else {
imaginaryDebtReserveB =
(realDebtReserveB * realDebtReserveB) / (imaginaryDebtReserveB / E27);
}
// Calculate imaginaryDebtReserveA (irx_)
// irx_ = ((iry_ * debtA) / ry_) - debtA
const imaginaryDebtReserveA =
(imaginaryDebtReserveB * debtA) / realDebtReserveB - debtA;
// Calculate realDebtReserveA (rx_)
// rx_ = (irx_ * debtB) / (iry_ + debtB)
const realDebtReserveA =
(imaginaryDebtReserveA * debtB) / (imaginaryDebtReserveB + debtB);
return {
realDebtReserveA,
realDebtReserveB,
imaginaryDebtReserveA,
imaginaryDebtReserveB,
};
}
function _borrowAdjusted(
token0AmtAdjusted,
token1AmtAdjusted,
slippage,
dexFee,
totalBorrowShares,
debtReserves,
pex,
) {
const EIGHTEEN_DECIMALS = 10n ** 18n;
const TWENTY_SEVEN_DECIMALS = 10n ** 27n;
const FIFTY_FOUR_DECIMALS = 10n ** 54n;
let temp;
let temp2;
let shares = 0n;
let sharesWithSlippage = 0n;
if (token0AmtAdjusted > 0n && token1AmtAdjusted > 0n) {
// Mint shares in equal proportion
temp = (token0AmtAdjusted * EIGHTEEN_DECIMALS) / debtReserves.token0Debt;
temp2 = (token1AmtAdjusted * EIGHTEEN_DECIMALS) / debtReserves.token1Debt;
if (temp > temp2) {
shares = (temp2 * totalBorrowShares) / EIGHTEEN_DECIMALS;
temp = ((temp - temp2) * debtReserves.token0Debt) / EIGHTEEN_DECIMALS;
temp2 = 0n;
} else if (temp2 > temp) {
shares = (temp * totalBorrowShares) / EIGHTEEN_DECIMALS;
temp2 = ((temp2 - temp) * debtReserves.token1Debt) / EIGHTEEN_DECIMALS;
temp = 0n;
} else {
return { shares: 0n, sharesWithSlippage: 0n, success: false };
}
// User borrowed in equal proportion here. Hence updating col reserves and the swap will happen on updated col reserves
debtReserves = _getUpdateDebtReserves(
shares,
totalBorrowShares,
debtReserves,
true,
);
totalBorrowShares += shares;
} else if (token0AmtAdjusted > 0n) {
temp = token0AmtAdjusted;
temp2 = 0n;
} else if (token1AmtAdjusted > 0n) {
temp = 0n;
temp2 = token1AmtAdjusted;
} else {
return { shares: 0n, sharesWithSlippage: 0n, success: false };
}
let token0FinalImaginaryReserves;
let token1FinalImaginaryReserves;
if (pex.geometricMean < TWENTY_SEVEN_DECIMALS) {
const ob_ = _calculateDebtReserves(
pex.geometricMean,
pex.lowerRange,
debtReserves.token0Debt + temp,
debtReserves.token1Debt + temp2,
);
token0FinalImaginaryReserves = ob_.imaginaryDebtReserveA;
token1FinalImaginaryReserves = ob_.imaginaryDebtReserveB;
} else {
const ob_ = _calculateDebtReserves(
FIFTY_FOUR_DECIMALS / pex.geometricMean,
FIFTY_FOUR_DECIMALS / pex.upperRange,
debtReserves.token1Debt + temp2,
debtReserves.token0Debt + temp,
);
token0FinalImaginaryReserves = ob_.imaginaryDebtReserveB;
token1FinalImaginaryReserves = ob_.imaginaryDebtReserveA;
}
if (temp > 0n) {
// Swap into token0
temp = _getBorrowAndSwap(
debtReserves.token0Debt,
debtReserves.token1Debt,
token0FinalImaginaryReserves,
token1FinalImaginaryReserves,
temp,
);
} else if (temp2 > 0n) {
// Swap into token1
temp = _getBorrowAndSwap(
debtReserves.token1Debt,
debtReserves.token0Debt,
token1FinalImaginaryReserves,
token0FinalImaginaryReserves,
temp2,
);
} else {
return { shares: 0n, sharesWithSlippage: 0n, success: false };
}
// New shares to mint from borrow & swap
temp = (temp * totalBorrowShares) / EIGHTEEN_DECIMALS;
// Adding fee in case of borrow & swap
temp = (temp * (BigInt(1e6) + dexFee)) / BigInt(1e6);
// Final new shares to mint for user
shares += temp;
// Calculate shares with slippage
sharesWithSlippage = BigInt(Math.floor(Number(shares) * (1 + slippage)));
// Convert to BigInt integers
shares = BigInt(Math.floor(Number(shares)));
sharesWithSlippage = BigInt(Math.floor(Number(sharesWithSlippage)));
return { shares, sharesWithSlippage, success: true };
}
// ##################### BORROW END #####################
// ##################### PAYBACK #####################
function _getSwapAndPayback(c, d, e, f, g) {
const EIGHTEEN_DECIMALS = 10n ** 18n;
// Calculate temp_ as B/A
const temp = (c * f + d * e - f * g - d * g) / d;
// Calculate temp2_ as -AC / A^2
const temp2 = 4n * e * g;
// Calculate the amount to swap
const amtToSwap = (calculateSquareRoot(temp2 + temp * temp) - temp) / 2n;
// Calculate amt0ToPayback
const amt0ToPayback = g - amtToSwap;
// Calculate amt1ToPayback
const amt1ToPayback = (f * amtToSwap) / (e + amtToSwap);
// Calculate shares0
const shares0 = (amt0ToPayback * EIGHTEEN_DECIMALS) / (c - amtToSwap);
// Calculate shares1
const shares1 = (amt1ToPayback * EIGHTEEN_DECIMALS) / (d + amt1ToPayback);
// Return the lower of shares0 and shares1
return shares0 < shares1 ? shares0 : shares1;
}
function _paybackAdjusted(
token0AmtAdjusted,
token1AmtAdjusted,
slippage,
dexFee,
totalBorrowShares,
debtReserves,
) {
const EIGHTEEN_DECIMALS = 10n ** 18n;
let temp;
let temp2;
let shares = 0n;
let sharesWithSlippage = 0n;
if (token0AmtAdjusted > 0n && token1AmtAdjusted > 0n) {
// Calculate expected shares from token0 and token1 payback
temp = (token0AmtAdjusted * EIGHTEEN_DECIMALS) / debtReserves.token0Debt;
temp2 = (token1AmtAdjusted * EIGHTEEN_DECIMALS) / debtReserves.token1Debt;
if (temp > temp2) {
shares = (temp2 * totalBorrowShares) / EIGHTEEN_DECIMALS;
temp = token0AmtAdjusted - (temp2 * token0AmtAdjusted) / temp;
temp2 = 0n;
} else if (temp2 > temp) {
shares = (temp * totalBorrowShares) / EIGHTEEN_DECIMALS;
temp2 = token1AmtAdjusted - (temp * token1AmtAdjusted) / temp2;
temp = 0n;
} else {
return { shares: 0n, sharesWithSlippage: 0n, success: false };
}
// Update debt reserves
debtReserves = _getUpdateDebtReserves(
shares,
totalBorrowShares,
debtReserves,
false,
);
totalBorrowShares -= shares;
} else if (token0AmtAdjusted > 0n) {
temp = token0AmtAdjusted;
temp2 = 0n;
} else if (token1AmtAdjusted > 0n) {
temp = 0n;
temp2 = token1AmtAdjusted;
} else {
return { shares: 0n, sharesWithSlippage: 0n, success: false };
}
if (temp > 0n) {
temp = _getSwapAndPayback(
debtReserves.token0Debt,
debtReserves.token1Debt,
debtReserves.token0ImaginaryReserves,
debtReserves.token1ImaginaryReserves,
temp,
);
} else if (temp2 > 0n) {
temp = _getSwapAndPayback(
debtReserves.token1Debt,
debtReserves.token0Debt,
debtReserves.token1ImaginaryReserves,
debtReserves.token0ImaginaryReserves,
temp2,
);
} else {
return { shares: 0n, sharesWithSlippage: 0n, success: false };
}
// Calculate new shares to burn
temp = (temp * totalBorrowShares) / EIGHTEEN_DECIMALS;
// Handle dexFee with BigInt calculation
temp = (temp * (BigInt(1e6) - dexFee)) / BigInt(1e6);
shares += temp;
sharesWithSlippage = BigInt(Math.floor(Number(shares) * (1 - slippage)));
// Convert to BigInt integers
shares = BigInt(Math.floor(Number(shares)));
sharesWithSlippage = BigInt(Math.floor(Number(sharesWithSlippage)));
return { shares, sharesWithSlippage, success: true };
}
// ##################### PAYBACK END #####################
// ##################### PAYBACK PERFECT IN ONE TOKEN #####################
function _getSwapAndPaybackOneTokenPerfectShares(a, b, c, d, i, j) {
// Calculate reserves outside range
const l = a - i;
const m = b - j;
// Calculate new K or final K
const w = a * b;
// Calculate final reserves
const z = w / l;
const y = w / m;
const v = z - m - d;
const x = (v * y) / (m + v);
// Calculate amount to payback
const tokenAmt = c - x;
return tokenAmt;
}
function _paybackPerfectInOneToken(
shares,
paybackToken0Or1,
decimals0Or1,
slippage,
dexFee,
totalBorrowShares,
debtReserves,
) {
let tokenAmount = 0n;
let tokenAmountWithSlippage = 0n;
const token0CurrentDebt = debtReserves.token0Debt;
const token1CurrentDebt = debtReserves.token1Debt;
// Constants for calculations
const PRECISION = 12n;
const BASE = 10n ** PRECISION;
// Removing debt liquidity in equal proportion
debtReserves = _getUpdateDebtReserves(
shares,
totalBorrowShares,
debtReserves,
false,
);
if (paybackToken0Or1 === 0n) {
// entire payback is in token0
tokenAmount = _getSwapAndPaybackOneTokenPerfectShares(
debtReserves.token0ImaginaryReserves,
debtReserves.token1ImaginaryReserves,
token0CurrentDebt,
token1CurrentDebt,
debtReserves.token0RealReserves,
debtReserves.token1RealReserves,
);
} else if (paybackToken0Or1 === 1n) {
// entire payback is in token1
tokenAmount = _getSwapAndPaybackOneTokenPerfectShares(
debtReserves.token1ImaginaryReserves,
debtReserves.token0ImaginaryReserves,
token1CurrentDebt,
token0CurrentDebt,
debtReserves.token1RealReserves,
debtReserves.token0RealReserves,
);
} else {
return {
tokenAmount: 0n,
tokenAmountWithSlippage: 0n,
success: false,
};
}
// Adjust decimals
const decimalAdjustment = 10n ** decimals0Or1;
tokenAmount = (tokenAmount * decimalAdjustment) / BASE;
// adding fee on paying back in 1 token
tokenAmount = (tokenAmount * (BigInt(1e6) + dexFee)) / BigInt(1e6);
// Apply slippage
tokenAmountWithSlippage = BigInt(
Math.floor(Number(tokenAmount) * (1 + slippage)),
);
return {
tokenAmount,
tokenAmountWithSlippage,
success: true,
};
}
// ##################### PAYBACK PERFECT IN ONE TOKEN END #####################
// ##################### DEPOSIT OR WITHDRAW PERFECT #####################
function _depositOrWithdrawPerfect(
token0Amt,
token1Amt,
token0Decimals,
token1Decimals,
slippage,
totalSupplyShares,
colReserves,
) {
if (
(token0Amt > 0n && token1Amt > 0n) ||
(token0Amt === 0n && token1Amt === 0n)
) {
return {
shares: 0n,
token0Amt: 0n,
token1Amt: 0n,
};
}
let token0AmtAdjusted = 0n;
let token1AmtAdjusted = 0n;
let shares = 0n;
const PRECISION = 12n;
const BASE = 10n ** PRECISION;
if (token0Amt > 0n) {
token0AmtAdjusted = (token0Amt * BASE) / 10n ** token0Decimals;
token1AmtAdjusted =
(token0AmtAdjusted * colReserves.token1RealReserves) /
colReserves.token0RealReserves;
shares =
(token0AmtAdjusted * totalSupplyShares) / colReserves.token0RealReserves;
} else {
token1AmtAdjusted = (token1Amt * BASE) / 10n ** token1Decimals;
token0AmtAdjusted =
(token1AmtAdjusted * colReserves.token0RealReserves) /
colReserves.token1RealReserves;
shares =
(token1AmtAdjusted * totalSupplyShares) / colReserves.token1RealReserves;
}
token0Amt = (token0AmtAdjusted * 10n ** token0Decimals) / BASE;
token1Amt = (token1AmtAdjusted * 10n ** token1Decimals) / BASE;
return {
shares,
token0Amt,
token1Amt,
};
}
// ##################### DEPOSIT OR WITHDRAW PERFECT END #####################
// ##################### BORROW OR PAYBACK PERFECT #####################
function _borrowOrPaybackPerfect(
token0Amt,
token1Amt,
token0Decimals,
token1Decimals,
totalBorrowShares,
debtReserves,
) {
if (
(token0Amt > 0n && token1Amt > 0n) ||
(token0Amt === 0n && token1Amt === 0n)
) {
return {
shares: 0n,
token0Amt: 0n,
token1Amt: 0n,
};
}
let token0AmtAdjusted = 0n;
let token1AmtAdjusted = 0n;
let shares = 0n;
const PRECISION = 12n;
const BASE = 10n ** PRECISION;
if (token0Amt > 0n) {
token0AmtAdjusted = (token0Amt * BASE) / 10n ** token0Decimals;
token1AmtAdjusted =
(token0AmtAdjusted * debtReserves.token1Debt) / debtReserves.token0Debt;
shares = (token0AmtAdjusted * totalBorrowShares) / debtReserves.token0Debt;
} else {
token1AmtAdjusted = (token1Amt * BASE) / 10n ** token1Decimals;
token0AmtAdjusted =
(token1AmtAdjusted * debtReserves.token0Debt) / debtReserves.token1Debt;
shares = (token1AmtAdjusted * totalBorrowShares) / debtReserves.token1Debt;
}
token0Amt = (token0AmtAdjusted * 10n ** token0Decimals) / BASE;
token1Amt = (token1AmtAdjusted * 10n ** token1Decimals) / BASE;
return {
shares,
token0Amt,
token1Amt,
};
}
// ##################### BORROW OR PAYBACK PERFECT END #####################
