UNPKG

@kamino-finance/klend-sdk

Version:

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

877 lines (876 loc) 130 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.KaminoAction = void 0; const kit_1 = require("@solana/kit"); const bn_js_1 = __importDefault(require("bn.js")); const decimal_js_1 = __importDefault(require("decimal.js")); const instructions_1 = require("../@codegen/klend/instructions"); const utils_1 = require("../utils"); const market_1 = require("./market"); const obligation_1 = require("./obligation"); const types_1 = require("../@codegen/klend/types"); const programId_1 = require("@kamino-finance/farms-sdk/dist/@codegen/farms/programId"); const ObligationType_1 = require("../utils/ObligationType"); const token_1 = require("@solana-program/token"); const sysvars_1 = require("@solana/sysvars"); const token_2022_1 = require("@solana-program/token-2022"); const system_1 = require("@solana-program/system"); const signer_1 = require("../utils/signer"); class KaminoAction { kaminoMarket; reserve; outflowReserve; owner; payer; obligation; referrer; /** * Null unless the obligation is not passed */ obligationType = null; mint; secondaryMint; positions; amount; outflowAmount; computeBudgetIxs; computeBudgetIxsLabels; setupIxs; setupIxsLabels; inBetweenIxs; inBetweenIxsLabels; lendingIxs; lendingIxsLabels; cleanupIxs; cleanupIxsLabels; refreshFarmsCleanupTxnIxs; refreshFarmsCleanupTxnIxsLabels; depositReserves; borrowReserves; preLoadedDepositReservesSameTx; currentSlot; constructor(kaminoMarket, owner, obligation, mint, positions, amount, depositReserves, borrowReserves, reserveState, currentSlot, secondaryMint, outflowReserveState, outflowAmount, referrer = (0, kit_1.none)(), payer) { this.kaminoMarket = kaminoMarket; this.obligation = obligation; this.owner = owner; this.payer = payer ?? owner; this.amount = new bn_js_1.default(amount); this.mint = mint; this.positions = positions; this.computeBudgetIxs = []; this.computeBudgetIxsLabels = []; this.setupIxs = []; this.setupIxsLabels = []; this.inBetweenIxs = []; this.inBetweenIxsLabels = []; this.lendingIxs = []; this.lendingIxsLabels = []; this.cleanupIxs = []; this.cleanupIxsLabels = []; this.refreshFarmsCleanupTxnIxs = []; this.refreshFarmsCleanupTxnIxsLabels = []; this.depositReserves = depositReserves; this.borrowReserves = borrowReserves; this.secondaryMint = secondaryMint; this.reserve = reserveState; this.outflowReserve = outflowReserveState; this.outflowAmount = outflowAmount ? new bn_js_1.default(outflowAmount) : undefined; this.preLoadedDepositReservesSameTx = []; this.referrer = referrer; this.currentSlot = currentSlot; } static async initialize(action, amount, mint, owner, kaminoMarket, obligation, referrer = (0, kit_1.none)(), currentSlot = 0n, payer = owner) { const reserve = kaminoMarket.getReserveByMint(mint); if (reserve === undefined) { throw new Error(`Reserve ${mint} not found in market ${kaminoMarket.getAddress()}`); } const { kaminoObligation, depositReserves, borrowReserves, distinctReserveCount } = await KaminoAction.loadObligation(action, kaminoMarket, owner.address, reserve.address, obligation); const referrerKey = await this.getReferrerKey(kaminoMarket, owner.address, kaminoObligation, referrer); return new KaminoAction(kaminoMarket, owner, kaminoObligation || obligation, mint, distinctReserveCount, amount, depositReserves, borrowReserves, reserve, currentSlot, undefined, undefined, undefined, referrerKey, payer); } static async getUserAccountAddresses(owner, reserve) { const [userTokenAccountAddress, userCollateralAccountAddress] = await Promise.all([ (0, utils_1.getAssociatedTokenAddress)(reserve.liquidity.mintPubkey, owner, reserve.liquidity.tokenProgram, token_1.ASSOCIATED_TOKEN_PROGRAM_ADDRESS), (0, utils_1.getAssociatedTokenAddress)(reserve.collateral.mintPubkey, owner, token_1.TOKEN_PROGRAM_ADDRESS, token_1.ASSOCIATED_TOKEN_PROGRAM_ADDRESS), ]); return { userTokenAccountAddress, userCollateralAccountAddress }; } static async loadObligation(action, kaminoMarket, owner, reserve, obligation, outflowReserve) { let kaminoObligation; const depositReserves = []; const borrowReserves = []; if (obligation instanceof obligation_1.KaminoObligation) { kaminoObligation = obligation; } else { const obligationAddress = await obligation.toPda(kaminoMarket.getAddress(), owner); kaminoObligation = await obligation_1.KaminoObligation.load(kaminoMarket, obligationAddress); } if (kaminoObligation !== null) { depositReserves.push(...[...kaminoObligation.deposits.keys()]); borrowReserves.push(...[...kaminoObligation.borrows.keys()]); } if (!outflowReserve && action === 'depositAndBorrow') { throw new Error(`Outflow reserve has not been set for depositAndBorrow`); } // Union of addresses const distinctReserveCount = new Set([ ...borrowReserves.map((e) => e), ...(action === 'borrow' ? [reserve] : []), ...(action === 'depositAndBorrow' ? [reserve] : []), ]).size + new Set([ ...depositReserves.map((e) => e), ...(action === 'deposit' ? [reserve] : []), ...(action === 'depositAndBorrow' ? [outflowReserve] : []), ]).size; return { kaminoObligation, depositReserves, borrowReserves, distinctReserveCount, }; } static async buildRefreshObligationTxns(kaminoMarket, payer, obligation, extraComputeBudget = 1_000_000, // if > 0 then adds the ix currentSlot = 0n) { // placeholder for action initialization const firstReserve = obligation.getDeposits()[0].reserveAddress; const firstKaminoReserve = kaminoMarket.getReserveByAddress(firstReserve); if (!firstKaminoReserve) { throw new Error(`Reserve ${firstReserve} not found`); } const axn = await KaminoAction.initialize('refreshObligation', '0', firstKaminoReserve?.getLiquidityMint(), (0, signer_1.noopSigner)(obligation.state.owner), // owner does not need to sign for refresh kaminoMarket, obligation, undefined, currentSlot); if (extraComputeBudget > 0) { axn.addComputeBudgetIx(extraComputeBudget); } await axn.addRefreshObligation(payer); return axn; } static async buildRequestElevationGroupTxns(kaminoMarket, owner, obligation, elevationGroup, extraComputeBudget = 1_000_000, // if > 0 then adds the ix currentSlot = 0n) { const firstReserve = obligation.state.deposits.find((x) => x.depositReserve !== utils_1.DEFAULT_PUBLIC_KEY).depositReserve; const firstKaminoReserve = kaminoMarket.getReserveByAddress(firstReserve); if (!firstKaminoReserve) { throw new Error(`Reserve ${firstReserve} not found`); } const axn = await KaminoAction.initialize('requestElevationGroup', '0', firstKaminoReserve?.getLiquidityMint(), owner, kaminoMarket, obligation, undefined, currentSlot); if (extraComputeBudget > 0) { axn.addComputeBudgetIx(extraComputeBudget); } await axn.addRefreshObligation(owner); await axn.addRequestElevationIx(elevationGroup, 'setup'); return axn; } static async buildDepositTxns(kaminoMarket, amount, mint, owner, obligation, useV2Ixs, scopeRefreshConfig, extraComputeBudget = 1_000_000, // if > 0 then adds the ix includeAtaIxs = true, // if true it includes create and close wsol and token atas, requestElevationGroup = false, // to be requested *before* the deposit initUserMetadata = { skipInitialization: false, skipLutCreation: false, }, referrer = (0, kit_1.none)(), currentSlot = 0n, overrideElevationGroupRequest = undefined // if set, when an elevationgroup request is made, it will use this value ) { const axn = await KaminoAction.initialize('deposit', amount, mint, owner, kaminoMarket, obligation, referrer, currentSlot); const addInitObligationForFarm = true; if (extraComputeBudget > 0) { axn.addComputeBudgetIx(extraComputeBudget); } await axn.addSupportIxs('deposit', includeAtaIxs, requestElevationGroup, addInitObligationForFarm, useV2Ixs, scopeRefreshConfig, initUserMetadata, undefined, overrideElevationGroupRequest); if (useV2Ixs) { await axn.addDepositIxV2(); } else { await axn.addDepositIx(); } axn.addRefreshFarmsCleanupTxnIxsToCleanupIxs(); return axn; } async addScopeRefreshIxs(scope, tokens, scopeConfig) { const refreshIx = await scope.refreshPriceListIx({ config: scopeConfig }, tokens); if (refreshIx) { this.setupIxsLabels.unshift(`refreshScopePrices`); this.setupIxs.unshift(refreshIx); } } static async buildBorrowTxns(kaminoMarket, amount, mint, owner, obligation, useV2Ixs, scopeRefreshConfig, extraComputeBudget = 1_000_000, // if > 0 then adds the ix includeAtaIxs = true, // if true it includes create and close wsol and token atas, requestElevationGroup = false, initUserMetadata = { skipInitialization: false, skipLutCreation: false, }, referrer = (0, kit_1.none)(), currentSlot = 0n, overrideElevationGroupRequest = undefined // if set, when an elevationgroup request is made, it will use this value ) { const axn = await KaminoAction.initialize('borrow', amount, mint, owner, kaminoMarket, obligation, referrer, currentSlot); const addInitObligationForFarm = true; if (extraComputeBudget > 0) { axn.addComputeBudgetIx(extraComputeBudget); } if ((0, kit_1.isSome)(axn.referrer)) { const referrerTokenState = await (0, utils_1.referrerTokenStatePda)(axn.referrer.value, axn.reserve.address, axn.kaminoMarket.programId); const account = await (0, kit_1.fetchEncodedAccount)(kaminoMarket.getRpc(), referrerTokenState); if (!account.exists) { axn.addInitReferrerTokenStateIx(axn.reserve, referrerTokenState); } } await axn.addSupportIxs('borrow', includeAtaIxs, requestElevationGroup, addInitObligationForFarm, useV2Ixs, scopeRefreshConfig, initUserMetadata, undefined, overrideElevationGroupRequest); if (useV2Ixs) { await axn.addBorrowIxV2(); } else { await axn.addBorrowIx(); } axn.addRefreshFarmsCleanupTxnIxsToCleanupIxs(); return axn; } static async buildDepositReserveLiquidityTxns(kaminoMarket, amount, mint, owner, obligation, scopeRefreshConfig, extraComputeBudget = 1_000_000, // if > 0 then adds the ix includeAtaIxs = true, // if true it includes create and close wsol and token atas requestElevationGroup = false, referrer = (0, kit_1.none)(), currentSlot = 0n) { const axn = await KaminoAction.initialize('mint', amount, mint, owner, kaminoMarket, obligation, referrer, currentSlot); const addInitObligationForFarm = true; if (extraComputeBudget > 0) { axn.addComputeBudgetIx(extraComputeBudget); } await axn.addSupportIxs('mint', includeAtaIxs, requestElevationGroup, false, addInitObligationForFarm, scopeRefreshConfig, { skipInitialization: true, skipLutCreation: true }); await axn.addDepositReserveLiquidityIx(); axn.addRefreshFarmsCleanupTxnIxsToCleanupIxs(); return axn; } static async buildRedeemReserveCollateralTxns(kaminoMarket, amount, mint, owner, obligation, scopeRefreshConfig, extraComputeBudget = 1_000_000, // if > 0 then adds the ix includeAtaIxs = true, // if true it includes create and close wsol and token atas requestElevationGroup = false, referrer = (0, kit_1.none)(), currentSlot = 0n) { const axn = await KaminoAction.initialize('redeem', amount, mint, owner, kaminoMarket, obligation, referrer, currentSlot); const addInitObligationForFarm = true; if (extraComputeBudget > 0) { axn.addComputeBudgetIx(extraComputeBudget); } await axn.addSupportIxs('redeem', includeAtaIxs, requestElevationGroup, false, addInitObligationForFarm, scopeRefreshConfig, { skipInitialization: true, skipLutCreation: true }); await axn.addRedeemReserveCollateralIx(); axn.addRefreshFarmsCleanupTxnIxsToCleanupIxs(); return axn; } static async buildDepositObligationCollateralTxns(kaminoMarket, amount, mint, owner, obligation, useV2Ixs, scopeRefreshConfig, extraComputeBudget = 1_000_000, // if > 0 then adds the ix includeAtaIxs = true, // if true it includes create and close wsol and token atas requestElevationGroup = false, initUserMetadata = { skipInitialization: false, skipLutCreation: false, }, referrer = (0, kit_1.none)(), currentSlot = 0n) { const axn = await KaminoAction.initialize('depositCollateral', amount, mint, owner, kaminoMarket, obligation, referrer, currentSlot); const addInitObligationForFarm = true; if (extraComputeBudget > 0) { axn.addComputeBudgetIx(extraComputeBudget); } await axn.addSupportIxs('depositCollateral', includeAtaIxs, requestElevationGroup, addInitObligationForFarm, useV2Ixs, scopeRefreshConfig, initUserMetadata); if (useV2Ixs) { await axn.addDepositObligationCollateralIxV2(); } else { await axn.addDepositObligationCollateralIx(); } axn.addRefreshFarmsCleanupTxnIxsToCleanupIxs(); return axn; } static async buildDepositAndBorrowTxns(kaminoMarket, depositAmount, depositMint, borrowAmount, borrowMint, owner, obligation, useV2Ixs, scopeRefreshConfig, extraComputeBudget = 1_000_000, // if > 0 then adds the ix includeAtaIxs = true, // if true it includes create and close wsol and token atas, requestElevationGroup = false, initUserMetadata = { skipInitialization: false, skipLutCreation: false, }, referrer = (0, kit_1.none)(), currentSlot = 0n) { const axn = await KaminoAction.initializeMultiTokenAction(kaminoMarket, 'depositAndBorrow', depositAmount, depositMint, borrowMint, owner, owner.address, obligation, borrowAmount, referrer, currentSlot); const addInitObligationForFarmForDeposit = true; const addInitObligationForFarmForBorrow = false; const twoTokenAction = true; if (extraComputeBudget > 0) { axn.addComputeBudgetIx(extraComputeBudget); } if ((0, kit_1.isSome)(axn.referrer)) { const referrerTokenState = await (0, utils_1.referrerTokenStatePda)(axn.referrer.value, axn.outflowReserve.address, axn.kaminoMarket.programId); const account = await (0, kit_1.fetchEncodedAccount)(axn.kaminoMarket.getRpc(), referrerTokenState); if (!account.exists) { axn.addInitReferrerTokenStateIx(axn.outflowReserve, referrerTokenState); } } await axn.addSupportIxs('deposit', includeAtaIxs, requestElevationGroup, addInitObligationForFarmForDeposit, useV2Ixs, undefined, initUserMetadata, twoTokenAction); if (useV2Ixs) { await axn.addDepositAndBorrowIxV2(); } else { await axn.addDepositAndBorrowIx(); } await axn.addInBetweenIxs('depositAndBorrow', includeAtaIxs, requestElevationGroup, addInitObligationForFarmForBorrow, useV2Ixs); axn.addRefreshFarmsCleanupTxnIxsToCleanupIxs(); // Create the scope refresh ix in here to ensure it's the first ix in the tx const allReserves = [ ...new Set([ ...axn.depositReserves, ...axn.borrowReserves, axn.reserve.address, ...(axn.outflowReserve ? [axn.outflowReserve.address] : []), ...(axn.preLoadedDepositReservesSameTx ? axn.preLoadedDepositReservesSameTx : []), ]), ]; const scopeTokensMap = (0, market_1.getTokenIdsForScopeRefresh)(axn.kaminoMarket, allReserves); if (scopeTokensMap.size > 0 && scopeRefreshConfig) { for (const [configPubkey, config] of scopeRefreshConfig.scopeConfigurations) { const tokenIds = scopeTokensMap.get(config.oraclePrices); if (tokenIds && tokenIds.length > 0) { await axn.addScopeRefreshIxs(scopeRefreshConfig.scope, tokenIds, configPubkey); } } } return axn; } static async buildDepositAndWithdrawV2Txns(kaminoMarket, depositAmount, depositMint, withdrawAmount, withdrawMint, owner, currentSlot, obligation, scopeRefreshConfig, extraComputeBudget = 1_000_000, // if > 0 then adds the ix includeAtaIxs = true, // if true it includes create and close wsol and token atas, requestElevationGroup = false, initUserMetadata = { skipInitialization: false, skipLutCreation: false, }, referrer = (0, kit_1.none)()) { const axn = await KaminoAction.initializeMultiTokenAction(kaminoMarket, 'depositAndWithdraw', depositAmount, depositMint, withdrawMint, owner, owner.address, obligation, withdrawAmount, referrer, currentSlot); const addInitObligationForFarm = true; const twoTokenAction = true; if (extraComputeBudget > 0) { axn.addComputeBudgetIx(extraComputeBudget); } await axn.addSupportIxs('depositAndWithdraw', includeAtaIxs, requestElevationGroup, addInitObligationForFarm, true, scopeRefreshConfig, initUserMetadata, twoTokenAction); const withdrawCollateralAmount = axn.getWithdrawCollateralAmount(axn.outflowReserve, axn.outflowAmount); await axn.addDepositAndWithdrawV2Ixs(withdrawCollateralAmount); return axn; } static async buildRepayAndWithdrawV2Txns(kaminoMarket, repayAmount, repayMint, withdrawAmount, withdrawMint, payer, currentSlot, obligation, scopeRefreshConfig, extraComputeBudget = 1_000_000, // if > 0 then adds the ix includeAtaIxs = true, // if true it includes create and close wsol and token atas, requestElevationGroup = false, initUserMetadata = { skipInitialization: false, skipLutCreation: false, }, referrer = (0, kit_1.none)()) { const axn = await KaminoAction.initializeMultiTokenAction(kaminoMarket, 'repayAndWithdrawV2', repayAmount, repayMint, withdrawMint, payer, payer.address, obligation, withdrawAmount, referrer, currentSlot); const addInitObligationForFarm = true; const twoTokenAction = true; if (extraComputeBudget > 0) { axn.addComputeBudgetIx(extraComputeBudget); } await axn.addSupportIxs('repayAndWithdrawV2', includeAtaIxs, requestElevationGroup, addInitObligationForFarm, true, scopeRefreshConfig, initUserMetadata, twoTokenAction); const withdrawCollateralAmount = axn.getWithdrawCollateralAmount(axn.outflowReserve, axn.outflowAmount); await axn.addRepayAndWithdrawV2Ixs(withdrawCollateralAmount); return axn; } static async buildRepayAndWithdrawTxns(kaminoMarket, repayAmount, repayMint, withdrawAmount, withdrawMint, payer, currentSlot, obligation, useV2Ixs, scopeRefreshConfig, extraComputeBudget = 1_000_000, // if > 0 then adds the ix includeAtaIxs = true, // if true it includes create and close wsol and token atas, requestElevationGroup = false, initUserMetadata = { skipInitialization: false, skipLutCreation: false, }, referrer = (0, kit_1.none)()) { const axn = await KaminoAction.initializeMultiTokenAction(kaminoMarket, 'repayAndWithdraw', repayAmount, repayMint, withdrawMint, payer, payer.address, obligation, withdrawAmount, referrer, currentSlot); const addInitObligationForFarmForRepay = true; const addInitObligationForFarmForWithdraw = false; const twoTokenAction = true; if (extraComputeBudget > 0) { axn.addComputeBudgetIx(extraComputeBudget); } await axn.addSupportIxs('repay', includeAtaIxs, requestElevationGroup, addInitObligationForFarmForRepay, useV2Ixs, undefined, initUserMetadata, twoTokenAction); const withdrawCollateralAmount = axn.getWithdrawCollateralAmount(axn.outflowReserve, axn.outflowAmount); if (useV2Ixs) { await axn.addRepayAndWithdrawIxsV2(withdrawCollateralAmount); } else { await axn.addRepayAndWithdrawIxs(withdrawCollateralAmount); } await axn.addInBetweenIxs('repayAndWithdraw', includeAtaIxs, requestElevationGroup, addInitObligationForFarmForWithdraw, useV2Ixs); axn.addRefreshFarmsCleanupTxnIxsToCleanupIxs(); // Create the scope refresh ix in here to ensure it's the first ix in the tx const allReserves = [ ...new Set([ ...axn.depositReserves, ...axn.borrowReserves, axn.reserve.address, ...(axn.outflowReserve ? [axn.outflowReserve.address] : []), ...(axn.preLoadedDepositReservesSameTx ? axn.preLoadedDepositReservesSameTx : []), ]), ]; const scopeTokensMap = (0, market_1.getTokenIdsForScopeRefresh)(axn.kaminoMarket, allReserves); if (scopeTokensMap.size > 0 && scopeRefreshConfig) { for (const [configPubkey, config] of scopeRefreshConfig.scopeConfigurations) { const tokenIds = scopeTokensMap.get(config.oraclePrices); if (tokenIds && tokenIds.length > 0) { await axn.addScopeRefreshIxs(scopeRefreshConfig.scope, tokenIds, configPubkey); } } } return axn; } static async buildWithdrawTxns(kaminoMarket, amount, mint, owner, obligation, useV2Ixs, scopeRefreshConfig, extraComputeBudget = 1_000_000, // if > 0 then adds the ix includeAtaIxs = true, // if true it includes create and close wsol and token atas, requestElevationGroup = false, // to be requested *after* the withdraw initUserMetadata = { skipInitialization: false, skipLutCreation: false, }, referrer = (0, kit_1.none)(), currentSlot = 0n, overrideElevationGroupRequest, // Optional customizations which may be needed if the obligation was mutated by some previous ix. obligationCustomizations) { const axn = await KaminoAction.initialize('withdraw', amount, mint, owner, kaminoMarket, obligation, referrer, currentSlot); const addInitObligationForFarm = true; if (extraComputeBudget > 0) { axn.addComputeBudgetIx(extraComputeBudget); } axn.depositReserves.push(...(obligationCustomizations?.addedDepositReserves || [])); await axn.addSupportIxs('withdraw', includeAtaIxs, requestElevationGroup, addInitObligationForFarm, useV2Ixs, scopeRefreshConfig, initUserMetadata, false, overrideElevationGroupRequest); const collateralAmount = axn.getWithdrawCollateralAmount(axn.reserve, axn.amount); if (useV2Ixs) { await axn.addWithdrawIxV2(collateralAmount); } else { await axn.addWithdrawIx(collateralAmount); } axn.addRefreshFarmsCleanupTxnIxsToCleanupIxs(); return axn; } /** * * @param kaminoMarket * @param amount * @param mint * @param owner * @param obligation - obligation to repay or the PDA seeds * @param useV2Ixs * @param scopeRefreshConfig * @param currentSlot * @param payer - if not set then owner is used * @param extraComputeBudget - if > 0 then adds the ix * @param includeAtaIxs - if true it includes create and close wsol and token atas * @param requestElevationGroup * @param initUserMetadata * @param referrer */ static async buildRepayTxns(kaminoMarket, amount, mint, owner, obligation, useV2Ixs, scopeRefreshConfig, currentSlot, payer = owner, extraComputeBudget = 1_000_000, includeAtaIxs = true, requestElevationGroup = false, initUserMetadata = { skipInitialization: false, skipLutCreation: false, }, referrer = (0, kit_1.none)()) { const axn = await KaminoAction.initialize('repay', amount, mint, owner, kaminoMarket, obligation, referrer, currentSlot, payer); const addInitObligationForFarm = true; if (extraComputeBudget > 0) { axn.addComputeBudgetIx(extraComputeBudget); } await axn.addSupportIxs('repay', includeAtaIxs, requestElevationGroup, addInitObligationForFarm, useV2Ixs, scopeRefreshConfig, initUserMetadata); if (useV2Ixs) { await axn.addRepayIxV2(); } else { await axn.addRepayIx(); } axn.addRefreshFarmsCleanupTxnIxsToCleanupIxs(); return axn; } static async buildLiquidateTxns(kaminoMarket, amount, minCollateralReceiveAmount, repayTokenMint, withdrawTokenMint, liquidator, obligationOwner, obligation, useV2Ixs, scopeRefreshConfig = undefined, extraComputeBudget = 1_000_000, // if > 0 then adds the ix includeAtaIxs = true, // if true it includes create and close wsol and token atas, and creates all other token atas if they don't exist requestElevationGroup = false, initUserMetadata = { skipInitialization: false, skipLutCreation: false, }, referrer = (0, kit_1.none)(), maxAllowedLtvOverridePercent = 0, currentSlot = 0n) { const axn = await KaminoAction.initializeMultiTokenAction(kaminoMarket, 'liquidate', amount, repayTokenMint, withdrawTokenMint, liquidator, obligationOwner, obligation, minCollateralReceiveAmount, referrer, currentSlot); const addInitObligationForFarm = true; if (extraComputeBudget > 0) { axn.addComputeBudgetIx(extraComputeBudget); } await axn.addSupportIxs('liquidate', includeAtaIxs, requestElevationGroup, addInitObligationForFarm, useV2Ixs, scopeRefreshConfig, initUserMetadata); if (useV2Ixs) { await axn.addLiquidateIxV2(maxAllowedLtvOverridePercent); } else { await axn.addLiquidateIx(maxAllowedLtvOverridePercent); } axn.addRefreshFarmsCleanupTxnIxsToCleanupIxs(); return axn; } static async buildWithdrawReferrerFeeTxns(owner, tokenMint, kaminoMarket, currentSlot = 0n) { const { axn, createAtaIxs } = await KaminoAction.initializeWithdrawReferrerFees(tokenMint, owner, kaminoMarket, currentSlot); axn.setupIxs.push(...createAtaIxs); axn.setupIxsLabels.push(`createAtasIxs[${axn.owner.toString()}]`); if ((0, kit_1.isSome)(axn.referrer)) { const referrerTokenState = await (0, utils_1.referrerTokenStatePda)(axn.referrer.value, axn.reserve.address, axn.kaminoMarket.programId); const account = await (0, kit_1.fetchEncodedAccount)(axn.kaminoMarket.getRpc(), referrerTokenState); if (!account.exists) { axn.addInitReferrerTokenStateIx(axn.reserve, referrerTokenState); } } axn.addRefreshReserveIxs([axn.reserve.address]); await axn.addWithdrawReferrerFeesIxs(); return axn; } /** * Builds an instruction for setting the new state of one of the given obligation's orders. * * In other words: it will overwrite the given slot in the {@link Obligation.orders} array. This possibly includes * setting the `null` state (i.e. cancelling the order). */ static buildSetObligationOrderIxn(owner, kaminoMarket, obligation, orderAtIndex) { return (0, instructions_1.setObligationOrder)({ index: orderAtIndex.index, order: orderAtIndex.orderState(), }, { lendingMarket: kaminoMarket.getAddress(), obligation: obligation.obligationAddress, owner, }, undefined, kaminoMarket.programId); } async addDepositReserveLiquidityIx() { this.lendingIxsLabels.push(`depositReserveLiquidity`); this.lendingIxs.push((0, instructions_1.depositReserveLiquidity)({ liquidityAmount: this.amount, }, { owner: this.owner, lendingMarket: this.kaminoMarket.getAddress(), lendingMarketAuthority: await this.kaminoMarket.getLendingMarketAuthority(), reserve: this.reserve.address, reserveLiquidityMint: this.reserve.getLiquidityMint(), reserveLiquiditySupply: this.reserve.state.liquidity.supplyVault, reserveCollateralMint: this.reserve.getCTokenMint(), userSourceLiquidity: await this.getUserTokenAccountAddress(this.reserve), userDestinationCollateral: await this.getUserCollateralAccountAddress(this.reserve), collateralTokenProgram: token_1.TOKEN_PROGRAM_ADDRESS, liquidityTokenProgram: this.reserve.getLiquidityTokenProgram(), instructionSysvarAccount: sysvars_1.SYSVAR_INSTRUCTIONS_ADDRESS, }, undefined, this.kaminoMarket.programId)); } async addRedeemReserveCollateralIx() { this.lendingIxsLabels.push(`redeemReserveCollateral`); this.lendingIxs.push((0, instructions_1.redeemReserveCollateral)({ collateralAmount: this.amount, }, { owner: this.owner, lendingMarket: this.kaminoMarket.getAddress(), lendingMarketAuthority: await this.kaminoMarket.getLendingMarketAuthority(), reserve: this.reserve.address, reserveLiquidityMint: this.reserve.getLiquidityMint(), reserveLiquiditySupply: this.reserve.state.liquidity.supplyVault, reserveCollateralMint: this.reserve.getCTokenMint(), userSourceCollateral: await this.getUserCollateralAccountAddress(this.reserve), userDestinationLiquidity: await this.getUserTokenAccountAddress(this.reserve), collateralTokenProgram: token_1.TOKEN_PROGRAM_ADDRESS, liquidityTokenProgram: this.reserve.getLiquidityTokenProgram(), instructionSysvarAccount: sysvars_1.SYSVAR_INSTRUCTIONS_ADDRESS, }, undefined, this.kaminoMarket.programId)); } // @deprecated -- use addDepositIxV2 instead async addDepositIx() { this.lendingIxsLabels.push(`depositReserveLiquidityAndObligationCollateral`); this.lendingIxs.push((0, instructions_1.depositReserveLiquidityAndObligationCollateral)({ liquidityAmount: this.amount, }, { owner: this.owner, obligation: await this.getObligationPda(), lendingMarket: this.kaminoMarket.getAddress(), lendingMarketAuthority: await this.kaminoMarket.getLendingMarketAuthority(), reserve: this.reserve.address, reserveLiquidityMint: this.reserve.getLiquidityMint(), reserveLiquiditySupply: this.reserve.state.liquidity.supplyVault, reserveCollateralMint: this.reserve.getCTokenMint(), reserveDestinationDepositCollateral: this.reserve.state.collateral.supplyVault, // destinationCollateral userSourceLiquidity: await this.getUserTokenAccountAddress(this.reserve), placeholderUserDestinationCollateral: (0, kit_1.none)(), collateralTokenProgram: token_1.TOKEN_PROGRAM_ADDRESS, liquidityTokenProgram: this.reserve.getLiquidityTokenProgram(), instructionSysvarAccount: sysvars_1.SYSVAR_INSTRUCTIONS_ADDRESS, }, undefined, this.kaminoMarket.programId)); } async addDepositIxV2() { const { collateralFarmAccounts: farmsAccounts } = await KaminoAction.getFarmAccountsForReserve(await this.getObligationPda(), this.reserve); this.lendingIxsLabels.push(`depositReserveLiquidityAndObligationCollateralV2`); this.lendingIxs.push((0, instructions_1.depositReserveLiquidityAndObligationCollateralV2)({ liquidityAmount: this.amount, }, { depositAccounts: { owner: this.owner, obligation: await this.getObligationPda(), lendingMarket: this.kaminoMarket.getAddress(), lendingMarketAuthority: await this.kaminoMarket.getLendingMarketAuthority(), reserve: this.reserve.address, reserveLiquidityMint: this.reserve.getLiquidityMint(), reserveLiquiditySupply: this.reserve.state.liquidity.supplyVault, reserveCollateralMint: this.reserve.getCTokenMint(), reserveDestinationDepositCollateral: this.reserve.state.collateral.supplyVault, // destinationCollateral userSourceLiquidity: await this.getUserTokenAccountAddress(this.reserve), placeholderUserDestinationCollateral: (0, kit_1.none)(), collateralTokenProgram: token_1.TOKEN_PROGRAM_ADDRESS, liquidityTokenProgram: this.reserve.getLiquidityTokenProgram(), instructionSysvarAccount: sysvars_1.SYSVAR_INSTRUCTIONS_ADDRESS, }, farmsAccounts, farmsProgram: programId_1.PROGRAM_ID, }, undefined, this.kaminoMarket.programId)); } /// @deprecated -- use addDepositObligationCollateralIxV2 instead async addDepositObligationCollateralIx() { this.lendingIxsLabels.push(`depositObligationCollateral`); this.lendingIxs.push((0, instructions_1.depositObligationCollateral)({ collateralAmount: this.amount, }, { owner: this.owner, obligation: await this.getObligationPda(), lendingMarket: this.kaminoMarket.getAddress(), depositReserve: this.reserve.address, reserveDestinationCollateral: this.reserve.state.collateral.supplyVault, userSourceCollateral: await this.getUserCollateralAccountAddress(this.reserve), tokenProgram: token_1.TOKEN_PROGRAM_ADDRESS, instructionSysvarAccount: sysvars_1.SYSVAR_INSTRUCTIONS_ADDRESS, }, undefined, this.kaminoMarket.programId)); } async addDepositObligationCollateralIxV2() { const obligationAddress = await this.getObligationPda(); const { collateralFarmAccounts: farmsAccounts } = await KaminoAction.getFarmAccountsForReserve(obligationAddress, this.reserve); this.lendingIxsLabels.push(`depositObligationCollateralV2`); this.lendingIxs.push((0, instructions_1.depositObligationCollateralV2)({ collateralAmount: this.amount, }, { depositAccounts: { owner: this.owner, obligation: obligationAddress, lendingMarket: this.kaminoMarket.getAddress(), depositReserve: this.reserve.address, reserveDestinationCollateral: this.reserve.state.collateral.supplyVault, userSourceCollateral: await this.getUserCollateralAccountAddress(this.reserve), tokenProgram: token_1.TOKEN_PROGRAM_ADDRESS, instructionSysvarAccount: sysvars_1.SYSVAR_INSTRUCTIONS_ADDRESS, }, lendingMarketAuthority: await this.kaminoMarket.getLendingMarketAuthority(), farmsAccounts, farmsProgram: programId_1.PROGRAM_ID, }, undefined, this.kaminoMarket.programId)); } /// @deprecated -- use addDepositObligationCollateralIxV2 instead async addBorrowIx() { this.lendingIxsLabels.push(`borrowObligationLiquidity`); const depositReservesList = this.getAdditionalDepositReservesList(); const depositReserveAccountMetas = depositReservesList.map((reserve) => { return { address: reserve, role: kit_1.AccountRole.WRITABLE }; }); let borrowIx = (0, instructions_1.borrowObligationLiquidity)({ liquidityAmount: this.amount, }, { owner: this.owner, obligation: await this.getObligationPda(), lendingMarket: this.kaminoMarket.getAddress(), lendingMarketAuthority: await this.kaminoMarket.getLendingMarketAuthority(), borrowReserve: this.reserve.address, borrowReserveLiquidityMint: this.reserve.getLiquidityMint(), reserveSourceLiquidity: this.reserve.state.liquidity.supplyVault, userDestinationLiquidity: await this.getUserTokenAccountAddress(this.reserve), borrowReserveLiquidityFeeReceiver: this.reserve.state.liquidity.feeVault, referrerTokenState: await this.getReferrerTokenStateAddress(this.reserve.address), tokenProgram: this.reserve.getLiquidityTokenProgram(), instructionSysvarAccount: sysvars_1.SYSVAR_INSTRUCTIONS_ADDRESS, }, undefined, this.kaminoMarket.programId); borrowIx = { ...borrowIx, accounts: (0, obligation_1.isKaminoObligation)(this.obligation) && (this.obligation.state.elevationGroup > 0 || this.obligation.refreshedStats.potentialElevationGroupUpdate > 0) ? borrowIx.accounts.concat(depositReserveAccountMetas) : borrowIx.accounts, }; this.lendingIxs.push(borrowIx); } async addBorrowIxV2() { this.lendingIxsLabels.push(`borrowObligationLiquidityV2`); const depositReservesList = this.getAdditionalDepositReservesList(); const depositReserveAccountMetas = depositReservesList.map((reserve) => { return { address: reserve, role: kit_1.AccountRole.WRITABLE }; }); const obligationAddress = await this.getObligationPda(); const { debtFarmAccounts: farmsAccounts } = await KaminoAction.getFarmAccountsForReserve(obligationAddress, this.reserve); let borrowIx = (0, instructions_1.borrowObligationLiquidityV2)({ liquidityAmount: this.amount, }, { borrowAccounts: { owner: this.owner, obligation: obligationAddress, lendingMarket: this.kaminoMarket.getAddress(), lendingMarketAuthority: await this.kaminoMarket.getLendingMarketAuthority(), borrowReserve: this.reserve.address, borrowReserveLiquidityMint: this.reserve.getLiquidityMint(), reserveSourceLiquidity: this.reserve.state.liquidity.supplyVault, userDestinationLiquidity: await this.getUserTokenAccountAddress(this.reserve), borrowReserveLiquidityFeeReceiver: this.reserve.state.liquidity.feeVault, referrerTokenState: await this.getReferrerTokenStateAddress(this.reserve.address), tokenProgram: this.reserve.getLiquidityTokenProgram(), instructionSysvarAccount: sysvars_1.SYSVAR_INSTRUCTIONS_ADDRESS, }, farmsAccounts, farmsProgram: programId_1.PROGRAM_ID, }, undefined, this.kaminoMarket.programId); borrowIx = { ...borrowIx, accounts: (0, obligation_1.isKaminoObligation)(this.obligation) && (this.obligation.state.elevationGroup > 0 || this.obligation.refreshedStats.potentialElevationGroupUpdate > 0) ? borrowIx.accounts.concat(depositReserveAccountMetas) : borrowIx.accounts, }; this.lendingIxs.push(borrowIx); } /// @deprecated -- use addWithdrawIxV2 instead async addWithdrawIx(collateralAmount) { this.lendingIxsLabels.push(`withdrawObligationCollateralAndRedeemReserveCollateral`); this.lendingIxs.push((0, instructions_1.withdrawObligationCollateralAndRedeemReserveCollateral)({ collateralAmount, }, { owner: this.owner, obligation: await this.getObligationPda(), lendingMarket: this.kaminoMarket.getAddress(), lendingMarketAuthority: await this.kaminoMarket.getLendingMarketAuthority(), withdrawReserve: this.reserve.address, reserveLiquidityMint: this.reserve.getLiquidityMint(), reserveCollateralMint: this.reserve.getCTokenMint(), reserveLiquiditySupply: this.reserve.state.liquidity.supplyVault, reserveSourceCollateral: this.reserve.state.collateral.supplyVault, userDestinationLiquidity: await this.getUserTokenAccountAddress(this.reserve), placeholderUserDestinationCollateral: (0, kit_1.none)(), collateralTokenProgram: token_1.TOKEN_PROGRAM_ADDRESS, liquidityTokenProgram: this.reserve.getLiquidityTokenProgram(), instructionSysvarAccount: sysvars_1.SYSVAR_INSTRUCTIONS_ADDRESS, }, undefined, this.kaminoMarket.programId)); } async addWithdrawIxV2(collateralAmount) { const obligationAddress = await this.getObligationPda(); const { collateralFarmAccounts: farmsAccounts } = await KaminoAction.getFarmAccountsForReserve(obligationAddress, this.reserve); this.lendingIxsLabels.push(`withdrawObligationCollateralAndRedeemReserveCollateralV2`); this.lendingIxs.push((0, instructions_1.withdrawObligationCollateralAndRedeemReserveCollateralV2)({ collateralAmount, }, { withdrawAccounts: { owner: this.owner, obligation: obligationAddress, lendingMarket: this.kaminoMarket.getAddress(), lendingMarketAuthority: await this.kaminoMarket.getLendingMarketAuthority(), withdrawReserve: this.reserve.address, reserveLiquidityMint: this.reserve.getLiquidityMint(), reserveCollateralMint: this.reserve.getCTokenMint(), reserveLiquiditySupply: this.reserve.state.liquidity.supplyVault, reserveSourceCollateral: this.reserve.state.collateral.supplyVault, userDestinationLiquidity: await this.getUserTokenAccountAddress(this.reserve), placeholderUserDestinationCollateral: (0, kit_1.none)(), collateralTokenProgram: token_1.TOKEN_PROGRAM_ADDRESS, liquidityTokenProgram: this.reserve.getLiquidityTokenProgram(), instructionSysvarAccount: sysvars_1.SYSVAR_INSTRUCTIONS_ADDRESS, }, farmsAccounts: farmsAccounts, farmsProgram: programId_1.PROGRAM_ID, }, undefined, this.kaminoMarket.programId)); } /// @deprecated -- use addRepayIxV2 instead async addRepayIx() { const obligationAddress = await this.getObligationPda(); this.lendingIxsLabels.push(`repayObligationLiquidity(reserve=${this.reserve.address})(obligation=${obligationAddress})`); const depositReservesList = this.getAdditionalDepositReservesList(); const depositReserveAccountMetas = depositReservesList.map((reserve) => { return { address: reserve, role: kit_1.AccountRole.WRITABLE }; }); let repayIx = (0, instructions_1.repayObligationLiquidity)({ liquidityAmount: this.amount, }, { owner: this.payer, obligation: obligationAddress, lendingMarket: this.kaminoMarket.getAddress(), repayReserve: this.reserve.address, reserveLiquidityMint: this.reserve.getLiquidityMint(), userSourceLiquidity: await this.getTokenAccountAddressByUser(this.reserve, this.payer.address), reserveDestinationLiquidity: this.reserve.state.liquidity.supplyVault, tokenProgram: this.reserve.getLiquidityTokenProgram(), instructionSysvarAccount: sysvars_1.SYSVAR_INSTRUCTIONS_ADDRESS, }, undefined, this.kaminoMarket.programId); repayIx = { ...repayIx, accounts: (0, obligation_1.isKaminoObligation)(this.obligation) && this.obligation.state.elevationGroup > 0 ? repayIx.accounts.concat(depositReserveAccountMetas) : repayIx.accounts, }; this.lendingIxs.push(repayIx); } async addRepayIxV2() { const obligationAddress = await this.getObligationPda(); this.lendingIxsLabels.push(`repayObligationLiquidityV2(reserve=${this.reserve.address})(obligation=${obligationAddress})`); const depositReservesList = this.getAdditionalDepositReservesList(); const { debtFarmAccounts: farmsAccounts } = await KaminoAction.getFarmAccountsForReserve(obligationAddress, this.reserve); const depositReserveAccountMetas = depositReservesList.map((reserve) => { return { address: reserve, role: kit_1.AccountRole.WRITABLE }; }); let repayIx = (0, instructions_1.repayObligationLiquidityV2)({ liquidityAmount: this.amount, }, { repayAccounts: { owner: this.payer, obligation: obligationAddress, lendingMarket: this.kaminoMarket.getAddress(), repayReserve: this.reserve.address, reserveLiquidityMint: this.reserve.getLiquidityMint(), userSourceLiquidity: await this.getTokenAccountAddressByUser(this.reserve, this.payer.address), reserveDestinationLiquidity: this.reserve.state.liquidity.supplyVault, tokenProgram: this.reserve.getLiquidityTokenProgram(), instructionSysvarAccount: sysvars_1.SYSVAR_INSTRUCTIONS_ADDRESS, }, lendingMarketAuthority: await this.kaminoMarket.getLendingMarketAuthority(), farmsAccounts, farmsProgram: programId_1.PROGRAM_ID, }, undefined, this.kaminoMarket.programId); repayIx = { ...repayIx, // TODO: potential elev group update? accounts: (0, obligation_1.isKaminoObligation)(this.obligation) && this.obligation.state.elevationGroup > 0 ? repayIx.accounts.concat(depositReserveAccountMetas) : repayIx.accounts, }; this.lendingIxs.push(repayIx); } async addRepayAndWithdrawV2Ixs(withdrawCollateralAmount) { const obligationAddress = await this.getObligationPda(); this.lendingIxsLabels.push(`repayAndWithdrawAndRedeem(repayReserve=${this.reserve.address})(withdrawReserve=${this.outflowReserve.address})(obligation=${obligationAddress})`); const depositReservesList = this.getAdditionalDepositReservesList(); const depositReserveAccountMetas = depositReservesList.map((reserve) => { return { address: reserve, role: kit_1.AccountRole.WRITABLE }; }); const borrowReserveAccountMetas = this.borrowReserves.map((reserve) => { return { address: reserve, role: kit_1.AccountRole.WRITABLE }; }); if (!this.outflowAmount) { throw new Error(`outflowAmount not set`); } if (!this.outflowReserve) { throw new Error(`outflowReserve not set`); } const { collateralFarmAccounts } = await KaminoAction.getFarmAccountsForReserve(obligationAddress, this.outflowReserve); const { debtFarmAccounts } = await KaminoAction.getFarmAccountsForReserve(obligationAddress, this.reserve); let repayAndWithdrawIx = (0, instructions_1.repayAndWithdrawAndRedeem)({ repayAmount: this.amount, withdrawCollateralAmount, }, { repayAccounts: { owner: this.owner, obligation: obligationAddress, lendingMarket: this.kaminoMarket.getAddress(), repayReserve: this.reserve.address, reserveLiquidityMint: this.reserve.getLiquidityMint(), userSourceLiquidity: await this.getUserTokenAccountAddress(this.reserve), reserveDestinationLiquidity: this.reserve.state.liquidity.supplyVault, tokenProgram: this.reserve.getLiquidityTokenProgram(), instructionSysvarAccount: sysvars_1.SYSVAR_INSTRUCTIONS_ADDRESS, }, withdrawAccounts: { owner: this.owner, obligation: obligationAddress, lendingMarket: this.kaminoMarket.getAddress(), lendingMarketAuthority: await this.kaminoMarket.getLendingMarketAuthority(), withdrawReserve: this.outflowReserve.address, reserveLiquidityMint: this.outflowReserve.getLiquidityMint(), reserveCollateralMint: this.outflowReserve.getCTokenMint(), reserveLiquiditySupply: this.outflowReserve.state.liquidity.supplyVault, reserveSourceCollateral: this.outflowReserve.state.collateral.supplyVault, userDestinationLiquidity: await this.getUserTokenAccountAddress(this.outflowReserve), placeholderUserDestinationCollateral: (0, kit_1.none)(), collateralTokenProgram: token_1.TOKEN_PROGRAM_ADDRESS, liquidityTokenProgram: this.outflowReserve.getLiquidityTokenProgram(), instructionSysvarAccount: sysvars_1.SYSVAR_INSTRUCTIONS_ADDRESS, }, collateralFarmsAccounts: collateralFarmAccounts, debtFarmsAccounts: debtFarmAccounts, farmsProgram: programId_1.PROGRAM_ID, }, undefined, this.kaminoMarket.programId); repayAndWithdrawIx = { ...repayAndWithdrawIx,