UNPKG

@kamino-finance/klend-sdk

Version:

Typescript SDK for interacting with the Kamino Lending (klend) protocol

834 lines 49.8 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.getScopeRefreshIx = exports.getSetupIxs = void 0; exports.getDepositWithLeverageSwapInputs = getDepositWithLeverageSwapInputs; exports.getDepositWithLeverageIxs = getDepositWithLeverageIxs; exports.getWithdrawWithLeverageSwapInputs = getWithdrawWithLeverageSwapInputs; exports.getWithdrawWithLeverageIxs = getWithdrawWithLeverageIxs; exports.buildWithdrawWithLeverageIxs = buildWithdrawWithLeverageIxs; exports.getAdjustLeverageSwapInputs = getAdjustLeverageSwapInputs; exports.getAdjustLeverageIxs = getAdjustLeverageIxs; const web3_js_1 = require("@solana/web3.js"); const decimal_js_1 = __importDefault(require("decimal.js")); const classes_1 = require("../classes"); const instructions_1 = require("./instructions"); const classes_2 = require("../classes"); const utils_1 = require("../utils"); const calcs_1 = require("./calcs"); const spl_token_1 = require("@solana/spl-token"); const utils_2 = require("./utils"); const CreationParameters_1 = require("@kamino-finance/kliquidity-sdk/dist/utils/CreationParameters"); async function getDepositWithLeverageSwapInputs({ owner, kaminoMarket, debtTokenMint, collTokenMint, depositAmount, priceDebtToColl, slippagePct, obligation, referrer, currentSlot, targetLeverage, selectedTokenMint, kamino, obligationTypeTagOverride, scopeRefreshConfig, budgetAndPriorityFeeIxs, quoteBufferBps, priceAinB, isKtoken, quoter, useV2Ixs, elevationGroupOverride, }) { const collReserve = kaminoMarket.getReserveByMint(collTokenMint); const debtReserve = kaminoMarket.getReserveByMint(debtTokenMint); const solTokenReserve = kaminoMarket.getReserveByMint(spl_token_1.NATIVE_MINT); const flashLoanFee = collReserve.getFlashLoanFee() || new decimal_js_1.default(0); const selectedTokenIsCollToken = selectedTokenMint.equals(collTokenMint); const depositTokenIsSol = !solTokenReserve ? false : selectedTokenMint.equals(solTokenReserve.getLiquidityMint()); const collIsKtoken = await isKtoken(collTokenMint); const strategy = collIsKtoken ? (await kamino.getStrategyByKTokenMint(collTokenMint)) : undefined; const calcs = await getDepositWithLeverageCalcs(depositAmount, selectedTokenIsCollToken, collIsKtoken, depositTokenIsSol, priceDebtToColl, targetLeverage, slippagePct, flashLoanFee, kamino, strategy, debtTokenMint, priceAinB, debtReserve); console.log('Ops Calcs', (0, classes_1.toJson)(calcs)); const obligationType = checkObligationType(obligationTypeTagOverride, collTokenMint, debtTokenMint, kaminoMarket); // Build the repay & withdraw collateral tx to get the number of accounts const klendIxs = await buildDepositWithLeverageIxs(kaminoMarket, debtReserve, collReserve, owner, obligation ? obligation : obligationType, referrer, currentSlot, depositTokenIsSol, scopeRefreshConfig, calcs, budgetAndPriorityFeeIxs, { preActionIxs: [], swapIxs: [], lookupTables: [], quote: { priceAInB: new decimal_js_1.default(0), // not used quoteResponse: undefined, }, }, strategy, collIsKtoken, useV2Ixs, elevationGroupOverride); const uniqueKlendAccounts = (0, utils_1.uniqueAccountsWithProgramIds)(klendIxs.instructions); const swapInputAmount = (0, classes_2.numberToLamportsDecimal)(!collIsKtoken ? calcs.swapDebtTokenIn : calcs.singleSidedDepositKtokenOnly, debtReserve.stats.decimals).ceil(); const swapInputsForQuote = { inputAmountLamports: swapInputAmount.mul(new decimal_js_1.default(1).add(quoteBufferBps.div(CreationParameters_1.FullBPS))), inputMint: debtTokenMint, outputMint: collTokenMint, amountDebtAtaBalance: new decimal_js_1.default(0), // Only needed for ktokens swaps }; const swapQuote = await quoter(swapInputsForQuote, uniqueKlendAccounts); const quotePriceCalcs = await getDepositWithLeverageCalcs(depositAmount, selectedTokenIsCollToken, collIsKtoken, depositTokenIsSol, swapQuote.priceAInB, targetLeverage, slippagePct, flashLoanFee, kamino, strategy, debtTokenMint, priceAinB, debtReserve); const swapInputAmountQuotePrice = (0, classes_2.numberToLamportsDecimal)(!collIsKtoken ? quotePriceCalcs.swapDebtTokenIn : quotePriceCalcs.singleSidedDepositKtokenOnly, debtReserve.stats.decimals).ceil(); let expectedDebtTokenAtaBalance = new decimal_js_1.default(0); if (collIsKtoken) { let futureBalanceInAta = new decimal_js_1.default(0); if (debtTokenMint.equals(spl_token_1.NATIVE_MINT)) { futureBalanceInAta = futureBalanceInAta.add(!collIsKtoken ? quotePriceCalcs.initDepositInSol : quotePriceCalcs.initDepositInSol); } futureBalanceInAta = futureBalanceInAta.add(!collIsKtoken ? quotePriceCalcs.debtTokenToBorrow : quotePriceCalcs.flashBorrowInDebtTokenKtokenOnly); expectedDebtTokenAtaBalance = await (0, utils_2.getExpectedTokenBalanceAfterBorrow)(kaminoMarket.getConnection(), debtTokenMint, owner, (0, classes_2.numberToLamportsDecimal)(futureBalanceInAta.toDecimalPlaces(debtReserve.stats.decimals), debtReserve.stats.decimals), debtReserve.state.liquidity.mintDecimals.toNumber()); } return { swapInputs: { inputAmountLamports: swapInputAmountQuotePrice, minOutAmountLamports: (0, classes_2.numberToLamportsDecimal)(!collIsKtoken ? quotePriceCalcs.flashBorrowInCollToken : quotePriceCalcs.flashBorrowInDebtTokenKtokenOnly, !collIsKtoken ? collReserve.stats.decimals : debtReserve.stats.decimals), inputMint: debtTokenMint, outputMint: collTokenMint, amountDebtAtaBalance: expectedDebtTokenAtaBalance, }, flashLoanInfo: klendIxs.flashLoanInfo, initialInputs: { calcs: quotePriceCalcs, swapQuote, currentSlot, collIsKtoken, strategy, obligation: obligation ? obligation : obligationType, klendAccounts: uniqueKlendAccounts, }, }; } async function getDepositWithLeverageCalcs(depositAmount, selectedTokenIsCollToken, collIsKtoken, depositTokenIsSol, priceDebtToColl, targetLeverage, slippagePct, flashLoanFee, kamino, strategy, debtTokenMint, priceAinB, debtReserve) { let calcs; if (!collIsKtoken) { calcs = (0, calcs_1.depositLeverageCalcs)({ depositAmount: depositAmount, depositTokenIsCollToken: selectedTokenIsCollToken, depositTokenIsSol, priceDebtToColl, targetLeverage, slippagePct, flashLoanFee, }); } else { calcs = await (0, calcs_1.depositLeverageKtokenCalcs)({ kamino: kamino, strategy: strategy, debtTokenMint, depositAmount: depositAmount, depositTokenIsCollToken: selectedTokenIsCollToken, depositTokenIsSol, priceDebtToColl, targetLeverage, slippagePct, flashLoanFee, priceAinB, }); // Rounding to exact number of decimals so this value is passed through in all calcs without rounding inconsistencies calcs.flashBorrowInDebtTokenKtokenOnly = calcs.flashBorrowInDebtTokenKtokenOnly.toDecimalPlaces(debtReserve.state.liquidity.mintDecimals.toNumber(), decimal_js_1.default.ROUND_CEIL); calcs.debtTokenToBorrow = calcs.debtTokenToBorrow.toDecimalPlaces(debtReserve.state.liquidity.mintDecimals.toNumber(), decimal_js_1.default.ROUND_CEIL); calcs.singleSidedDepositKtokenOnly = calcs.singleSidedDepositKtokenOnly.toDecimalPlaces(debtReserve.state.liquidity.mintDecimals.toNumber(), decimal_js_1.default.ROUND_CEIL); } return calcs; } async function getDepositWithLeverageIxs({ owner, kaminoMarket, debtTokenMint, collTokenMint, depositAmount, priceDebtToColl, slippagePct, obligation, referrer, currentSlot, targetLeverage, selectedTokenMint, kamino, obligationTypeTagOverride, scopeRefreshConfig, budgetAndPriorityFeeIxs, quoteBufferBps, priceAinB, isKtoken, quoter, swapper, elevationGroupOverride, useV2Ixs, }) { const { swapInputs, initialInputs } = await getDepositWithLeverageSwapInputs({ owner, kaminoMarket, debtTokenMint, collTokenMint, depositAmount, priceDebtToColl, slippagePct, obligation, referrer, currentSlot, targetLeverage, selectedTokenMint, kamino, obligationTypeTagOverride, scopeRefreshConfig, budgetAndPriorityFeeIxs, quoteBufferBps, priceAinB, isKtoken, quoter, useV2Ixs, }); let depositSwapper; if (!initialInputs.collIsKtoken) { depositSwapper = swapper; } else { if (kamino === undefined) { throw Error('Ktoken use as collateral for leverage without Kamino instance'); } depositSwapper = await (0, utils_2.getTokenToKtokenSwapper)(kaminoMarket, kamino, owner, slippagePct, swapper, priceAinB, false); } const swapsArray = await depositSwapper(swapInputs, initialInputs.klendAccounts, initialInputs.swapQuote); if (initialInputs.collIsKtoken) { if (initialInputs.strategy.strategy.strategyLookupTable) { const strategyLut = await (0, utils_1.getLookupTableAccount)(kaminoMarket.getConnection(), initialInputs.strategy.strategy.strategyLookupTable); swapsArray.forEach((swap) => { swap.lookupTables.push(strategyLut); }); } else { console.log('Strategy lookup table not found'); } } const collReserve = kaminoMarket.getReserveByMint(collTokenMint); const debtReserve = kaminoMarket.getReserveByMint(debtTokenMint); const solTokenReserve = kaminoMarket.getReserveByMint(spl_token_1.NATIVE_MINT); const depositTokenIsSol = !solTokenReserve ? false : selectedTokenMint.equals(solTokenReserve.getLiquidityMint()); return Promise.all(swapsArray.map(async (swap) => { const ixs = await buildDepositWithLeverageIxs(kaminoMarket, debtReserve, collReserve, owner, initialInputs.obligation, referrer, currentSlot, depositTokenIsSol, scopeRefreshConfig, initialInputs.calcs, budgetAndPriorityFeeIxs, { preActionIxs: [], swapIxs: swap.swapIxs, lookupTables: swap.lookupTables, quote: swap.quote, }, initialInputs.strategy, initialInputs.collIsKtoken, useV2Ixs, elevationGroupOverride); return { ixs: ixs.instructions, flashLoanInfo: ixs.flashLoanInfo, lookupTables: swap.lookupTables, swapInputs, initialInputs, quote: swap.quote.quoteResponse, }; })); } async function buildDepositWithLeverageIxs(market, debtReserve, collReserve, owner, obligation, referrer, currentSlot, depositTokenIsSol, scopeRefreshConfig, calcs, budgetAndPriorityFeeIxs, swapQuoteIxs, strategy, collIsKtoken, useV2Ixs, elevationGroupOverride) { const collTokenMint = collReserve.getLiquidityMint(); const debtTokenMint = debtReserve.getLiquidityMint(); const collTokenAta = (0, spl_token_1.getAssociatedTokenAddressSync)(collTokenMint, owner, false, collReserve.getLiquidityTokenProgram()); const debtTokenAta = (0, spl_token_1.getAssociatedTokenAddressSync)(debtTokenMint, owner, false, debtReserve.getLiquidityTokenProgram()); // 1. Create atas & budget ixs const { budgetIxs, createAtasIxs, scopeRefreshIx } = await (0, exports.getSetupIxs)(owner, market, obligation, collTokenMint, collReserve, collIsKtoken, debtTokenMint, debtReserve, strategy, scopeRefreshConfig, budgetAndPriorityFeeIxs); const fillWsolAtaIxs = []; if (depositTokenIsSol) { fillWsolAtaIxs.push(...(0, utils_1.getTransferWsolIxs)(owner, (0, spl_token_1.getAssociatedTokenAddressSync)(spl_token_1.NATIVE_MINT, owner), (0, classes_2.numberToLamportsDecimal)(calcs.initDepositInSol, utils_1.SOL_DECIMALS).ceil())); } // 2. Flash borrow & repay the collateral amount needed for given leverage // if user deposits coll, then we borrow the diff, else we borrow the entire amount const { flashBorrowIx, flashRepayIx } = (0, instructions_1.getFlashLoanInstructions)({ borrowIxIndex: budgetIxs.length + createAtasIxs.length + fillWsolAtaIxs.length + (scopeRefreshIx.length > 0 ? 1 : 0), walletPublicKey: owner, lendingMarketAuthority: market.getLendingMarketAuthority(), lendingMarketAddress: market.getAddress(), reserve: !collIsKtoken ? collReserve : debtReserve, amountLamports: (0, classes_2.numberToLamportsDecimal)(!collIsKtoken ? calcs.flashBorrowInCollToken : calcs.flashBorrowInDebtTokenKtokenOnly, !collIsKtoken ? collReserve.stats.decimals : debtReserve.stats.decimals), destinationAta: !collIsKtoken ? collTokenAta : debtTokenAta, // TODO(referrals): once we support referrals, we will have to replace the placeholder args below: referrerAccount: market.programId, referrerTokenState: market.programId, programId: market.programId, }); // 3. Deposit initial tokens + borrowed tokens into reserve const kaminoDepositAndBorrowAction = await classes_1.KaminoAction.buildDepositAndBorrowTxns(market, (0, classes_2.numberToLamportsDecimal)(!collIsKtoken ? calcs.collTokenToDeposit : calcs.collTokenToDeposit, collReserve.stats.decimals) .floor() .toString(), collTokenMint, (0, classes_2.numberToLamportsDecimal)(!collIsKtoken ? calcs.debtTokenToBorrow : calcs.debtTokenToBorrow, debtReserve.stats.decimals) .ceil() .toString(), debtTokenMint, owner, obligation, useV2Ixs, undefined, 0, false, elevationGroupOverride === 0 ? false : true, // emode { skipInitialization: true, skipLutCreation: true }, // to be checked and created in a setup tx in the UI referrer, currentSlot); // 4. Swap const { swapIxs } = swapQuoteIxs; const swapInstructions = (0, utils_1.removeBudgetIxs)(swapIxs); const flashBorrowReserve = !collIsKtoken ? collReserve : debtReserve; const flashLoanInfo = { flashBorrowReserve: flashBorrowReserve.address, flashLoanFee: flashBorrowReserve.getFlashLoanFee(), }; return { flashLoanInfo, instructions: [ ...scopeRefreshIx, ...budgetIxs, ...createAtasIxs, ...fillWsolAtaIxs, ...[flashBorrowIx], ...(collIsKtoken ? swapInstructions : []), ...classes_1.KaminoAction.actionToIxs(kaminoDepositAndBorrowAction), ...(collIsKtoken ? [] : swapInstructions), ...[flashRepayIx], ], }; } async function getWithdrawWithLeverageSwapInputs({ owner, kaminoMarket, debtTokenMint, collTokenMint, deposited, borrowed, obligation, referrer, currentSlot, withdrawAmount, priceCollToDebt, slippagePct, isClosingPosition, selectedTokenMint, budgetAndPriorityFeeIxs, kamino, scopeRefreshConfig, quoteBufferBps, isKtoken, quoter, useV2Ixs, }) { const collReserve = kaminoMarket.getReserveByMint(collTokenMint); const debtReserve = kaminoMarket.getReserveByMint(debtTokenMint); const flashLoanFee = debtReserve.getFlashLoanFee() || new decimal_js_1.default(0); const selectedTokenIsCollToken = selectedTokenMint.equals(collTokenMint); const collIsKtoken = await isKtoken(collTokenMint); const strategy = collIsKtoken ? (await kamino.getStrategyByKTokenMint(collTokenMint)) : undefined; const inputTokenIsSol = selectedTokenMint.equals(spl_token_1.NATIVE_MINT); const calcs = (0, calcs_1.withdrawLeverageCalcs)(kaminoMarket, collReserve, debtReserve, priceCollToDebt, withdrawAmount, deposited, borrowed, currentSlot, isClosingPosition, selectedTokenIsCollToken, selectedTokenMint, obligation, flashLoanFee, slippagePct); const klendIxs = await buildWithdrawWithLeverageIxs(kaminoMarket, debtReserve, collReserve, owner, obligation, referrer, currentSlot, isClosingPosition, inputTokenIsSol, scopeRefreshConfig, calcs, budgetAndPriorityFeeIxs, { preActionIxs: [], swapIxs: [], lookupTables: [], quote: { priceAInB: new decimal_js_1.default(0), // not used quoteResponse: undefined, }, }, strategy, collIsKtoken, useV2Ixs); const uniqueKlendAccounts = (0, utils_1.uniqueAccountsWithProgramIds)(klendIxs.instructions); const swapInputAmount = (0, classes_2.numberToLamportsDecimal)(calcs.collTokenSwapIn, collReserve.getMintDecimals()).ceil(); const swapInputsForQuote = { inputAmountLamports: swapInputAmount.mul(new decimal_js_1.default(1).add(quoteBufferBps.div(CreationParameters_1.FullBPS))), inputMint: collTokenMint, outputMint: debtTokenMint, amountDebtAtaBalance: undefined, // Only needed for ktokens deposits }; const swapQuote = await quoter(swapInputsForQuote, uniqueKlendAccounts); const calcsQuotePrice = (0, calcs_1.withdrawLeverageCalcs)(kaminoMarket, collReserve, debtReserve, !collIsKtoken ? swapQuote.priceAInB : priceCollToDebt, withdrawAmount, deposited, borrowed, currentSlot, isClosingPosition, selectedTokenIsCollToken, selectedTokenMint, obligation, flashLoanFee, slippagePct); const swapInputAmountQuotePrice = (0, classes_2.numberToLamportsDecimal)(calcsQuotePrice.collTokenSwapIn, collReserve.getMintDecimals()).ceil(); return { swapInputs: { inputAmountLamports: swapInputAmountQuotePrice, minOutAmountLamports: calcsQuotePrice.repayAmount, inputMint: collTokenMint, outputMint: debtTokenMint, amountDebtAtaBalance: new decimal_js_1.default(0), // Only needed for ktokens deposits }, flashLoanInfo: klendIxs.flashLoanInfo, initialInputs: { calcs: calcsQuotePrice, swapQuote, currentSlot, collIsKtoken, strategy, obligation, klendAccounts: uniqueKlendAccounts, }, }; } async function getWithdrawWithLeverageIxs({ owner, kaminoMarket, debtTokenMint, collTokenMint, obligation, deposited, borrowed, referrer, currentSlot, withdrawAmount, priceCollToDebt, slippagePct, isClosingPosition, selectedTokenMint, budgetAndPriorityFeeIxs, kamino, scopeRefreshConfig, quoteBufferBps, isKtoken, quoter, swapper, useV2Ixs, }) { const collReserve = kaminoMarket.getReserveByMint(collTokenMint); const debtReserve = kaminoMarket.getReserveByMint(debtTokenMint); const inputTokenIsSol = selectedTokenMint.equals(spl_token_1.NATIVE_MINT); const { swapInputs, initialInputs } = await getWithdrawWithLeverageSwapInputs({ owner, kaminoMarket, debtTokenMint, collTokenMint, deposited, borrowed, obligation, referrer, currentSlot, withdrawAmount, priceCollToDebt, slippagePct, isClosingPosition, selectedTokenMint, budgetAndPriorityFeeIxs, kamino, scopeRefreshConfig, quoteBufferBps, isKtoken, quoter, useV2Ixs, }); let withdrawSwapper; if (initialInputs.collIsKtoken) { if (kamino === undefined) { throw Error('Ktoken use as collateral for leverage without Kamino instance'); } withdrawSwapper = await (0, utils_2.getKtokenToTokenSwapper)(kaminoMarket, kamino, owner, swapper); } else { withdrawSwapper = swapper; } const swapsArray = await withdrawSwapper(swapInputs, initialInputs.klendAccounts, initialInputs.swapQuote); if (initialInputs.collIsKtoken) { if (initialInputs.strategy.strategy.strategyLookupTable) { const strategyLut = await (0, utils_1.getLookupTableAccount)(kaminoMarket.getConnection(), initialInputs.strategy.strategy.strategyLookupTable); swapsArray.forEach((swap) => { swap.lookupTables.push(strategyLut); }); } else { console.log('Strategy lookup table not found'); } } return Promise.all(swapsArray.map(async (swap) => { const ixs = await buildWithdrawWithLeverageIxs(kaminoMarket, debtReserve, collReserve, owner, obligation, referrer, currentSlot, isClosingPosition, inputTokenIsSol, scopeRefreshConfig, initialInputs.calcs, budgetAndPriorityFeeIxs, { preActionIxs: [], swapIxs: swap.swapIxs, lookupTables: swap.lookupTables, quote: swap.quote, }, initialInputs.strategy, initialInputs.collIsKtoken, useV2Ixs); // Send ixs and lookup tables return { ixs: ixs.instructions, flashLoanInfo: ixs.flashLoanInfo, lookupTables: swap.lookupTables, swapInputs, initialInputs: initialInputs, quote: swap.quote.quoteResponse, }; })); } async function buildWithdrawWithLeverageIxs(market, debtReserve, collReserve, owner, obligation, referrer, currentSlot, isClosingPosition, depositTokenIsSol, scopeRefreshConfig, calcs, budgetAndPriorityFeeIxs, swapQuoteIxs, strategy, collIsKtoken, useV2Ixs) { const collTokenMint = collReserve.getLiquidityMint(); const debtTokenMint = debtReserve.getLiquidityMint(); const debtTokenAta = (0, spl_token_1.getAssociatedTokenAddressSync)(debtTokenMint, owner, false, debtReserve.getLiquidityTokenProgram()); // 1. Create atas & budget txns & user metadata const { budgetIxs, createAtasIxs, scopeRefreshIx } = await (0, exports.getSetupIxs)(owner, market, obligation, collTokenMint, collReserve, collIsKtoken, debtTokenMint, debtReserve, strategy, scopeRefreshConfig, budgetAndPriorityFeeIxs); const closeWsolAtaIxs = []; if (depositTokenIsSol || debtTokenMint.equals(spl_token_1.NATIVE_MINT)) { const wsolAta = (0, utils_1.getAssociatedTokenAddress)(spl_token_1.NATIVE_MINT, owner, false); closeWsolAtaIxs.push((0, spl_token_1.createCloseAccountInstruction)(wsolAta, owner, owner, [], spl_token_1.TOKEN_PROGRAM_ID)); } // TODO: Mihai/Marius check if we can improve this logic and not convert any SOL // This is here so that we have enough wsol to repay in case the kAB swapped to sol after estimates is not enough const fillWsolAtaIxs = []; if (debtTokenMint.equals(spl_token_1.NATIVE_MINT)) { const halfSolBalance = (await market.getConnection().getBalance(owner)) / web3_js_1.LAMPORTS_PER_SOL / 2; const balanceToWrap = halfSolBalance < 0.1 ? halfSolBalance : 0.1; fillWsolAtaIxs.push(...(0, utils_1.getTransferWsolIxs)(owner, (0, spl_token_1.getAssociatedTokenAddressSync)(spl_token_1.NATIVE_MINT, owner), (0, classes_2.numberToLamportsDecimal)(balanceToWrap, utils_1.SOL_DECIMALS).ceil())); } // 2. Prepare the flash borrow and flash repay amounts and ixs // We borrow exactly how much we need to repay // and repay that + flash amount fee const { flashBorrowIx, flashRepayIx } = (0, instructions_1.getFlashLoanInstructions)({ borrowIxIndex: budgetIxs.length + createAtasIxs.length + fillWsolAtaIxs.length + (scopeRefreshIx.length > 0 ? 1 : 0), walletPublicKey: owner, lendingMarketAuthority: market.getLendingMarketAuthority(), lendingMarketAddress: market.getAddress(), reserve: debtReserve, amountLamports: (0, classes_2.numberToLamportsDecimal)(calcs.repayAmount, debtReserve.stats.decimals), destinationAta: debtTokenAta, // TODO(referrals): once we support referrals, we will have to replace the placeholder args below: referrerAccount: market.programId, referrerTokenState: market.programId, programId: market.programId, }); // 3. Repay borrowed tokens and Withdraw tokens from reserve that will be swapped to repay flash loan const repayAndWithdrawAction = await classes_1.KaminoAction.buildRepayAndWithdrawTxns(market, isClosingPosition ? utils_1.U64_MAX : (0, classes_2.numberToLamportsDecimal)(calcs.repayAmount, debtReserve.stats.decimals).floor().toString(), debtTokenMint, isClosingPosition ? utils_1.U64_MAX : (0, classes_2.numberToLamportsDecimal)(calcs.depositTokenWithdrawAmount, collReserve.stats.decimals).ceil().toString(), collTokenMint, owner, currentSlot, obligation, useV2Ixs, undefined, 0, false, false, { skipInitialization: true, skipLutCreation: true }, // to be checked and created in a setup tx in the UI (won't be the case for withdraw anyway as this would be created in deposit) referrer); const swapInstructions = (0, utils_1.removeBudgetIxs)(swapQuoteIxs.swapIxs); return { flashLoanInfo: { flashLoanFee: debtReserve.getFlashLoanFee(), flashBorrowReserve: debtReserve.address, }, instructions: [ ...scopeRefreshIx, ...budgetIxs, ...createAtasIxs, ...fillWsolAtaIxs, ...[flashBorrowIx], ...classes_1.KaminoAction.actionToIxs(repayAndWithdrawAction), ...swapInstructions, ...[flashRepayIx], ...closeWsolAtaIxs, ], }; } async function getAdjustLeverageSwapInputs({ owner, kaminoMarket, debtTokenMint, collTokenMint, obligation, depositedLamports, borrowedLamports, referrer, currentSlot, targetLeverage, priceCollToDebt, priceDebtToColl, slippagePct, budgetAndPriorityFeeIxs, kamino, scopeRefreshConfig, quoteBufferBps, isKtoken, quoter, useV2Ixs, }) { const collReserve = kaminoMarket.getReserveByMint(collTokenMint); const debtReserve = kaminoMarket.getReserveByMint(debtTokenMint); const deposited = (0, classes_1.lamportsToNumberDecimal)(depositedLamports, collReserve.stats.decimals); const borrowed = (0, classes_1.lamportsToNumberDecimal)(borrowedLamports, debtReserve.stats.decimals); const collIsKtoken = await isKtoken(collTokenMint); const strategy = collIsKtoken ? (await kamino.getStrategyByKTokenMint(collTokenMint)) : undefined; // Getting current flash loan fee const currentLeverage = obligation.refreshedStats.leverage; const isDepositViaLeverage = targetLeverage.gte(new decimal_js_1.default(currentLeverage)); let flashLoanFee; if (isDepositViaLeverage) { flashLoanFee = collReserve.getFlashLoanFee() || new decimal_js_1.default(0); } else { flashLoanFee = debtReserve.getFlashLoanFee() || new decimal_js_1.default(0); } const { adjustDepositPosition, adjustBorrowPosition } = (0, calcs_1.calcAdjustAmounts)({ currentDepositPosition: deposited, currentBorrowPosition: borrowed, targetLeverage: targetLeverage, priceCollToDebt: priceCollToDebt, flashLoanFee: new decimal_js_1.default(flashLoanFee), }); const isDeposit = adjustDepositPosition.gte(0) && adjustBorrowPosition.gte(0); if (isDepositViaLeverage !== isDeposit) { throw new Error('Invalid target leverage'); } if (isDeposit) { const calcs = await (0, calcs_1.adjustDepositLeverageCalcs)(kaminoMarket, owner, debtReserve, adjustDepositPosition, adjustBorrowPosition, priceDebtToColl, flashLoanFee, slippagePct, collIsKtoken); // Build the repay & withdraw collateral tx to get the number of accounts const klendIxs = await buildIncreaseLeverageIxs(owner, kaminoMarket, collTokenMint, debtTokenMint, obligation, referrer, currentSlot, calcs, strategy, scopeRefreshConfig, collIsKtoken, { preActionIxs: [], swapIxs: [], lookupTables: [], quote: { priceAInB: new decimal_js_1.default(0), // not used quoteResponse: undefined, }, }, budgetAndPriorityFeeIxs, useV2Ixs); const uniqueKlendAccounts = (0, utils_1.uniqueAccountsWithProgramIds)(klendIxs.instructions); const swapInputAmount = (0, classes_2.numberToLamportsDecimal)(!collIsKtoken ? calcs.borrowAmount : calcs.amountToFlashBorrowDebt, debtReserve.stats.decimals).ceil(); const swapInputsForQuote = { inputAmountLamports: swapInputAmount.mul(new decimal_js_1.default(1).add(quoteBufferBps.div(CreationParameters_1.FullBPS))), inputMint: debtTokenMint, outputMint: collTokenMint, amountDebtAtaBalance: new decimal_js_1.default(0), // Only needed for ktokens swaps }; const swapQuote = await quoter(swapInputsForQuote, uniqueKlendAccounts); const { adjustDepositPosition: adjustDepositPositionQuotePrice, adjustBorrowPosition: adjustBorrowPositionQuotePrice, } = (0, calcs_1.calcAdjustAmounts)({ currentDepositPosition: deposited, currentBorrowPosition: borrowed, targetLeverage: targetLeverage, priceCollToDebt: new decimal_js_1.default(1).div(swapQuote.priceAInB), flashLoanFee: new decimal_js_1.default(flashLoanFee), }); const calcsQuotePrice = await (0, calcs_1.adjustDepositLeverageCalcs)(kaminoMarket, owner, debtReserve, adjustDepositPositionQuotePrice, adjustBorrowPositionQuotePrice, swapQuote.priceAInB, flashLoanFee, slippagePct, collIsKtoken); const swapInputAmountQuotePrice = (0, classes_2.numberToLamportsDecimal)(!collIsKtoken ? calcsQuotePrice.borrowAmount : calcsQuotePrice.amountToFlashBorrowDebt, debtReserve.getMintDecimals()).ceil(); let expectedDebtTokenAtaBalance = new decimal_js_1.default(0); if (collIsKtoken) { expectedDebtTokenAtaBalance = await (0, utils_2.getExpectedTokenBalanceAfterBorrow)(kaminoMarket.getConnection(), debtTokenMint, owner, (0, classes_2.numberToLamportsDecimal)(!collIsKtoken ? calcsQuotePrice.borrowAmount : calcsQuotePrice.amountToFlashBorrowDebt, debtReserve.stats.decimals).floor(), debtReserve.getMintDecimals()); } return { swapInputs: { inputAmountLamports: swapInputAmountQuotePrice, minOutAmountLamports: (0, classes_2.numberToLamportsDecimal)(!collIsKtoken ? calcsQuotePrice.adjustDepositPosition : calcsQuotePrice.amountToFlashBorrowDebt, !collIsKtoken ? collReserve.stats.decimals : debtReserve.stats.decimals), inputMint: debtTokenMint, outputMint: collTokenMint, amountDebtAtaBalance: expectedDebtTokenAtaBalance, }, flashLoanInfo: klendIxs.flashLoanInfo, initialInputs: { calcs: calcsQuotePrice, swapQuote, currentSlot, collIsKtoken, strategy, obligation: obligation, klendAccounts: uniqueKlendAccounts, isDeposit: isDeposit, }, }; } else { const calcs = (0, calcs_1.adjustWithdrawLeverageCalcs)(adjustDepositPosition, adjustBorrowPosition, flashLoanFee, slippagePct); const klendIxs = await buildDecreaseLeverageIxs(owner, kaminoMarket, collTokenMint, debtTokenMint, obligation, referrer, currentSlot, calcs, strategy, scopeRefreshConfig, collIsKtoken, { preActionIxs: [], swapIxs: [], lookupTables: [], quote: { priceAInB: new decimal_js_1.default(0), // not used quoteResponse: undefined, }, }, budgetAndPriorityFeeIxs, useV2Ixs); const uniqueKlendAccounts = (0, utils_1.uniqueAccountsWithProgramIds)(klendIxs.instructions); const swapInputAmount = (0, classes_2.numberToLamportsDecimal)(calcs.withdrawAmountWithSlippageAndFlashLoanFee, collReserve.state.liquidity.mintDecimals.toNumber()).ceil(); const swapInputsForQuote = { inputAmountLamports: swapInputAmount.mul(new decimal_js_1.default(1).add(quoteBufferBps.div(CreationParameters_1.FullBPS))), inputMint: collTokenMint, outputMint: debtTokenMint, amountDebtAtaBalance: undefined, // Only needed for ktokens deposits }; const swapQuote = await quoter(swapInputsForQuote, uniqueKlendAccounts); const { adjustDepositPosition: adjustDepositPositionQuotePrice, adjustBorrowPosition: adjustBorrowPositionQuotePrice, } = (0, calcs_1.calcAdjustAmounts)({ currentDepositPosition: deposited, currentBorrowPosition: borrowed, targetLeverage: targetLeverage, priceCollToDebt: swapQuote.priceAInB, flashLoanFee: new decimal_js_1.default(flashLoanFee), }); const calcsQuotePrice = (0, calcs_1.adjustWithdrawLeverageCalcs)(adjustDepositPositionQuotePrice, adjustBorrowPositionQuotePrice, flashLoanFee, slippagePct); const swapInputAmountQuotePrice = (0, classes_2.numberToLamportsDecimal)(calcsQuotePrice.withdrawAmountWithSlippageAndFlashLoanFee, collReserve.getMintDecimals()).ceil(); return { swapInputs: { inputAmountLamports: swapInputAmountQuotePrice, minOutAmountLamports: (0, classes_2.numberToLamportsDecimal)(calcsQuotePrice.adjustBorrowPosition.abs(), debtReserve.stats.decimals), inputMint: collTokenMint, outputMint: debtTokenMint, amountDebtAtaBalance: undefined, // Only needed for ktokens deposits }, flashLoanInfo: klendIxs.flashLoanInfo, initialInputs: { calcs: calcsQuotePrice, swapQuote, currentSlot, collIsKtoken, strategy, obligation, klendAccounts: uniqueKlendAccounts, isDeposit, }, }; } } async function getAdjustLeverageIxs({ owner, kaminoMarket, debtTokenMint, collTokenMint, obligation, depositedLamports, borrowedLamports, referrer, currentSlot, targetLeverage, priceCollToDebt, priceDebtToColl, slippagePct, budgetAndPriorityFeeIxs, kamino, scopeRefreshConfig, quoteBufferBps, priceAinB, isKtoken, quoter, swapper, useV2Ixs, }) { const { swapInputs, initialInputs } = await getAdjustLeverageSwapInputs({ owner, kaminoMarket, debtTokenMint, collTokenMint, obligation, depositedLamports, borrowedLamports, referrer, currentSlot, targetLeverage, priceCollToDebt, priceDebtToColl, slippagePct, budgetAndPriorityFeeIxs, kamino, scopeRefreshConfig, quoteBufferBps, priceAinB, isKtoken, quoter, useV2Ixs, }); // leverage increased so we need to deposit and borrow more if (initialInputs.isDeposit) { let depositSwapper; if (initialInputs.collIsKtoken) { if (kamino === undefined) { throw Error('Ktoken use as collateral for leverage without Kamino instance'); } depositSwapper = await (0, utils_2.getTokenToKtokenSwapper)(kaminoMarket, kamino, owner, slippagePct, swapper, priceAinB, false); } else { depositSwapper = swapper; } const swapsArray = await depositSwapper(swapInputs, initialInputs.klendAccounts, initialInputs.swapQuote); return Promise.all(swapsArray.map(async (swap) => { const ixs = await buildIncreaseLeverageIxs(owner, kaminoMarket, collTokenMint, debtTokenMint, obligation, referrer, currentSlot, initialInputs.calcs, initialInputs.strategy, scopeRefreshConfig, initialInputs.collIsKtoken, { preActionIxs: [], swapIxs: swap.swapIxs, lookupTables: swap.lookupTables, quote: swap.quote, }, budgetAndPriorityFeeIxs, useV2Ixs); return { ixs: ixs.instructions, flashLoanInfo: ixs.flashLoanInfo, lookupTables: swap.lookupTables, swapInputs, initialInputs, quote: swap.quote.quoteResponse, }; })); } else { console.log('Decreasing leverage'); let withdrawSwapper; if (initialInputs.collIsKtoken) { if (kamino === undefined) { throw Error('Ktoken use as collateral for leverage without Kamino instance'); } withdrawSwapper = await (0, utils_2.getKtokenToTokenSwapper)(kaminoMarket, kamino, owner, swapper); } else { withdrawSwapper = swapper; } // 5. Get swap ixs const swapsArray = await withdrawSwapper(swapInputs, initialInputs.klendAccounts, initialInputs.swapQuote); return Promise.all(swapsArray.map(async (swap) => { const ixs = await buildDecreaseLeverageIxs(owner, kaminoMarket, collTokenMint, debtTokenMint, obligation, referrer, currentSlot, initialInputs.calcs, initialInputs.strategy, scopeRefreshConfig, initialInputs.collIsKtoken, { preActionIxs: [], swapIxs: swap.swapIxs, lookupTables: swap.lookupTables, quote: swap.quote, }, budgetAndPriorityFeeIxs, useV2Ixs); return { ixs: ixs.instructions, flashLoanInfo: ixs.flashLoanInfo, lookupTables: swap.lookupTables, swapInputs, initialInputs, quote: swap.quote.quoteResponse, }; })); } } /** * Deposit and borrow tokens if leverage increased */ async function buildIncreaseLeverageIxs(owner, kaminoMarket, collTokenMint, debtTokenMint, obligation, referrer, currentSlot, calcs, strategy, scopeRefreshConfig, collIsKtoken, swapQuoteIxs, budgetAndPriorityFeeIxs, useV2Ixs) { const collReserve = kaminoMarket.getExistingReserveByMint(collTokenMint); const debtReserve = kaminoMarket.getExistingReserveByMint(debtTokenMint); const debtTokenAta = (0, spl_token_1.getAssociatedTokenAddressSync)(debtTokenMint, owner, false, debtReserve.getLiquidityTokenProgram()); const collTokenAta = (0, spl_token_1.getAssociatedTokenAddressSync)(collTokenMint, owner, false, collReserve.getLiquidityTokenProgram()); // 1. Create atas & budget txns const { budgetIxs, createAtasIxs, scopeRefreshIx } = await (0, exports.getSetupIxs)(owner, kaminoMarket, obligation, collTokenMint, collReserve, collIsKtoken, debtTokenMint, debtReserve, strategy, scopeRefreshConfig, budgetAndPriorityFeeIxs); // 2. Create borrow flash loan instruction const { flashBorrowIx, flashRepayIx } = (0, instructions_1.getFlashLoanInstructions)({ borrowIxIndex: budgetIxs.length + createAtasIxs.length + (scopeRefreshIx.length > 0 ? 1 : 0), // TODO: how about user metadata ixs walletPublicKey: owner, lendingMarketAuthority: kaminoMarket.getLendingMarketAuthority(), lendingMarketAddress: kaminoMarket.getAddress(), reserve: !collIsKtoken ? collReserve : debtReserve, amountLamports: (0, classes_2.numberToLamportsDecimal)(!collIsKtoken ? calcs.adjustDepositPosition : calcs.amountToFlashBorrowDebt, !collIsKtoken ? collReserve.stats.decimals : debtReserve.stats.decimals), destinationAta: !collIsKtoken ? collTokenAta : debtTokenAta, // TODO(referrals): once we support referrals, we will have to replace the placeholder args below: referrerAccount: kaminoMarket.programId, referrerTokenState: kaminoMarket.programId, programId: kaminoMarket.programId, }); const depositAction = await classes_1.KaminoAction.buildDepositTxns(kaminoMarket, (0, classes_2.numberToLamportsDecimal)(calcs.adjustDepositPosition, collReserve.stats.decimals).floor().toString(), collTokenMint, owner, obligation, useV2Ixs, undefined, 0, false, false, { skipInitialization: true, skipLutCreation: true }, referrer, currentSlot); // 4. Borrow tokens in borrow token reserve that will be swapped to repay flash loan const borrowAction = await classes_1.KaminoAction.buildBorrowTxns(kaminoMarket, (0, classes_2.numberToLamportsDecimal)(calcs.borrowAmount, debtReserve.stats.decimals).ceil().toString(), debtTokenMint, owner, obligation, useV2Ixs, undefined, 0, false, false, { skipInitialization: true, skipLutCreation: true }, // to be checked and create in a setup tx in the UI (won't be the case for adjust anyway as this would be created in deposit) referrer, currentSlot); const swapInstructions = (0, utils_1.removeBudgetIxs)(swapQuoteIxs.swapIxs); const ixs = [ ...scopeRefreshIx, ...budgetIxs, ...createAtasIxs, ...[flashBorrowIx], ...(collIsKtoken ? swapInstructions : []), ...classes_1.KaminoAction.actionToIxs(depositAction), ...classes_1.KaminoAction.actionToIxs(borrowAction), ...(collIsKtoken ? [] : swapInstructions), ...[flashRepayIx], ]; const flashBorrowReserve = !collIsKtoken ? collReserve : debtReserve; const res = { flashLoanInfo: { flashBorrowReserve: flashBorrowReserve.address, flashLoanFee: flashBorrowReserve.getFlashLoanFee(), }, instructions: ixs, }; return res; } /** * Withdraw and repay tokens if leverage decreased */ async function buildDecreaseLeverageIxs(owner, kaminoMarket, collTokenMint, debtTokenMint, obligation, referrer, currentSlot, calcs, strategy, scopeRefreshConfig, collIsKtoken, swapQuoteIxs, budgetAndPriorityFeeIxs, useV2Ixs) { const collReserve = kaminoMarket.getExistingReserveByMint(collTokenMint); const debtReserve = kaminoMarket.getExistingReserveByMint(debtTokenMint); const debtTokenAta = (0, spl_token_1.getAssociatedTokenAddressSync)(debtTokenMint, owner, false, debtReserve.getLiquidityTokenProgram()); // 1. Create atas & budget txns const { budgetIxs, createAtasIxs, scopeRefreshIx } = await (0, exports.getSetupIxs)(owner, kaminoMarket, obligation, collTokenMint, collReserve, collIsKtoken, debtTokenMint, debtReserve, strategy, scopeRefreshConfig, budgetAndPriorityFeeIxs); // TODO: Mihai/Marius check if we can improve this logic and not convert any SOL // This is here so that we have enough wsol to repay in case the kAB swapped to sol after estimates is not enough const closeWsolAtaIxs = []; const fillWsolAtaIxs = []; if (debtTokenMint.equals(spl_token_1.NATIVE_MINT)) { const wsolAta = (0, utils_1.getAssociatedTokenAddress)(spl_token_1.NATIVE_MINT, owner, false); closeWsolAtaIxs.push((0, spl_token_1.createCloseAccountInstruction)(wsolAta, owner, owner, [], spl_token_1.TOKEN_PROGRAM_ID)); const halfSolBalance = (await kaminoMarket.getConnection().getBalance(owner)) / web3_js_1.LAMPORTS_PER_SOL / 2; const balanceToWrap = halfSolBalance < 0.1 ? halfSolBalance : 0.1; fillWsolAtaIxs.push(...(0, utils_1.getTransferWsolIxs)(owner, wsolAta, (0, classes_2.numberToLamportsDecimal)(balanceToWrap, debtReserve.stats.decimals).ceil())); } // 3. Flash borrow & repay amount to repay (debt) const { flashBorrowIx, flashRepayIx } = (0, instructions_1.getFlashLoanInstructions)({ borrowIxIndex: budgetIxs.length + createAtasIxs.length + fillWsolAtaIxs.length + (scopeRefreshIx.length > 0 ? 1 : 0), walletPublicKey: owner, lendingMarketAuthority: kaminoMarket.getLendingMarketAuthority(), lendingMarketAddress: kaminoMarket.getAddress(), reserve: debtReserve, amountLamports: (0, classes_2.numberToLamportsDecimal)(decimal_js_1.default.abs(calcs.adjustBorrowPosition), debtReserve.stats.decimals), destinationAta: debtTokenAta, // TODO(referrals): once we support referrals, we will have to replace the placeholder args below: referrerAccount: kaminoMarket.programId, referrerTokenState: kaminoMarket.programId, programId: kaminoMarket.programId, }); // 4. Actually do the repay of the flash borrowed amounts const repayAction = await classes_1.KaminoAction.buildRepayTxns(kaminoMarket, (0, classes_2.numberToLamportsDecimal)(decimal_js_1.default.abs(calcs.adjustBorrowPosition), debtReserve.stats.decimals).floor().toString(), debtTokenMint, owner, obligation, useV2Ixs, undefined, currentSlot, undefined, 0, false, false, { skipInitialization: true, skipLutCreation: true }, // to be checked and create in a setup tx in the UI (won't be the case for adjust anyway as this would be created in deposit) referrer); // 6. Withdraw collateral (a little bit more to be able to pay for the slippage on swap) const withdrawAction = await classes_1.KaminoAction.buildWithdrawTxns(kaminoMarket, (0, classes_2.numberToLamportsDecimal)(calcs.withdrawAmountWithSlippageAndFlashLoanFee, collReserve.stats.decimals).ceil().toString(), collTokenMint, owner, obligation, useV2Ixs, undefined, 0, false, false, { skipInitialization: true, skipLutCreation: true }, // to be checked and create in a setup tx in the UI (won't be the case for adjust anyway as this would be created in deposit) referrer, currentSlot); const swapInstructions = (0, utils_1.removeBudgetIxs)(swapQuoteIxs.swapIxs); const ixs = [ ...scopeRefreshIx, ...budgetIxs, ...createAtasIxs, ...fillWsolAtaIxs, ...[flashBorrowIx], ...classes_1.KaminoAction.actionToIxs(repayAction), ...classes_1.KaminoAction.actionToIxs(withdrawAction), ...swapInstructions, ...[flashRepayIx], ...closeWsolAtaIxs, ]; const res = { flashLoanInfo: { flashBorrowReserve: debtReserve.address, flashLoanFee: debtReserve.getFlashLoanFee(), }, instructions: ixs, }; return res; } const getSetupIxs = async (owner, kaminoMarket, obligation, collTokenMint, collReserve, collIsKtoken, debtTokenMint, debtReserve, strategy, scopeRefreshConfig, budgetAndPriorityFeeIxs) => { const budgetIxs = budgetAndPriorityFeeIxs || (0, utils_1.getComputeBudgetAndPriorityFeeIxs)(3000000); const mintsWithTokenPrograms = getTokenMintsWithTokenPrograms(collTokenMint, collReserve, debtTokenMint, debtReserve, collIsKtoken, strategy); const createAtasIxs = (0, utils_1.createAtasIdempotent)(owner, mintsWithTokenPrograms).map((x) => x.createAtaIx); const scopeRefreshIx = await (0, exports.getScopeRefreshIx)(kaminoMarket, collReserve, debtReserve, obligation, scopeRefreshConfig); return { budgetIxs, createAtasIxs, scopeRefreshIx, }; }; exports.getSetupIxs = getSetupIxs; const getScopeRefreshIx = async (market, collReserve, debtReserve, obligation, scopeRefreshConfig) => { const allReserves = obligation && (0, classes_1.isKaminoObligation)(obligation) ? new utils_1.PublicKeySet([ ...obligation.getDeposits().map((x) => x.reserveAddress), ...obligation.getBorrows().map((x) => x.reserveAddress), collReserve.address, debtReserve.address, ]).toArray() : new utils_1.PublicKeySet([collReserve.address, debtReserve.address]).toArray(); const tokenIds = (0, classes_1.getTokenIdsForScopeRefresh)(market, allReserves); const scopeRefreshIxs = []; if (tokenIds.length > 0 && scopeRefreshConfig) { scopeRefreshIxs.push(await scopeRefreshConfig.scope.refreshPriceListIx({ feed: scopeRefreshConfig.scopeFeed, }, tokenIds)); } return scopeRefreshIxs; }; exports.getScopeRefreshIx = getScopeRefreshIx; const checkObligationType = (obligationTypeTag, collTokenMint, debtTokenMint, kaminoMarket) => { let obligationType; if (obligationTypeTag === utils_1.ObligationTypeTag.Multiply) { // multiply obligationType = new utils_1.MultiplyObligation(collTokenMint, debtTokenMint, kaminoMarket.programId); } else if (obligationTypeTag === utils_1.ObligationTypeTag.Leverage) { // leverage obligationType = new utils_1.LeverageObligation(collTokenMint, debtTokenMint, kaminoMarket.programId); } else { throw Error('Obligation type tag not supported for leverage, please use 1 - multiply or 3 - leverage'); } return obligationType; }; const getTokenMintsWithTokenPrograms = (collTokenMint, collReserve, debtTokenMint, debtReserve, collIsKtoken, strategy) => { const mintsWithTokenPrograms = [ { mint: collTokenMint, tokenProgram: collReserve.getLiquidityTokenProgram(), }, { mint: debtTokenMint, tokenProgram: debtReserve.getLiquidityTokenProgram(), }, { mint: collReserve.getCTokenMint(), tokenProgram: spl_token_1.TOKEN_PROGRAM_ID, }, ]; if (collIsKtoken) { let kTokenAta; let kTokenTokenProgram; if (strategy.strategy.tokenAMint.equals(debtTokenMint)) { kTokenAta = strategy.strategy.tokenBMint; kTokenTokenProgram = strategy.strategy.tokenBTokenProgram.equals(web3_js_1.PublicKey.default) ? spl_token_1.TOKEN_PROGRAM_ID : strategy.strategy.tokenBTokenProgram; } else { kTokenAta = strategy.strategy.tokenAMint; kTokenTokenProgram = strategy.strategy.tokenATokenProgram.equals(web3_js_1.PublicKey.default) ? spl_token_1.TOKEN_PROGRAM_ID : strategy.strategy.tokenATokenProgram; } mintsWithTokenPrograms.push({ mint: kTokenAta, tokenProgram: kTokenTokenProgram, }); } return mintsWithTokenPrograms; }; //# sourceMappingURL=operations.js.map