"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.calculateSwapCollExpectedOutput = calculateSwapCollExpectedOutput;
exports.calculateSwapDebtExpectedOutput = calculateSwapDebtExpectedOutput;
const decimal_js_1 = __importDefault(require("decimal.js"));
/**
 * Calculates the expected amount of target collateral that will end up in the obligation after a swap, taking into
 * account the flash-loan fee and slippage. For the 'debt' flash-borrow flow, also reports the (small) additional debt
 * the user will incur as a consequence of the flash-loan fee.
 *
 * Note: this is an estimate only; the actual on-chain result is subject to real DEX slippage.
 */
function calculateSwapCollExpectedOutput(inputs) {
    const { sourceCollAmount, sourceCollReserve, targetCollReserve, priceSourceCollToTargetColl, slippagePct } = inputs;
    if (sourceCollReserve.address === targetCollReserve.address) {
        throw new Error('Cannot swap from/to the same collateral');
    }
    if (sourceCollAmount.lte(0)) {
        throw new Error('sourceCollAmount must be positive');
    }
    const flashBorrowToken = inputs.flashBorrowToken ?? 'targetColl';
    const slippageFactor = new decimal_js_1.default(1).sub(slippagePct.div(100));
    const grossSwapOutput = sourceCollAmount.mul(priceSourceCollToTargetColl).mul(slippageFactor);
    if (flashBorrowToken === 'targetColl') {
        const flashLoanFeeRate = targetCollReserve.getFlashLoanFee();
        // Inclusive fee: flashRepay = flashBorrow + fee, where fee = flashRepay * rate / (1 + rate).
        const flashLoanFeeAmount = grossSwapOutput.mul(flashLoanFeeRate.div(flashLoanFeeRate.add(1)));
        const expectedTargetCollAmount = grossSwapOutput.sub(flashLoanFeeAmount);
        return {
            expectedTargetCollAmount,
            flashLoanFeeAmount,
            additionalDebtFromFee: new decimal_js_1.default(0),
        };
    }
    if (flashBorrowToken === 'sourceColl') {
        const flashLoanFeeRate = sourceCollReserve.getFlashLoanFee();
        // Inclusive fee: the requested source collateral amount is withdrawn to repay the flash loan, while only the
        // fee-net flash-borrowed amount is sent to the external swap.
        const flashLoanFeeAmount = sourceCollAmount.mul(flashLoanFeeRate.div(flashLoanFeeRate.add(1)));
        const expectedTargetCollAmount = sourceCollAmount
            .sub(flashLoanFeeAmount)
            .mul(priceSourceCollToTargetColl)
            .mul(slippageFactor);
        return {
            expectedTargetCollAmount,
            flashLoanFeeAmount,
            additionalDebtFromFee: new decimal_js_1.default(0),
        };
    }
    // 'debt' flash-borrow flow: the input union requires `debtReserve` and `outstandingDebtAmount`.
    const debtInputs = inputs;
    if (debtInputs.outstandingDebtAmount.lte(0)) {
        throw new Error(`outstandingDebtAmount must be positive when flashBorrowToken === 'debt'`);
    }
    const flashLoanFeeRate = debtInputs.debtReserve.getFlashLoanFee();
    // Flow on-chain: flash borrow outstanding debt → repay (debt cancels) → ... → re-borrow `flashRepay = outstanding +
    // flashFee` → pay back the flash loan. The re-borrow goes through `borrow_obligation_liquidity` which adds the
    // target reserve's `originationFee` on top, so the obligation's debt actually grows by
    //   newDebt = flashRepay × (1 + originationFee)
    // and the net change vs. the starting outstanding is
    //   additionalDebt = newDebt − outstanding = flashFee + originationFee × flashRepay
    // For reserves with `originationFee === 0` this collapses to just `flashFee` (matches the original behavior).
    const borrowFeeRate = debtInputs.debtReserve.getBorrowFee();
    const flashLoanFeeAmount = debtInputs.outstandingDebtAmount.mul(flashLoanFeeRate);
    const flashRepayAmount = debtInputs.outstandingDebtAmount.add(flashLoanFeeAmount);
    const borrowOriginationFeeAmount = flashRepayAmount.mul(borrowFeeRate);
    const additionalDebtFromFee = flashRepayAmount.add(borrowOriginationFeeAmount).sub(debtInputs.outstandingDebtAmount);
    return {
        expectedTargetCollAmount: grossSwapOutput,
        flashLoanFeeAmount,
        additionalDebtFromFee,
    };
}
/**
 * Calculates the expected amount of target debt that the obligation will owe after a debt swap, taking into account
 * the flash-loan fee and slippage.
 */
function calculateSwapDebtExpectedOutput(inputs) {
    const { sourceDebtSwapAmount, sourceDebtReserve, targetDebtReserve, flashBorrowToken, priceSourceDebtToTargetDebt, slippagePct, } = inputs;
    if (sourceDebtReserve.address === targetDebtReserve.address) {
        throw new Error('Cannot swap from/to the same debt');
    }
    if (sourceDebtSwapAmount.lte(0)) {
        throw new Error('sourceDebtSwapAmount must be positive');
    }
    const slippageFactor = new decimal_js_1.default(1).sub(slippagePct.div(100));
    // The target reserve charges an origination fee on the borrow's *receive amount*; the obligation's debt grows by
    // `receive + originationFee`. Mirror that here so the reported `expectedTargetDebtAmount` matches what the on-chain
    // LTV checks see (`KaminoObligation.getDebtWithFeesForBorrowAmount`).
    const targetBorrowFeeRate = targetDebtReserve.getBorrowFee();
    if (flashBorrowToken === 'targetDebt') {
        const flashLoanFeeRate = targetDebtReserve.getFlashLoanFee();
        // Flash borrow X of targetDebt, swap → sourceDebt. Need swap output ≥ sourceDebtSwapAmount:
        //   X * (1 / priceSourceToTarget) * slippageFactor ≥ sourceDebtSwapAmount
        //   X ≥ sourceDebtSwapAmount * priceSourceToTarget / slippageFactor
        const flashBorrowAmount = sourceDebtSwapAmount.mul(priceSourceDebtToTargetDebt).div(slippageFactor);
        const flashLoanFeeAmount = flashBorrowAmount.mul(flashLoanFeeRate);
        const targetBorrowReceiveAmount = flashBorrowAmount.add(flashLoanFeeAmount);
        const borrowOriginationFeeAmount = targetBorrowReceiveAmount.mul(targetBorrowFeeRate);
        return {
            expectedTargetDebtAmount: targetBorrowReceiveAmount.add(borrowOriginationFeeAmount),
            flashLoanFeeAmount,
            borrowOriginationFeeAmount,
        };
    }
    // flashBorrowToken === 'sourceDebt'
    const flashLoanFeeRate = sourceDebtReserve.getFlashLoanFee();
    // Flash borrow sourceDebtSwapAmount of sourceDebt, repay obligation, borrow Y targetDebt, swap → sourceDebt.
    // Need swap output ≥ flashRepay = sourceDebtSwapAmount * (1 + fee):
    //   Y * (1 / priceSourceToTarget) * slippageFactor ≥ sourceDebtSwapAmount * (1 + fee)
    //   Y ≥ sourceDebtSwapAmount * (1 + fee) * priceSourceToTarget / slippageFactor
    const flashLoanFeeAmount = sourceDebtSwapAmount.mul(flashLoanFeeRate);
    const flashRepayAmount = sourceDebtSwapAmount.add(flashLoanFeeAmount);
    const targetBorrowReceiveAmount = flashRepayAmount.mul(priceSourceDebtToTargetDebt).div(slippageFactor);
    const borrowOriginationFeeAmount = targetBorrowReceiveAmount.mul(targetBorrowFeeRate);
    return {
        expectedTargetDebtAmount: targetBorrowReceiveAmount.add(borrowOriginationFeeAmount),
        flashLoanFeeAmount,
        borrowOriginationFeeAmount,
    };
}
//# sourceMappingURL=swap_calcs.js.map