UNPKG

@kamino-finance/klend-sdk

Version:

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

164 lines 10 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.isBorrowingEnabled = exports.getExpectedTokenBalanceAfterBorrow = void 0; exports.getTokenToKtokenSwapper = getTokenToKtokenSwapper; exports.getKtokenDepositIxs = getKtokenDepositIxs; exports.getKtokenToTokenSwapper = getKtokenToTokenSwapper; exports.getKtokenWithdrawIxs = getKtokenWithdrawIxs; exports.getKtokenWithdrawEstimatesAndPrice = getKtokenWithdrawEstimatesAndPrice; exports.swapProviderToKaminoSwapProvider = swapProviderToKaminoSwapProvider; const classes_1 = require("../classes"); const decimal_js_1 = __importDefault(require("decimal.js")); const utils_1 = require("../utils"); const utils_2 = require("../classes/utils"); const bn_js_1 = __importDefault(require("bn.js")); async function getTokenToKtokenSwapper(kaminoMarket, kamino, depositor, slippagePct, swapper, priceAinB, includeAtaIxns = true) { return async (inputs, klendAccounts, quote) => { const slippageBps = new decimal_js_1.default(slippagePct).mul('100'); const mintInDecimals = kaminoMarket.getReserveByMint(inputs.inputMint).state.liquidity.mintDecimals.toNumber(); const amountIn = (0, classes_1.lamportsToNumberDecimal)(inputs.inputAmountLamports, mintInDecimals); console.debug('Depositing token', inputs.inputMint.toString(), ' for ', inputs.outputMint.toString(), 'ktoken'); if (inputs.amountDebtAtaBalance === undefined) { throw Error('Amount in debt ATA balance is undefined for leverage ktoken deposit'); } const ixWithLookup = (await getKtokenDepositIxs(kaminoMarket.getConnection(), kamino, depositor, inputs.inputMint, inputs.outputMint, amountIn, slippageBps, inputs.amountDebtAtaBalance, swapper, priceAinB, includeAtaIxns, klendAccounts, quote)); const luts = await (0, utils_1.getLookupTableAccounts)(kaminoMarket.getConnection(), ixWithLookup.lookupTablesAddresses); return { preActionIxs: [], swapIxs: ixWithLookup.instructions, lookupTables: luts, }; }; } async function getKtokenDepositIxs(connection, kamino, depositor, depositTokenMint, ktokenMint, amountToDeposit, slippageBps, amountExpectedDepositAtaBalance, swapper, priceAinB, includeAtaIxns = true, klendAccounts, quote) { const kaminoStrategy = await kamino.getStrategyByKTokenMint(ktokenMint); const tokenAMint = kaminoStrategy?.strategy.tokenAMint; const tokenBMint = kaminoStrategy?.strategy.tokenBMint; const priceAinBDecimal = await priceAinB(tokenAMint, tokenBMint); if (tokenAMint.equals(depositTokenMint)) { const bBalance = await (0, utils_1.getTokenAccountBalanceDecimal)(connection, tokenBMint, depositor); const tokensBalances = { a: amountExpectedDepositAtaBalance, b: bBalance }; console.log('amountToDeposit', amountToDeposit); return await kamino.singleSidedDepositTokenA(kaminoStrategy, amountToDeposit, depositor, slippageBps, undefined, swapProviderToKaminoSwapProvider(swapper, klendAccounts, quote), tokensBalances, priceAinBDecimal, includeAtaIxns); } else if (tokenBMint.equals(depositTokenMint)) { const aBalance = await (0, utils_1.getTokenAccountBalanceDecimal)(connection, tokenAMint, depositor); const tokensBalances = { a: aBalance, b: amountExpectedDepositAtaBalance }; return await kamino.singleSidedDepositTokenB(kaminoStrategy, amountToDeposit, depositor, slippageBps, undefined, swapProviderToKaminoSwapProvider(swapper, klendAccounts, quote), tokensBalances, priceAinBDecimal, includeAtaIxns); } else { throw Error('Deposit token is neither A nor B in the strategy'); } } async function getKtokenToTokenSwapper(kaminoMarket, kamino, depositor, swapper) { return async (inputs, klendAccounts, quote) => { const amountInDecimals = kaminoMarket.getReserveByMint(inputs.inputMint).state.liquidity.mintDecimals.toNumber(); const amountToWithdraw = (0, classes_1.lamportsToNumberDecimal)(inputs.inputAmountLamports, amountInDecimals); const kaminoStrategy = await kamino.getStrategyByKTokenMint(inputs.inputMint); console.log('Withdrawing ktoken', inputs.inputMint.toString(), ' for ', inputs.outputMint.toString(), 'token'); const ixWithdraw = (await getKtokenWithdrawIxs(kamino, depositor, kaminoStrategy, amountToWithdraw)); const [estimatedAOut, estimatedBOut] = await getKtokenWithdrawEstimatesAndPrice(kamino, kaminoStrategy, amountToWithdraw); if (inputs.outputMint.equals(kaminoStrategy.strategy.tokenAMint)) { const { swapIxs, lookupTables } = await swapper({ inputAmountLamports: estimatedBOut, inputMint: kaminoStrategy.strategy.tokenBMint, outputMint: kaminoStrategy.strategy.tokenAMint, amountDebtAtaBalance: new decimal_js_1.default(0), }, klendAccounts, quote); return { preActionIxs: [], swapIxs: [...ixWithdraw.prerequisiteIxs, ixWithdraw.withdrawIx, ...swapIxs], lookupTables, }; } else if (inputs.outputMint.equals(kaminoStrategy.strategy.tokenBMint)) { const { swapIxs, lookupTables } = await swapper({ inputAmountLamports: estimatedAOut, inputMint: kaminoStrategy.strategy.tokenAMint, outputMint: kaminoStrategy.strategy.tokenBMint, amountDebtAtaBalance: new decimal_js_1.default(0), }, klendAccounts, quote); return { preActionIxs: [], swapIxs: [...ixWithdraw.prerequisiteIxs, ixWithdraw.withdrawIx, ...swapIxs], lookupTables, }; } else { throw Error('Deposit token is neither A nor B in the strategy'); } }; } async function getKtokenWithdrawIxs(kamino, withdrawer, kaminoStrategy, amountToWithdraw) { return await kamino.withdrawShares(kaminoStrategy, amountToWithdraw, withdrawer); } async function getKtokenWithdrawEstimatesAndPrice(kamino, kaminoStrategy, amountToWithdraw) { const sharesData = await kamino.getStrategyShareData(kaminoStrategy); const withdrawPct = amountToWithdraw .div((0, classes_1.lamportsToNumberDecimal)(new decimal_js_1.default(kaminoStrategy.strategy.sharesIssued.toString()), kaminoStrategy.strategy.sharesMintDecimals.toNumber())) .toDecimalPlaces(18); const withdrawFee = new decimal_js_1.default(10_000).sub(new decimal_js_1.default(kaminoStrategy.strategy.withdrawFee.toString())); // TODO: Mihai/Marius improve - currently subtracting due to decimal accuracy issues compared to yvaults SC // for both A and B op: .sub(0.000002) const estimatedAOut = sharesData.balance.computedHoldings.invested.a .add(sharesData.balance.computedHoldings.available.a) .mul(withdrawPct) .toDecimalPlaces(kaminoStrategy.strategy.tokenAMintDecimals.toNumber(), 1) .sub(0.000002) .mul(withdrawFee) .div(10_000) .toDecimalPlaces(kaminoStrategy.strategy.tokenAMintDecimals.toNumber()); const estimatedAOutDecimal = (0, utils_2.numberToLamportsDecimal)(estimatedAOut, kaminoStrategy.strategy.tokenAMintDecimals.toNumber()).floor(); const estimatedBOut = sharesData.balance.computedHoldings.invested.b .add(sharesData.balance.computedHoldings.available.b) .mul(withdrawPct) .toDecimalPlaces(kaminoStrategy.strategy.tokenBMintDecimals.toNumber(), 1) .sub(0.000002) .mul(withdrawFee) .div(10_000) .toDecimalPlaces(kaminoStrategy.strategy.tokenAMintDecimals.toNumber()); const estimatedBOutDecimal = (0, utils_2.numberToLamportsDecimal)(estimatedBOut, kaminoStrategy.strategy.tokenBMintDecimals.toNumber()).floor(); console.log('a-out', estimatedAOutDecimal.toString()); console.log('b-out', estimatedBOut.toString()); return [estimatedAOutDecimal, estimatedBOutDecimal]; } function swapProviderToKaminoSwapProvider(swapper, klendAccounts, swapQuote) { return async (input, tokenAMint, tokenBMint, _owner, _slippage, _allKeys) => { if (input.tokenBToSwapAmount.lt(0)) { const swapperIxs = await swapper({ inputAmountLamports: input.tokenBToSwapAmount.abs(), inputMint: tokenBMint, outputMint: tokenAMint, amountDebtAtaBalance: undefined, }, klendAccounts, swapQuote); return [swapperIxs.swapIxs, swapperIxs.lookupTables.map((lt) => lt.key)]; } else if (input.tokenAToSwapAmount.lt(0)) { const swapperIxs = await swapper({ inputAmountLamports: input.tokenAToSwapAmount.abs(), inputMint: tokenAMint, outputMint: tokenBMint, amountDebtAtaBalance: undefined, }, klendAccounts, swapQuote); return [swapperIxs.swapIxs, swapperIxs.lookupTables.map((lt) => lt.key)]; } else { throw Error('Nothing to swap'); } }; } const getExpectedTokenBalanceAfterBorrow = async (connection, mint, owner, amountToBorrowLamports, amountToBorrowmintDecimals) => { const initialUserTokenABalance = await (0, utils_1.getTokenAccountBalanceDecimal)(connection, mint, owner); return initialUserTokenABalance .add((0, classes_1.lamportsToNumberDecimal)(amountToBorrowLamports, amountToBorrowmintDecimals)) .toDecimalPlaces(amountToBorrowmintDecimals); }; exports.getExpectedTokenBalanceAfterBorrow = getExpectedTokenBalanceAfterBorrow; const isBorrowingEnabled = (reserve) => { return reserve.state.config.borrowLimit.gt(new bn_js_1.default(0)); }; exports.isBorrowingEnabled = isBorrowingEnabled; //# sourceMappingURL=utils.js.map