"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.depositLeverageCalcsDebtFlash = exports.depositLeverageCalcs = exports.estimateDepositMode = exports.estimateAdjustMode = exports.estimateWithdrawMode = exports.calcBorrowAmount = exports.LeverageOption = void 0;
exports.calculateMultiplyEffects = calculateMultiplyEffects;
exports.calcWithdrawAmounts = calcWithdrawAmounts;
exports.calcAdjustAmounts = calcAdjustAmounts;
exports.withdrawLeverageCalcs = withdrawLeverageCalcs;
exports.adjustDepositLeverageCalcs = adjustDepositLeverageCalcs;
exports.adjustWithdrawLeverageCalcs = adjustWithdrawLeverageCalcs;
exports.withdrawLeverageCalcsCollFlash = withdrawLeverageCalcsCollFlash;
exports.adjustDepositLeverageCalcsDebtFlash = adjustDepositLeverageCalcsDebtFlash;
exports.adjustWithdrawLeverageCalcsCollFlash = adjustWithdrawLeverageCalcsCollFlash;
const decimal_js_1 = __importDefault(require("decimal.js"));
const classes_1 = require("../classes");
const utils_1 = require("../utils");
const closingPositionDiffTolerance = 0.0001;
var LeverageOption;
(function (LeverageOption) {
    LeverageOption["deposit"] = "Deposit";
    LeverageOption["withdraw"] = "Withdraw";
    LeverageOption["adjust"] = "Adjust";
    LeverageOption["close"] = "Close";
})(LeverageOption || (exports.LeverageOption = LeverageOption = {}));
async function calculateMultiplyEffects(getPriceByTokenMintDecimal, { depositAmount, withdrawAmount, deposited, borrowed, debtTokenMint, selectedTokenMint, collTokenMint, targetLeverage, activeLeverageOption, flashLoanFeeRatio, debtBorrowFactorPct, priceCollToDebt, priceDebtToColl, }, logEstimations = false) {
    // calculate estimations for deposit operation
    const { adjustDepositPosition: depositModeEstimatedDepositAmount, adjustBorrowPosition: depositModeEstimatedBorrowAmount, } = (0, exports.estimateDepositMode)({
        priceCollToDebt,
        priceDebtToColl,
        amount: depositAmount,
        targetLeverage,
        selectedTokenMint,
        collTokenMint: collTokenMint,
        flashLoanFee: flashLoanFeeRatio,
    });
    // calculate estimations for withdraw operation
    const { adjustDepositPosition: withdrawModeEstimatedDepositTokenWithdrawn, adjustBorrowPosition: withdrawModeEstimatedBorrowTokenWithdrawn, } = (0, exports.estimateWithdrawMode)({
        priceCollToDebt: priceCollToDebt,
        collTokenMint,
        selectedTokenMint,
        amount: withdrawAmount,
        deposited: new decimal_js_1.default(deposited),
        borrowed: new decimal_js_1.default(borrowed),
    });
    // calculate estimations for adjust operation
    const { adjustDepositPosition: adjustModeEstimatedDepositAmount, adjustBorrowPosition: adjustModeEstimateBorrowAmount, } = (0, exports.estimateAdjustMode)(priceCollToDebt, {
        targetLeverage,
        debtTokenMint,
        collTokenMint,
        totalDeposited: new decimal_js_1.default(deposited),
        totalBorrowed: new decimal_js_1.default(borrowed),
        flashLoanFee: flashLoanFeeRatio, // TODO: is this the right flash borrow?
    });
    if (logEstimations) {
        console.log('Estimations', (0, classes_1.toJson)({
            activeLeverageOption,
            depositModeEstimatedDepositAmount,
            depositModeEstimatedBorrowAmount,
            withdrawModeEstimatedDepositTokenWithdrawn,
            withdrawModeEstimatedBorrowTokenWithdrawn,
            adjustModeEstimatedDepositAmount,
            adjustModeEstimateBorrowAmount,
        }));
    }
    let [isClosingPosition, totalDeposited, totalBorrowed] = [false, new decimal_js_1.default(0), new decimal_js_1.default(0)];
    switch (activeLeverageOption) {
        case LeverageOption.deposit: {
            // Deposit and Adjust never clos the position
            isClosingPosition = false;
            totalDeposited = deposited.add(depositModeEstimatedDepositAmount);
            totalBorrowed = borrowed.add(depositModeEstimatedBorrowAmount);
            break;
        }
        case LeverageOption.close:
        case LeverageOption.withdraw: {
            isClosingPosition =
                (withdrawModeEstimatedDepositTokenWithdrawn.gte(new decimal_js_1.default(deposited)) ||
                    withdrawModeEstimatedBorrowTokenWithdrawn.gte(new decimal_js_1.default(borrowed)) ||
                    (0, utils_1.fuzzyEqual)(withdrawModeEstimatedDepositTokenWithdrawn, new decimal_js_1.default(deposited), closingPositionDiffTolerance) ||
                    (0, utils_1.fuzzyEqual)(withdrawModeEstimatedBorrowTokenWithdrawn, new decimal_js_1.default(borrowed), closingPositionDiffTolerance)) &&
                    !(0, utils_1.fuzzyEqual)(withdrawModeEstimatedDepositTokenWithdrawn, new decimal_js_1.default(0), closingPositionDiffTolerance);
            totalDeposited = isClosingPosition ? new decimal_js_1.default(0) : deposited.sub(withdrawModeEstimatedDepositTokenWithdrawn);
            totalBorrowed = isClosingPosition ? new decimal_js_1.default(0) : borrowed.sub(withdrawModeEstimatedBorrowTokenWithdrawn);
            break;
        }
        case LeverageOption.adjust: {
            // Deposit and Adjust never clos the position
            isClosingPosition = false;
            totalDeposited = deposited.add(adjustModeEstimatedDepositAmount);
            totalBorrowed = borrowed.add(adjustModeEstimateBorrowAmount);
            break;
        }
    }
    const borrowTokenPrice = await getPriceByTokenMintDecimal(debtTokenMint);
    const depositTokenPrice = await getPriceByTokenMintDecimal(collTokenMint);
    const totalDepositedUsd = depositTokenPrice.mul(totalDeposited);
    const totalBorrowedUsd = borrowTokenPrice.mul(totalBorrowed);
    const netValueUsd = totalDepositedUsd.minus(totalBorrowedUsd);
    // TODO marius this is bad, do not convert to sol as we don't only do leveraged loops only
    const netValueSol = netValueUsd.div(borrowTokenPrice);
    const ltv = totalBorrowedUsd.mul(debtBorrowFactorPct.div(100)).div(totalDepositedUsd);
    return {
        earned: new decimal_js_1.default(0),
        totalDeposited,
        totalBorrowed,
        netValue: netValueSol,
        netValueUsd: netValueUsd,
        ltv,
    };
}
/**
 * returns how much borrowToken will be borrowed to reach leverage given initial collateral amount
 * @param depositTokenAmount
 * @param leverage
 * @param priceAToB
 * @param flashBorrowFee
 */
const calcBorrowAmount = ({ depositTokenAmount, targetLeverage, priceCollToDebt, flashLoanFeeRatio, }) => {
    const initialCollAmountInCollToken = depositTokenAmount;
    const finalCollAmountInCollToken = initialCollAmountInCollToken.mul(targetLeverage);
    const finalDebtAmountInCollToken = finalCollAmountInCollToken.sub(initialCollAmountInCollToken);
    const finalDebtAmountInDebtToken = finalDebtAmountInCollToken.mul(priceCollToDebt);
    const flashFeeFactor = new decimal_js_1.default(1).add(flashLoanFeeRatio);
    const debtTokenToBorrow = finalDebtAmountInDebtToken.mul(flashFeeFactor);
    return debtTokenToBorrow;
};
exports.calcBorrowAmount = calcBorrowAmount;
const estimateWithdrawMode = (props) => {
    const { amount, collTokenMint, selectedTokenMint, deposited, borrowed, priceCollToDebt } = props;
    return calcWithdrawAmounts({
        selectedTokenMint,
        collTokenMint,
        withdrawAmount: new decimal_js_1.default(amount),
        priceCollToDebt,
        currentBorrowPosition: borrowed,
        currentDepositPosition: deposited,
    });
};
exports.estimateWithdrawMode = estimateWithdrawMode;
function calcWithdrawAmounts(params) {
    const { currentBorrowPosition, currentDepositPosition, priceCollToDebt, withdrawAmount, selectedTokenMint, collTokenMint, } = params;
    // MSOL/SOL
    const currentDepositInCollateralToken = currentDepositPosition;
    const currentDebtInCollateralToken = currentBorrowPosition.div(priceCollToDebt);
    const currentNetPositionInCollateralToken = currentDepositInCollateralToken.minus(currentDebtInCollateralToken);
    const targetLeverage = currentDepositInCollateralToken.div(currentNetPositionInCollateralToken);
    const initialDepositInCollateralToken = currentDepositPosition.minus(currentBorrowPosition.div(priceCollToDebt));
    const amountToWithdrawDepositToken = selectedTokenMint === collTokenMint ? withdrawAmount : withdrawAmount.div(priceCollToDebt);
    const targetDeposit = initialDepositInCollateralToken.minus(amountToWithdrawDepositToken).mul(targetLeverage);
    const targetBorrow = (0, exports.calcBorrowAmount)({
        depositTokenAmount: initialDepositInCollateralToken.minus(amountToWithdrawDepositToken),
        priceCollToDebt: new decimal_js_1.default(priceCollToDebt),
        targetLeverage: new decimal_js_1.default(targetLeverage),
        flashLoanFeeRatio: new decimal_js_1.default(0),
    });
    const adjustDepositPosition = currentDepositPosition.minus(targetDeposit);
    const adjustBorrowPosition = currentBorrowPosition.minus(targetBorrow);
    // TODO: add flashLoan fee here in final values
    return {
        adjustDepositPosition,
        adjustBorrowPosition,
    };
}
/**
 * Calculate how much token will be deposited or withdrawn in case of position adjustment
 * @param leverage
 * @param totalDeposited
 * @param totalBorrowed
 */
const estimateAdjustMode = (priceCollToDebt, { targetLeverage, totalDeposited, totalBorrowed, flashLoanFee }) => {
    return calcAdjustAmounts({
        currentBorrowPosition: totalBorrowed,
        currentDepositPosition: totalDeposited,
        priceCollToDebt,
        targetLeverage,
        flashLoanFee,
    });
};
exports.estimateAdjustMode = estimateAdjustMode;
/**
 * Calculates the amounts of tokenA to deposit/withdraw and tokenB to borrow/repay proportionally to adjust the leverage of a position.
 *
 * @param {AdjustLeverageParams} params - Parameters for the calculation
 * @param {number} params.targetLeverage - The target leverage for the position
 * @param {Decimal} params.currentPositionTokenA - The current amount of tokenA in the position
 * @param {Decimal} params.currentPositionTokenB - The current amount of borrowed tokenB in the position
 * @param {number} params.priceAtoB - The conversion rate from tokenA to tokenB (tokenA price = tokenB price * priceAtoB)
 * @returns {AdjustLeverageResult} An object containing the amounts of tokenA to deposit/withdraw and tokenB to borrow/repay
 */
function calcAdjustAmounts({ targetLeverage, currentBorrowPosition, currentDepositPosition, priceCollToDebt, flashLoanFee, }) {
    const initialDeposit = currentDepositPosition.minus(currentBorrowPosition.div(priceCollToDebt));
    const targetDeposit = initialDeposit.mul(targetLeverage);
    const targetBorrow = (0, exports.calcBorrowAmount)({
        depositTokenAmount: initialDeposit,
        priceCollToDebt: new decimal_js_1.default(priceCollToDebt),
        targetLeverage: new decimal_js_1.default(targetLeverage),
        flashLoanFeeRatio: flashLoanFee,
    });
    const adjustDepositPosition = targetDeposit.minus(currentDepositPosition);
    const adjustBorrowPosition = targetBorrow.minus(currentBorrowPosition);
    return {
        adjustDepositPosition,
        adjustBorrowPosition,
    };
}
// Given a deposit amount of Deposit|Borrow token
// and a target leverage, calculate final { collateral, debt } value
const estimateDepositMode = ({ priceCollToDebt, priceDebtToColl, amount, targetLeverage, selectedTokenMint, collTokenMint, flashLoanFee, slippagePct = new decimal_js_1.default(0), }) => {
    const isDepositingCollToken = selectedTokenMint === collTokenMint;
    const finalCollTokenAmount = isDepositingCollToken
        ? new decimal_js_1.default(amount).mul(targetLeverage).toNumber()
        : new decimal_js_1.default(amount).mul(priceDebtToColl).mul(targetLeverage).toNumber();
    const depositCollTokenAmount = isDepositingCollToken ? amount : amount.mul(priceDebtToColl);
    const borrowAmount = (0, exports.calcBorrowAmount)({
        depositTokenAmount: depositCollTokenAmount,
        targetLeverage: new decimal_js_1.default(targetLeverage),
        priceCollToDebt: new decimal_js_1.default(priceCollToDebt),
        flashLoanFeeRatio: new decimal_js_1.default(flashLoanFee),
    });
    const slippageFactor = new decimal_js_1.default(1).add(slippagePct.div(new decimal_js_1.default(100)));
    const borrowAmountWithSlippage = borrowAmount.mul(slippageFactor);
    return {
        adjustDepositPosition: finalCollTokenAmount,
        adjustBorrowPosition: borrowAmountWithSlippage.toNumber(),
    };
};
exports.estimateDepositMode = estimateDepositMode;
const depositLeverageCalcs = (props) => {
    // Initialize local variables from the props object
    const { depositAmount, depositTokenIsCollToken, depositTokenIsSol, priceDebtToColl, targetLeverage, slippagePct, flashLoanFee, } = props;
    const slippage = slippagePct.div('100');
    const initDepositInSol = depositTokenIsSol ? depositAmount : new decimal_js_1.default(0);
    // Core logic
    if (depositTokenIsCollToken) {
        const y = targetLeverage.mul(priceDebtToColl);
        const x = flashLoanFee.add('1').mul(slippage.add('1')).div(priceDebtToColl);
        const finalColl = depositAmount.mul(x).div(x.sub(targetLeverage.sub('1').div(y)));
        const debt = finalColl.sub(depositAmount).mul(x);
        const flashBorrowColl = finalColl.sub(depositAmount).mul(flashLoanFee.add('1'));
        return {
            flashBorrowInCollToken: flashBorrowColl,
            initDepositInSol,
            debtTokenToBorrow: debt,
            collTokenToDeposit: finalColl,
            swapDebtTokenIn: debt,
            swapCollTokenExpectedOut: finalColl.sub(depositAmount),
        };
    }
    else {
        const y = targetLeverage.mul(priceDebtToColl);
        const x = flashLoanFee.add('1').mul(slippage.add('1')).div(priceDebtToColl);
        const finalColl = depositAmount.div(x.sub(targetLeverage.sub('1').div(y)));
        const flashBorrowColl = finalColl.mul(flashLoanFee.add('1'));
        const debt = targetLeverage.sub('1').mul(finalColl).div(y);
        return {
            flashBorrowInCollToken: flashBorrowColl,
            initDepositInSol,
            debtTokenToBorrow: debt,
            collTokenToDeposit: finalColl,
            swapDebtTokenIn: debt.add(depositAmount),
            swapCollTokenExpectedOut: finalColl,
        };
    }
};
exports.depositLeverageCalcs = depositLeverageCalcs;
function withdrawLeverageCalcs(market, collReserve, debtReserve, priceCollToDebt, withdrawAmount, deposited, borrowed, currentSlot, isClosingPosition, selectedTokenIsCollToken, selectedTokenMint, obligation, flashLoanFee, slippagePct) {
    // 1. Calculate coll_amount and debt_amount to repay such that we maintain leverage and we withdraw to
    // the wallet `amountInDepositTokenToWithdrawToWallet` amount of collateral token
    // We need to withdraw withdrawAmountInDepositToken coll tokens
    // and repay repayAmountInBorrowToken debt tokens
    const { adjustDepositPosition: withdrawAmountCalculated, adjustBorrowPosition: initialRepayAmount } = isClosingPosition
        ? { adjustDepositPosition: deposited, adjustBorrowPosition: borrowed }
        : calcWithdrawAmounts({
            collTokenMint: collReserve.getLiquidityMint(),
            priceCollToDebt: new decimal_js_1.default(priceCollToDebt),
            currentDepositPosition: deposited,
            currentBorrowPosition: borrowed,
            withdrawAmount: new decimal_js_1.default(withdrawAmount),
            selectedTokenMint: selectedTokenMint,
        });
    // Add slippage for the accrued interest rate amount
    const irSlippageBpsForDebt = obligation
        .estimateObligationInterestRate(market, debtReserve, obligation?.state.borrows[0], currentSlot)
        .toDecimalPlaces(debtReserve?.getMintDecimals(), decimal_js_1.default.ROUND_CEIL);
    // add 0.1 to irSlippageBpsForDebt because we don't want to estimate slightly less than SC and end up not repaying enough
    const repayAmount = initialRepayAmount
        .mul(irSlippageBpsForDebt.add('0.1').div('10_000').add('1'))
        .toDecimalPlaces(debtReserve?.getMintDecimals(), decimal_js_1.default.ROUND_CEIL);
    // 6. Get swap ixs
    // 5. Get swap estimations to understand how much we need to borrow from borrow reserve
    // prevent withdrawing more then deposited if we close position
    const depositTokenWithdrawAmount = !isClosingPosition
        ? withdrawAmountCalculated.mul(new decimal_js_1.default(1).plus(flashLoanFee))
        : withdrawAmountCalculated;
    // We are swapping debt token
    // When withdrawing coll, it means we just need to swap enough to pay for the flash borrow
    const swapAmountIfWithdrawingColl = repayAmount
        .mul(new decimal_js_1.default(1).plus(flashLoanFee))
        .mul(new decimal_js_1.default(1).plus(slippagePct.div(100)))
        .div(priceCollToDebt);
    // When withdrawing debt, it means we need to swap just the collateral we are withdrwaing
    // enough to cover the debt we are repaying, leaving the remaining in the wallet
    const swapAmountIfWithdrawingDebt = withdrawAmountCalculated;
    const collTokenSwapIn = selectedTokenIsCollToken ? swapAmountIfWithdrawingColl : swapAmountIfWithdrawingDebt;
    const debtTokenExpectedSwapOut = collTokenSwapIn.mul(priceCollToDebt).div(new decimal_js_1.default(1).add(slippagePct.div(100)));
    return {
        withdrawAmount: withdrawAmountCalculated,
        repayAmount,
        collTokenSwapIn,
        debtTokenExpectedSwapOut,
        depositTokenWithdrawAmount,
    };
}
function adjustDepositLeverageCalcs(debtReserve, adjustDepositPosition, adjustBorrowPosition, priceDebtToColl, flashLoanFee, slippagePct) {
    const amountToFlashBorrowDebt = adjustDepositPosition
        .div(priceDebtToColl)
        .mul(new decimal_js_1.default(new decimal_js_1.default(1).add(slippagePct.div(100))))
        .toDecimalPlaces(debtReserve.stats.decimals, decimal_js_1.default.ROUND_UP);
    const borrowAmount = adjustDepositPosition
        .mul(new decimal_js_1.default(1).plus(flashLoanFee))
        .mul(new decimal_js_1.default(new decimal_js_1.default(1).add(slippagePct.div(100))))
        .div(priceDebtToColl);
    return {
        adjustDepositPosition,
        adjustBorrowPosition,
        amountToFlashBorrowDebt,
        borrowAmount,
        withdrawAmountWithSlippageAndFlashLoanFee: new decimal_js_1.default(0),
    };
}
function adjustWithdrawLeverageCalcs(adjustDepositPosition, adjustBorrowPosition, flashLoanFee, slippagePct) {
    const withdrawAmountWithSlippageAndFlashLoanFee = decimal_js_1.default.abs(adjustDepositPosition)
        .mul(new decimal_js_1.default(1).plus(flashLoanFee))
        .mul(new decimal_js_1.default(1).add(slippagePct.div(100)));
    return {
        adjustDepositPosition,
        adjustBorrowPosition,
        amountToFlashBorrowDebt: new decimal_js_1.default(0),
        borrowAmount: new decimal_js_1.default(0),
        withdrawAmountWithSlippageAndFlashLoanFee,
    };
}
/**
 * Deposit with flash borrow DEBT token.
 * Flow: flash borrow debt -> swap debt->coll -> deposit coll -> borrow debt -> flash repay debt
 *
 * The user deposits collateral (or debt token). We flash borrow debt, swap it to coll,
 * deposit all coll, borrow debt to repay the flash loan (principal + fee).
 */
const depositLeverageCalcsDebtFlash = (props) => {
    const { depositAmount, depositTokenIsCollToken, depositTokenIsSol, priceDebtToColl, targetLeverage, slippagePct, flashLoanFee, } = props;
    const slippage = slippagePct.div('100');
    const initDepositInSol = depositTokenIsSol ? depositAmount : new decimal_js_1.default(0);
    if (priceDebtToColl.isZero()) {
        throw new Error('depositLeverageCalcsDebtFlash: priceDebtToColl is zero');
    }
    if (depositTokenIsCollToken) {
        // User deposits coll. We flash borrow debt, swap to coll, deposit all, borrow debt to repay flash.
        //
        // Definitions:
        //   collTotal      = depositAmount + flashBorrowDebt * priceDebtToColl / (1 + slippage)
        //   debtToBorrow   = flashBorrowDebt * (1 + flashLoanFee)
        //   leverage       = collTotal / (collTotal - debtToBorrow * priceDebtToColl)
        //
        // Solving for flashBorrowDebt:
        //   flashBorrowDebt = depositAmount * (leverage - 1)
        //                     / (priceDebtToColl * (leverage * (1 + flashLoanFee) - (leverage - 1) / (1 + slippage)))
        const slippageFactor = slippage.add('1');
        const flashFeeFactor = flashLoanFee.add('1');
        const denominator = priceDebtToColl.mul(targetLeverage.mul(flashFeeFactor).sub(targetLeverage.sub('1').div(slippageFactor)));
        const flashBorrowDebt = depositAmount.mul(targetLeverage.sub('1')).div(denominator);
        const collFromSwap = flashBorrowDebt.mul(priceDebtToColl).div(slippageFactor);
        const collTokenToDeposit = depositAmount.add(collFromSwap);
        const debtTokenToBorrow = flashBorrowDebt.mul(flashFeeFactor);
        return {
            flashBorrowInDebtToken: flashBorrowDebt,
            initDepositInSol,
            debtTokenToBorrow,
            collTokenToDeposit,
            swapDebtTokenIn: flashBorrowDebt,
            swapCollTokenExpectedOut: collFromSwap,
        };
    }
    else {
        // User deposits debt token. The user's deposit + flash borrowed debt both go into the swap.
        // Flow: flash borrow flashBorrowDebt debt -> swap (depositAmount + flashBorrowDebt) debt -> coll
        //       -> deposit collTotal coll -> borrow debtToBorrow debt -> flash repay flashBorrowDebt * (1 + flashLoanFee)
        //
        // Definitions:
        //   collTotal      = (depositAmount + flashBorrowDebt) * priceDebtToColl / (1 + slippage)
        //   debtToBorrow   = flashBorrowDebt * (1 + flashLoanFee)
        //   leverage       = collTotal / (collTotal - debtToBorrow * priceDebtToColl)
        //
        // Solving for flashBorrowDebt:
        //   flashBorrowDebt = depositAmount * (leverage - 1)
        //                     / ((1 + slippage) * leverage * (1 + flashLoanFee) - (leverage - 1))
        const slippageFactor = slippage.add('1');
        const flashFeeFactor = flashLoanFee.add('1');
        const denominator = slippageFactor.mul(targetLeverage).mul(flashFeeFactor).sub(targetLeverage.sub('1'));
        if (denominator.isZero()) {
            throw new Error('depositLeverageCalcsDebtFlash: denominator is zero — check targetLeverage, slippage, and flashLoanFee');
        }
        const flashBorrowDebt = depositAmount.mul(targetLeverage.sub('1')).div(denominator);
        const totalDebtToSwap = depositAmount.add(flashBorrowDebt);
        const collFromSwap = totalDebtToSwap.mul(priceDebtToColl).div(slippageFactor);
        const collTokenToDeposit = collFromSwap;
        const debtTokenToBorrow = flashBorrowDebt.mul(flashFeeFactor);
        return {
            flashBorrowInDebtToken: flashBorrowDebt,
            initDepositInSol,
            debtTokenToBorrow,
            collTokenToDeposit,
            swapDebtTokenIn: totalDebtToSwap,
            swapCollTokenExpectedOut: collFromSwap,
        };
    }
};
exports.depositLeverageCalcsDebtFlash = depositLeverageCalcsDebtFlash;
/**
 * Withdraw with flash borrow COLLATERAL token.
 * Flow: flash borrow coll -> swap coll->debt -> repay debt -> withdraw coll -> flash repay coll
 *
 * We flash borrow enough coll to swap for the debt repayment amount,
 * then repay debt, withdraw coll, and use the withdrawn coll to repay the flash loan.
 */
function withdrawLeverageCalcsCollFlash(market, collReserve, debtReserve, priceCollToDebt, withdrawAmount, deposited, borrowed, currentSlot, isClosingPosition, selectedTokenIsCollToken, selectedTokenMint, obligation, flashLoanFee, slippagePct) {
    // 1. Calculate proportional withdraw/repay amounts (same as existing)
    const { adjustDepositPosition: withdrawAmountCalculated, adjustBorrowPosition: initialRepayAmount } = isClosingPosition
        ? { adjustDepositPosition: deposited, adjustBorrowPosition: borrowed }
        : calcWithdrawAmounts({
            collTokenMint: collReserve.getLiquidityMint(),
            priceCollToDebt: new decimal_js_1.default(priceCollToDebt),
            currentDepositPosition: deposited,
            currentBorrowPosition: borrowed,
            withdrawAmount: new decimal_js_1.default(withdrawAmount),
            selectedTokenMint: selectedTokenMint,
        });
    // 2. Add IR slippage to repay amount
    const irSlippageBpsForDebt = obligation
        .estimateObligationInterestRate(market, debtReserve, obligation?.state.borrows[0], currentSlot)
        .toDecimalPlaces(debtReserve?.getMintDecimals(), decimal_js_1.default.ROUND_CEIL);
    const repayAmount = initialRepayAmount
        .mul(irSlippageBpsForDebt.add('0.1').div('10_000').add('1'))
        .toDecimalPlaces(debtReserve?.getMintDecimals(), decimal_js_1.default.ROUND_CEIL);
    // 3. Calculate how much coll to flash borrow for the swap
    // When withdrawing coll: swap just enough coll->debt to cover the repayment
    // When withdrawing debt: swap all withdrawn coll to debt; user keeps surplus debt after repay
    if (priceCollToDebt.isZero()) {
        throw new Error('withdrawLeverageCalcsCollFlash: priceCollToDebt is zero');
    }
    const swapAmountIfWithdrawingColl = repayAmount.mul(new decimal_js_1.default(1).add(slippagePct.div(100))).div(priceCollToDebt);
    const swapAmountIfWithdrawingDebt = withdrawAmountCalculated;
    const collTokenSwapIn = selectedTokenIsCollToken ? swapAmountIfWithdrawingColl : swapAmountIfWithdrawingDebt;
    const debtTokenExpectedSwapOut = collTokenSwapIn.mul(priceCollToDebt).div(new decimal_js_1.default(1).add(slippagePct.div(100)));
    // 4. Flash borrow amount = collSwapIn (the coll we need for the swap)
    // We need to flash borrow the coll for the swap, then withdraw enough coll from the position
    // to repay the flash loan (collSwapIn * (1 + flashLoanFee))
    const flashBorrowInCollToken = collTokenSwapIn.mul(new decimal_js_1.default(1).add(flashLoanFee));
    // 5. Total withdrawal = user amount + flash loan fee on borrowed coll
    // (for close position the build function passes U64_MAX, so this value is unused)
    const depositTokenWithdrawAmount = withdrawAmountCalculated.add(collTokenSwapIn.mul(flashLoanFee));
    return {
        flashBorrowInCollToken,
        withdrawAmount: withdrawAmountCalculated,
        repayAmount,
        collTokenSwapIn,
        debtTokenExpectedSwapOut,
        depositTokenWithdrawAmount,
    };
}
/**
 * Adjust (increase leverage) with flash borrow DEBT token.
 * Flow: flash borrow debt -> swap debt->coll -> deposit coll -> borrow debt -> flash repay debt
 */
function adjustDepositLeverageCalcsDebtFlash(debtReserve, adjustDepositPosition, adjustBorrowPosition, priceDebtToColl, flashLoanFee, slippagePct) {
    // We need to deposit `adjustDepositPosition` more coll.
    // Flash borrow debt, swap to coll, deposit coll, borrow debt to repay flash.
    // flashBorrowDebt: enough debt to swap for adjustDepositPosition coll (with slippage)
    if (priceDebtToColl.isZero()) {
        throw new Error('adjustDepositLeverageCalcsDebtFlash: priceDebtToColl is zero');
    }
    const flashBorrowDebt = adjustDepositPosition
        .mul(new decimal_js_1.default(1).add(slippagePct.div(100)))
        .div(priceDebtToColl)
        .toDecimalPlaces(debtReserve.stats.decimals, decimal_js_1.default.ROUND_UP);
    // We borrow enough debt from klend to repay flash loan + fee
    const debtTokenToBorrow = flashBorrowDebt.mul(new decimal_js_1.default(1).add(flashLoanFee));
    return {
        adjustDepositPosition,
        adjustBorrowPosition,
        flashBorrowInDebtToken: flashBorrowDebt,
        debtTokenToBorrow,
        swapDebtTokenIn: flashBorrowDebt,
        swapCollTokenExpectedOut: adjustDepositPosition,
    };
}
/**
 * Adjust (decrease leverage) with flash borrow COLLATERAL token.
 * Flow: flash borrow coll -> swap coll->debt -> repay debt -> withdraw coll -> flash repay coll
 */
function adjustWithdrawLeverageCalcsCollFlash(adjustDepositPosition, adjustBorrowPosition, priceCollToDebt, flashLoanFee, slippagePct) {
    const absDebtRepay = decimal_js_1.default.abs(adjustBorrowPosition);
    const absCollWithdraw = decimal_js_1.default.abs(adjustDepositPosition);
    // Flash borrow coll to swap for debt repayment
    // collSwapIn * priceCollToDebt / (1 + slippage) >= absDebtRepay
    if (priceCollToDebt.isZero()) {
        throw new Error('adjustWithdrawLeverageCalcsCollFlash: priceCollToDebt is zero');
    }
    const collTokenSwapIn = absDebtRepay.mul(new decimal_js_1.default(1).add(slippagePct.div(100))).div(priceCollToDebt);
    const debtTokenExpectedSwapOut = collTokenSwapIn.mul(priceCollToDebt).div(new decimal_js_1.default(1).add(slippagePct.div(100)));
    // Flash borrow amount includes the fee
    const flashBorrowInCollToken = collTokenSwapIn.mul(new decimal_js_1.default(1).add(flashLoanFee));
    // Total coll to withdraw from position: the position reduction + flash loan fee on the swap amount
    const depositTokenWithdrawAmount = absCollWithdraw.add(collTokenSwapIn.mul(flashLoanFee));
    return {
        adjustDepositPosition,
        adjustBorrowPosition,
        flashBorrowInCollToken,
        collTokenSwapIn,
        debtTokenExpectedSwapOut,
        depositTokenWithdrawAmount,
    };
}
//# sourceMappingURL=calcs.js.map