"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getRepayWithCollSwapInputs = getRepayWithCollSwapInputs;
exports.getRepayWithCollIxs = getRepayWithCollIxs;
const classes_1 = require("../classes");
// Import from specific sub-modules instead of the `../leverage` barrel so we don't pull in
// `flashBorrowType.ts` (which in turn imports back into `repay_with_collateral_calcs.ts` → here),
// which would form an import cycle and leave intent-helper exports undefined at evaluation time.
const instructions_1 = require("../leverage/instructions");
const utils_1 = require("../utils");
const kit_1 = require("@solana/kit");
const decimal_js_1 = __importDefault(require("decimal.js"));
const redeem_drift_1 = require("./redeem_drift");
const repay_with_collateral_calcs_1 = require("./repay_with_collateral_calcs");
async function getRepayWithCollSwapInputs({ collReserveAddress, currentSlot, debtReserveAddress, kaminoMarket, owner, obligation, quoter, referrer, repayAmount, isClosingPosition, budgetAndPriorityFeeIxs, scopeRefreshIx, useV2Ixs, flashBorrowType, }) {
    const collReserve = kaminoMarket.getExistingReserveByAddress(collReserveAddress);
    const debtReserve = kaminoMarket.getExistingReserveByAddress(debtReserveAddress);
    const collTokenMint = collReserve.getLiquidityMint();
    const debtTokenMint = debtReserve.getLiquidityMint();
    const { repayAmountLamports, flashRepayAmountLamports, repayAmount: finalRepayAmount, } = (0, repay_with_collateral_calcs_1.calcRepayAmountWithSlippage)(kaminoMarket, debtReserve, currentSlot, obligation, repayAmount, referrer);
    const debtPosition = obligation.getBorrowByReserve(debtReserve.address);
    const collPosition = obligation.getDepositByReserve(collReserve.address);
    if (!debtPosition) {
        throw new Error(`Debt position not found for ${debtReserve.stats.symbol} reserve ${debtReserve.address} in obligation ${obligation.obligationAddress}`);
    }
    if (!collPosition) {
        throw new Error(`Collateral position not found for ${collReserve.stats.symbol} reserve ${collReserve.address} in obligation ${obligation.obligationAddress}`);
    }
    const { maxWithdrawableCollLamports } = (0, repay_with_collateral_calcs_1.calcMaxWithdrawCollateral)(kaminoMarket, obligation, collReserve.address, debtReserve.address, repayAmountLamports);
    const maxCollNeededFromOracle = (0, repay_with_collateral_calcs_1.getMaxCollateralFromRepayAmount)(finalRepayAmount, debtReserve, collReserve);
    const inputAmountLamports = decimal_js_1.default.min(maxWithdrawableCollLamports, maxCollNeededFromOracle);
    // Use the debt-flash builder for quote-time account discovery. The repay/withdraw account
    // footprint is shared between routes; the flash-loan-side accounts differ by `flashBorrowType`
    // and are layered in by the final route-specific builder in `getRepayWithCollIxs`.
    const klendIxs = (await buildRepayWithCollateralIxsDebtFlash(kaminoMarket, debtReserve, collReserve, owner, obligation, referrer, currentSlot, budgetAndPriorityFeeIxs, scopeRefreshIx, [
        {
            preActionIxs: [],
            swapIxs: [],
            lookupTables: [],
            quote: {},
        },
    ], isClosingPosition, repayAmountLamports, inputAmountLamports, useV2Ixs))[0];
    const uniqueKlendAccounts = (0, utils_1.uniqueAccountsWithProgramIds)(klendIxs.instructions);
    const swapQuoteInputs = {
        inputAmountLamports,
        inputMint: collTokenMint,
        outputMint: debtTokenMint,
    };
    const swapQuote = await quoter(swapQuoteInputs, uniqueKlendAccounts);
    const swapQuotePxDebtToColl = swapQuote.priceAInB;
    if (flashBorrowType === 'coll') {
        // Coll-flash sizing: swap exactly the flash-borrowed coll → at least `repayAmountLamports` debt
        // after slippage; withdraw flashBorrow * (1 + collFlashFee) coll to repay the flash loan.
        const { collSwapInLamports, debtMinOutLamports, collWithdrawForFlashRepayLamports } = (0, repay_with_collateral_calcs_1.calcRepayWithCollCollFlashSwap)({
            repayAmountLamports,
            swapPriceCollToDebt: swapQuotePxDebtToColl,
            slippagePct: new decimal_js_1.default(0), // swap-side slippage handled by the swapper; quote already reflects realised price
            collFlashLoanFee: collReserve.getFlashLoanFee(),
            collMintFactor: collReserve.getMintFactor(),
            debtMintFactor: debtReserve.getMintFactor(),
        });
        return {
            swapInputs: {
                inputAmountLamports: collSwapInLamports,
                minOutAmountLamports: debtMinOutLamports,
                inputMint: collTokenMint,
                outputMint: debtTokenMint,
            },
            flashLoanInfo: {
                flashBorrowReserve: collReserve.address,
                flashLoanFee: collReserve.getFlashLoanFee(),
            },
            initialInputs: {
                flashBorrowType: 'coll',
                debtRepayAmountLamports: repayAmountLamports,
                flashRepayAmountLamports: collWithdrawForFlashRepayLamports,
                maxCollateralWithdrawLamports: maxWithdrawableCollLamports,
                swapQuote,
                currentSlot,
                klendAccounts: uniqueKlendAccounts,
            },
        };
    }
    // Debt-flash sizing (default): swap enough coll to produce flashRepayAmountLamports of debt.
    const collSwapInLamports = flashRepayAmountLamports
        .div(debtReserve.getMintFactor())
        .div(swapQuotePxDebtToColl)
        .mul(collReserve.getMintFactor())
        .ceil();
    return {
        swapInputs: {
            inputAmountLamports: collSwapInLamports,
            minOutAmountLamports: flashRepayAmountLamports,
            inputMint: collTokenMint,
            outputMint: debtTokenMint,
        },
        flashLoanInfo: klendIxs.flashLoanInfo,
        initialInputs: {
            flashBorrowType: 'debt',
            debtRepayAmountLamports: repayAmountLamports,
            flashRepayAmountLamports,
            maxCollateralWithdrawLamports: maxWithdrawableCollLamports,
            swapQuote,
            currentSlot,
            klendAccounts: uniqueKlendAccounts,
        },
    };
}
async function getRepayWithCollIxs({ repayAmount, isClosingPosition, budgetAndPriorityFeeIxs, collReserveAddress, currentSlot, debtReserveAddress, kaminoMarket, owner, obligation, quoter, swapper, referrer, scopeRefreshIx, useV2Ixs, flashBorrowType, logger = console.log, }) {
    const { swapInputs, initialInputs } = await getRepayWithCollSwapInputs({
        collReserveAddress,
        currentSlot,
        debtReserveAddress,
        kaminoMarket,
        owner,
        obligation,
        quoter,
        referrer,
        repayAmount,
        isClosingPosition,
        budgetAndPriorityFeeIxs,
        scopeRefreshIx,
        useV2Ixs,
        flashBorrowType,
    });
    const { debtRepayAmountLamports, flashRepayAmountLamports, maxCollateralWithdrawLamports, swapQuote } = initialInputs;
    const { inputAmountLamports: collSwapInLamports } = swapInputs;
    const collReserve = kaminoMarket.getExistingReserveByAddress(collReserveAddress);
    const debtReserve = kaminoMarket.getExistingReserveByAddress(debtReserveAddress);
    if (initialInputs.flashBorrowType === 'coll') {
        // `flashRepayAmountLamports` on the coll-flash variant is the realised coll withdrawal.
        (0, repay_with_collateral_calcs_1.validateCollFlashWithdrawCap)({
            collWithdrawLamports: flashRepayAmountLamports,
            maxCollateralWithdrawLamports,
        });
    }
    else if (collSwapInLamports.greaterThan(maxCollateralWithdrawLamports)) {
        // Debt-flash: silently clamping the swap input would leave `flashRepayAmountLamports` /
        // `minOutAmountLamports` sized to the original amount, producing an inconsistent tx that
        // either fails on flash-repay or consumes pre-existing debt tokens from the user ATA. Throw
        // so the caller can reduce the repay amount or fall back to the coll-flash route.
        throw new Error(`Collateral swap in amount ${collSwapInLamports} exceeds max withdrawable collateral ${maxCollateralWithdrawLamports}. ` +
            `Reduce the repay amount or use \`flashBorrowType: 'coll'\`.`);
    }
    if (initialInputs.flashBorrowType === 'coll') {
        logger(`Expected to swap in: ${collSwapInLamports.div(collReserve.getMintFactor())} ${collReserve.symbol}, repay debt: ${debtRepayAmountLamports.div(debtReserve.getMintFactor())} ${debtReserve.symbol}, flash repay: ${flashRepayAmountLamports.div(collReserve.getMintFactor())} ${collReserve.symbol}, quoter px: ${swapQuote.priceAInB} ${debtReserve.symbol}/${collReserve.symbol}, required px: ${debtRepayAmountLamports
            .div(debtReserve.getMintFactor())
            .div(collSwapInLamports.div(collReserve.getMintFactor()))} ${debtReserve.symbol}/${collReserve.symbol}`);
    }
    else {
        logger(`Expected to swap in: ${collSwapInLamports.div(collReserve.getMintFactor())} ${collReserve.symbol}, for: ${flashRepayAmountLamports.div(debtReserve.getMintFactor())} ${debtReserve.symbol}, quoter px: ${swapQuote.priceAInB} ${debtReserve.symbol}/${collReserve.symbol}, required px: ${flashRepayAmountLamports
            .div(debtReserve.getMintFactor())
            .div(collSwapInLamports.div(collReserve.getMintFactor()))} ${debtReserve.symbol}/${collReserve.symbol}`);
    }
    const swapResponses = await swapper(swapInputs, initialInputs.klendAccounts, swapQuote);
    // Debt-flash: the exact-in swap pulls `collSwapInLamports` from the coll ATA, funded by the
    // withdraw's redeem — buffer the withdraw so the floor-rounded redeem (at the actual
    // execution-slot exchange rate) always covers it; the dust surplus stays in the user's ATA.
    // The guard above already ensured `collSwapInLamports <= maxCollateralWithdrawLamports`, so the
    // clamped buffer is never below the swap input. See `redeem_drift.ts`.
    const collWithdrawLamports = (0, redeem_drift_1.bufferWithdrawForRedeemDrift)(collSwapInLamports, maxCollateralWithdrawLamports);
    // Coll-flash: the flash-repay ix pulls exactly `flashRepayAmountLamports`
    // (= flashBorrow * (1 + fee)) from the coll ATA, funded by the withdraw's redeem — same
    // protection. `validateCollFlashWithdrawCap` above already ensured
    // `flashRepayAmountLamports <= maxCollateralWithdrawLamports`. Only the withdraw is buffered;
    // flash-loan sizing and swap inputs are untouched.
    const collFlashWithdrawLamports = (0, redeem_drift_1.bufferWithdrawForRedeemDrift)(flashRepayAmountLamports, maxCollateralWithdrawLamports);
    const repayWithCollateralIxs = await (initialInputs.flashBorrowType === 'coll'
        ? buildRepayWithCollateralIxsCollFlash(kaminoMarket, debtReserve, collReserve, owner, obligation, referrer, currentSlot, budgetAndPriorityFeeIxs, scopeRefreshIx, swapResponses, isClosingPosition, debtRepayAmountLamports, collSwapInLamports, collFlashWithdrawLamports, useV2Ixs)
        : buildRepayWithCollateralIxsDebtFlash(kaminoMarket, debtReserve, collReserve, owner, obligation, referrer, currentSlot, budgetAndPriorityFeeIxs, scopeRefreshIx, swapResponses, isClosingPosition, debtRepayAmountLamports, collWithdrawLamports, useV2Ixs));
    return repayWithCollateralIxs.map((ixs, index) => {
        return {
            ixs: ixs.instructions,
            lookupTables: swapResponses[index].lookupTables,
            swapInputs,
            flashLoanInfo: ixs.flashLoanInfo,
            initialInputs,
            quote: swapResponses[index].quote.quoteResponse,
        };
    });
}
/**
 * Debt-flash repay-with-collateral builder (default).
 *
 * Instruction order:
 *   scopeRefresh → createAtas → flashBorrow(DEBT) → repay+withdraw → swap(coll→debt) → flashRepay(DEBT) → budget
 *
 * Used when `flashBorrowType` is `'debt'` or omitted. The flash-borrowed debt is paid into the
 * obligation immediately; the withdrawn collateral is then swapped back to debt to repay the flash.
 */
async function buildRepayWithCollateralIxsDebtFlash(market, debtReserve, collReserve, owner, obligation, referrer, currentSlot, budgetAndPriorityFeeIxs, scopeRefreshIx, swapQuoteIxsArray, isClosingPosition, debtRepayAmountLamports, collWithdrawLamports, useV2Ixs) {
    // 1. Create atas & budget txns
    const budgetIxs = budgetAndPriorityFeeIxs || (0, utils_1.getComputeBudgetAndPriorityFeeIxs)(1_400_000);
    const atas = [
        { mint: collReserve.getLiquidityMint(), tokenProgram: collReserve.getLiquidityTokenProgram() },
        { mint: debtReserve.getLiquidityMint(), tokenProgram: debtReserve.getLiquidityTokenProgram() },
    ];
    const atasAndIxs = await (0, utils_1.createAtasIdempotent)(owner, atas);
    const [, { ata: debtTokenAta }] = atasAndIxs;
    // 2. Flash borrow & repay the debt to repay amount needed
    const { flashBorrowIx, flashRepayIx } = (0, instructions_1.getFlashLoanInstructions)({
        borrowIxIndex: atasAndIxs.length + scopeRefreshIx.length,
        userTransferAuthority: owner,
        lendingMarketAuthority: await market.getLendingMarketAuthority(),
        lendingMarketAddress: market.getAddress(),
        reserve: debtReserve,
        amountLamports: debtRepayAmountLamports,
        destinationAta: debtTokenAta,
        // TODO(referrals): once we support referrals, we will have to replace the placeholder args below:
        referrerAccount: (0, kit_1.none)(),
        referrerTokenState: (0, kit_1.none)(),
        programId: market.programId,
    });
    const requestElevationGroup = !isClosingPosition && obligation.state.elevationGroup !== 0;
    const maxWithdrawLtvCheck = (0, repay_with_collateral_calcs_1.getMaxWithdrawLtvCheck)(obligation, debtRepayAmountLamports, debtReserve, collWithdrawLamports, collReserve);
    // 3. Repay using the flash borrowed funds & withdraw collateral to swap and pay the flash loan
    let repayAndWithdrawAction;
    if (maxWithdrawLtvCheck === repay_with_collateral_calcs_1.MaxWithdrawLtvCheck.MAX_LTV) {
        repayAndWithdrawAction = await classes_1.KaminoAction.buildRepayAndWithdrawTxns({
            kaminoMarket: market,
            repayAmount: isClosingPosition ? utils_1.U64_MAX : debtRepayAmountLamports.toString(),
            repayReserveAddress: debtReserve.address,
            withdrawAmount: isClosingPosition ? utils_1.U64_MAX : collWithdrawLamports.toString(),
            withdrawReserveAddress: collReserve.address,
            payer: owner,
            currentSlot,
            obligation,
            useV2Ixs,
            scopeRefreshConfig: undefined,
            extraComputeBudget: 0,
            includeAtaIxs: false,
            requestElevationGroup,
            initUserMetadata: undefined,
            referrer,
        });
    }
    else {
        repayAndWithdrawAction = await classes_1.KaminoAction.buildRepayAndWithdrawV2Txns({
            kaminoMarket: market,
            repayAmount: isClosingPosition ? utils_1.U64_MAX : debtRepayAmountLamports.toString(),
            repayReserveAddress: debtReserve.address,
            withdrawAmount: isClosingPosition ? utils_1.U64_MAX : collWithdrawLamports.toString(),
            withdrawReserveAddress: collReserve.address,
            payer: owner,
            currentSlot,
            obligation,
            scopeRefreshConfig: undefined,
            extraComputeBudget: 0,
            includeAtaIxs: false,
            requestElevationGroup,
            initUserMetadata: undefined,
            referrer,
        });
    }
    // 4. Swap collateral to debt to repay flash loan
    return swapQuoteIxsArray.map((swapQuoteIxs) => {
        const { preActionIxs, swapIxs } = swapQuoteIxs;
        const swapInstructions = (0, utils_1.removeBudgetIxs)(swapIxs);
        const ixs = [
            ...scopeRefreshIx,
            ...atasAndIxs.map((x) => x.createAtaIx),
            flashBorrowIx,
            ...preActionIxs,
            ...classes_1.KaminoAction.actionToIxs(repayAndWithdrawAction),
            ...swapInstructions,
            flashRepayIx,
            ...budgetIxs,
        ];
        const res = {
            flashLoanInfo: {
                flashBorrowReserve: debtReserve.address,
                flashLoanFee: debtReserve.getFlashLoanFee(),
            },
            instructions: ixs,
        };
        return res;
    });
}
/**
 * Coll-flash repay-with-collateral builder.
 *
 * Instruction order:
 *   scopeRefresh → createAtas → flashBorrow(COLL) → swap(coll→debt) → repay+withdraw → flashRepay(COLL) → budget
 *
 * Vs the default debt-flash builder, the swap runs BEFORE repay+withdraw because the swap
 * output (debt) is what gets repaid to the obligation. The withdrawn coll is then used to
 * repay the flash loan.
 */
async function buildRepayWithCollateralIxsCollFlash(market, debtReserve, collReserve, owner, obligation, referrer, currentSlot, budgetAndPriorityFeeIxs, scopeRefreshIx, swapQuoteIxsArray, isClosingPosition, debtRepayAmountLamports, flashBorrowInCollLamports, collWithdrawForFlashRepayLamports, useV2Ixs) {
    // 1. Create atas & budget txns
    const budgetIxs = budgetAndPriorityFeeIxs || (0, utils_1.getComputeBudgetAndPriorityFeeIxs)(1_400_000);
    const atas = [
        { mint: collReserve.getLiquidityMint(), tokenProgram: collReserve.getLiquidityTokenProgram() },
        { mint: debtReserve.getLiquidityMint(), tokenProgram: debtReserve.getLiquidityTokenProgram() },
    ];
    const atasAndIxs = await (0, utils_1.createAtasIdempotent)(owner, atas);
    const [{ ata: collTokenAta }] = atasAndIxs;
    // 2. Flash borrow the same amount of coll we will feed into the swap. The withdrawal that
    // funds the flash repay (`collWithdrawForFlashRepayLamports`) is computed once in
    // `calcRepayWithCollCollFlashSwap` and threaded down — keeps sizing logic colocated.
    const { flashBorrowIx, flashRepayIx } = (0, instructions_1.getFlashLoanInstructions)({
        borrowIxIndex: atasAndIxs.length + scopeRefreshIx.length,
        userTransferAuthority: owner,
        lendingMarketAuthority: await market.getLendingMarketAuthority(),
        lendingMarketAddress: market.getAddress(),
        reserve: collReserve,
        amountLamports: flashBorrowInCollLamports,
        destinationAta: collTokenAta,
        referrerAccount: (0, kit_1.none)(),
        referrerTokenState: (0, kit_1.none)(),
        programId: market.programId,
    });
    const requestElevationGroup = !isClosingPosition && obligation.state.elevationGroup !== 0;
    const maxWithdrawLtvCheck = (0, repay_with_collateral_calcs_1.getMaxWithdrawLtvCheck)(obligation, debtRepayAmountLamports, debtReserve, collWithdrawForFlashRepayLamports, collReserve);
    // 3. Repay obligation debt with the swapped output, then withdraw coll to repay the flash loan.
    let repayAndWithdrawAction;
    if (maxWithdrawLtvCheck === repay_with_collateral_calcs_1.MaxWithdrawLtvCheck.MAX_LTV) {
        repayAndWithdrawAction = await classes_1.KaminoAction.buildRepayAndWithdrawTxns({
            kaminoMarket: market,
            repayAmount: isClosingPosition ? utils_1.U64_MAX : debtRepayAmountLamports.toString(),
            repayReserveAddress: debtReserve.address,
            withdrawAmount: isClosingPosition ? utils_1.U64_MAX : collWithdrawForFlashRepayLamports.toString(),
            withdrawReserveAddress: collReserve.address,
            payer: owner,
            currentSlot,
            obligation,
            useV2Ixs,
            scopeRefreshConfig: undefined,
            extraComputeBudget: 0,
            includeAtaIxs: false,
            requestElevationGroup,
            initUserMetadata: undefined,
            referrer,
        });
    }
    else {
        repayAndWithdrawAction = await classes_1.KaminoAction.buildRepayAndWithdrawV2Txns({
            kaminoMarket: market,
            repayAmount: isClosingPosition ? utils_1.U64_MAX : debtRepayAmountLamports.toString(),
            repayReserveAddress: debtReserve.address,
            withdrawAmount: isClosingPosition ? utils_1.U64_MAX : collWithdrawForFlashRepayLamports.toString(),
            withdrawReserveAddress: collReserve.address,
            payer: owner,
            currentSlot,
            obligation,
            scopeRefreshConfig: undefined,
            extraComputeBudget: 0,
            includeAtaIxs: false,
            requestElevationGroup,
            initUserMetadata: undefined,
            referrer,
        });
    }
    return swapQuoteIxsArray.map((swapQuoteIxs) => {
        const { preActionIxs, swapIxs } = swapQuoteIxs;
        const swapInstructions = (0, utils_1.removeBudgetIxs)(swapIxs);
        // Key difference vs debt-flash: swap BEFORE repay+withdraw (the swap output is what we repay).
        const ixs = [
            ...scopeRefreshIx,
            ...atasAndIxs.map((x) => x.createAtaIx),
            flashBorrowIx,
            ...preActionIxs,
            ...swapInstructions,
            ...classes_1.KaminoAction.actionToIxs(repayAndWithdrawAction),
            flashRepayIx,
            ...budgetIxs,
        ];
        const res = {
            flashLoanInfo: {
                flashBorrowReserve: collReserve.address,
                flashLoanFee: collReserve.getFlashLoanFee(),
            },
            instructions: ixs,
        };
        return res;
    });
}
//# sourceMappingURL=repay_with_collateral_operations.js.map