@kamino-finance/klend-sdk 5.0.2 → 5.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dist/classes/obligation.d.ts.map +1 -1
  2. package/dist/classes/obligation.js +1 -2
  3. package/dist/classes/obligation.js.map +1 -1
  4. package/dist/classes/reserve.d.ts +0 -1
  5. package/dist/classes/reserve.d.ts.map +1 -1
  6. package/dist/classes/reserve.js +3 -7
  7. package/dist/classes/reserve.js.map +1 -1
  8. package/dist/lending_operations/repay_with_collateral_operations.d.ts.map +1 -1
  9. package/dist/lending_operations/repay_with_collateral_operations.js +6 -0
  10. package/dist/lending_operations/repay_with_collateral_operations.js.map +1 -1
  11. package/dist/leverage/calcs.d.ts +28 -1
  12. package/dist/leverage/calcs.d.ts.map +1 -1
  13. package/dist/leverage/calcs.js +204 -8
  14. package/dist/leverage/calcs.js.map +1 -1
  15. package/dist/leverage/index.d.ts +1 -0
  16. package/dist/leverage/index.d.ts.map +1 -1
  17. package/dist/leverage/index.js +1 -0
  18. package/dist/leverage/index.js.map +1 -1
  19. package/dist/leverage/operations.d.ts +14 -241
  20. package/dist/leverage/operations.d.ts.map +1 -1
  21. package/dist/leverage/operations.js +508 -776
  22. package/dist/leverage/operations.js.map +1 -1
  23. package/dist/leverage/types.d.ts +173 -0
  24. package/dist/leverage/types.d.ts.map +1 -0
  25. package/dist/leverage/types.js +3 -0
  26. package/dist/leverage/types.js.map +1 -0
  27. package/dist/leverage/utils.d.ts +5 -5
  28. package/dist/leverage/utils.d.ts.map +1 -1
  29. package/dist/leverage/utils.js +68 -33
  30. package/dist/leverage/utils.js.map +1 -1
  31. package/dist/utils/constants.d.ts +1 -0
  32. package/dist/utils/constants.d.ts.map +1 -1
  33. package/dist/utils/constants.js +2 -1
  34. package/dist/utils/constants.js.map +1 -1
  35. package/dist/utils/fuzz.d.ts +3 -0
  36. package/dist/utils/fuzz.d.ts.map +1 -0
  37. package/dist/utils/fuzz.js +11 -0
  38. package/dist/utils/fuzz.js.map +1 -0
  39. package/dist/utils/index.d.ts +1 -0
  40. package/dist/utils/index.d.ts.map +1 -1
  41. package/dist/utils/index.js +1 -0
  42. package/dist/utils/index.js.map +1 -1
  43. package/package.json +1 -1
  44. package/src/classes/obligation.ts +1 -2
  45. package/src/classes/reserve.ts +3 -16
  46. package/src/lending_operations/repay_with_collateral_operations.ts +2 -0
  47. package/src/leverage/calcs.ts +315 -8
  48. package/src/leverage/index.ts +1 -0
  49. package/src/leverage/operations.ts +1079 -1331
  50. package/src/leverage/types.ts +211 -0
  51. package/src/leverage/utils.ts +103 -64
  52. package/src/utils/constants.ts +2 -0
  53. package/src/utils/fuzz.ts +5 -0
  54. package/src/utils/index.ts +1 -0
@@ -3,7 +3,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.getDecreaseLeverageIxns = exports.getIncreaseLeverageIxns = exports.getAdjustLeverageIxns = exports.getAdjustLeverageSwapInputs = exports.getWithdrawWithLeverageIxns = exports.getWithdrawWithLeverageSwapInputs = exports.getDepositWithLeverageIxns = exports.getDepositWithLeverageSwapInputs = exports.depositLeverageKtokenCalcs = exports.depositLeverageCalcs = void 0;
6
+ exports.getDepositWithLeverageSwapInputs = getDepositWithLeverageSwapInputs;
7
+ exports.getDepositWithLeverageIxns = getDepositWithLeverageIxns;
8
+ exports.getWithdrawWithLeverageSwapInputs = getWithdrawWithLeverageSwapInputs;
9
+ exports.getWithdrawWithLeverageIxns = getWithdrawWithLeverageIxns;
10
+ exports.buildWithdrawWithLeverageIxns = buildWithdrawWithLeverageIxns;
11
+ exports.getAdjustLeverageSwapInputs = getAdjustLeverageSwapInputs;
12
+ exports.getAdjustLeverageIxns = getAdjustLeverageIxns;
7
13
  const web3_js_1 = require("@solana/web3.js");
8
14
  const decimal_js_1 = __importDefault(require("decimal.js"));
9
15
  const classes_1 = require("../classes");
@@ -13,157 +19,93 @@ const utils_1 = require("../utils");
13
19
  const calcs_1 = require("./calcs");
14
20
  const spl_token_1 = require("@solana/spl-token");
15
21
  const utils_2 = require("./utils");
16
- const depositLeverageCalcs = (props) => {
17
- // Initialize local variables from the props object
18
- const { depositAmount, depositTokenIsCollToken, depositTokenIsSol, priceDebtToColl, targetLeverage, slippagePct, flashLoanFee, } = props;
19
- const slippage = slippagePct.div('100');
20
- const initDepositInSol = depositTokenIsSol ? depositAmount : new decimal_js_1.default(0);
21
- // Core logic
22
- if (depositTokenIsCollToken) {
23
- const y = targetLeverage.mul(priceDebtToColl);
24
- const x = flashLoanFee.add('1').mul(slippage.add('1')).div(priceDebtToColl);
25
- const finalColl = depositAmount.mul(x).div(x.sub(targetLeverage.sub('1').div(y)));
26
- const debt = finalColl.sub(depositAmount).mul(x);
27
- const flashBorrowColl = finalColl.sub(depositAmount).mul(flashLoanFee.add('1'));
28
- return {
29
- flashBorrowInCollToken: flashBorrowColl,
30
- initDepositInSol,
31
- debtTokenToBorrow: debt,
32
- collTokenToDeposit: finalColl,
33
- swapDebtTokenIn: debt,
34
- swapCollTokenExpectedOut: finalColl.sub(depositAmount),
35
- };
36
- }
37
- else {
38
- const y = targetLeverage.mul(priceDebtToColl);
39
- const x = flashLoanFee.add('1').mul(slippage.add('1')).div(priceDebtToColl);
40
- const finalColl = depositAmount.div(x.sub(targetLeverage.sub('1').div(y)));
41
- const flashBorrowColl = finalColl.mul(flashLoanFee.add('1'));
42
- const debt = targetLeverage.sub('1').mul(finalColl).div(y);
43
- return {
44
- flashBorrowInCollToken: flashBorrowColl,
45
- initDepositInSol,
46
- debtTokenToBorrow: debt,
47
- collTokenToDeposit: finalColl,
48
- swapDebtTokenIn: debt.add(depositAmount),
49
- swapCollTokenExpectedOut: finalColl,
50
- };
22
+ const CreationParameters_1 = require("@kamino-finance/kliquidity-sdk/dist/utils/CreationParameters");
23
+ async function getDepositWithLeverageSwapInputs({ owner, kaminoMarket, debtTokenMint, collTokenMint, depositAmount, priceDebtToColl, slippagePct, obligation, referrer, currentSlot, targetLeverage, selectedTokenMint, kamino, obligationTypeTagOverride, scopeFeed, budgetAndPriorityFeeIxs, quoteBufferBps, priceAinB, isKtoken, quoter, }) {
24
+ const collReserve = kaminoMarket.getReserveByMint(collTokenMint);
25
+ const debtReserve = kaminoMarket.getReserveByMint(debtTokenMint);
26
+ const solTokenReserve = kaminoMarket.getReserveByMint(utils_1.WRAPPED_SOL_MINT);
27
+ const flashLoanFee = collReserve.getFlashLoanFee() || new decimal_js_1.default(0);
28
+ const selectedTokenIsCollToken = selectedTokenMint.equals(collTokenMint);
29
+ const depositTokenIsSol = !solTokenReserve ? false : selectedTokenMint.equals(solTokenReserve.getLiquidityMint());
30
+ const collIsKtoken = await isKtoken(collTokenMint);
31
+ const strategy = collIsKtoken ? (await kamino.getStrategyByKTokenMint(collTokenMint)) : undefined;
32
+ const calcs = await getDepositWithLeverageCalcs(depositAmount, selectedTokenIsCollToken, collIsKtoken, depositTokenIsSol, priceDebtToColl, targetLeverage, slippagePct, flashLoanFee, kamino, strategy, debtTokenMint, priceAinB, debtReserve);
33
+ console.log('Ops Calcs', (0, calcs_1.toJson)(calcs));
34
+ let obligationType;
35
+ if (obligationTypeTagOverride === utils_1.ObligationTypeTag.Multiply) {
36
+ // multiply
37
+ obligationType = new utils_1.MultiplyObligation(collTokenMint, debtTokenMint, kaminoMarket.programId);
51
38
  }
52
- };
53
- exports.depositLeverageCalcs = depositLeverageCalcs;
54
- const depositLeverageKtokenCalcs = async (props) => {
55
- const { kamino, strategy, debtTokenMint, depositAmount, depositTokenIsCollToken, depositTokenIsSol, priceDebtToColl, targetLeverage, slippagePct, flashLoanFee, priceAinB, strategyHoldings, } = props;
56
- const initDepositInSol = depositTokenIsSol ? depositAmount : new decimal_js_1.default(0);
57
- const slippage = slippagePct.div('100');
58
- let flashBorrowInDebtToken;
59
- let collTokenToDeposit;
60
- let debtTokenToBorrow;
61
- if (depositTokenIsCollToken) {
62
- const x = slippage.add('1').div(priceDebtToColl);
63
- const y = flashLoanFee.add('1').mul(priceDebtToColl);
64
- const z = targetLeverage.mul(y).div(targetLeverage.sub(1));
65
- flashBorrowInDebtToken = depositAmount.div(z.minus(new decimal_js_1.default(1).div(x)));
66
- collTokenToDeposit = depositAmount.add(flashBorrowInDebtToken.div(x));
67
- debtTokenToBorrow = flashBorrowInDebtToken.mul(new decimal_js_1.default(1).add(flashLoanFee));
68
- return {
69
- flashBorrowInDebtToken,
70
- initDepositInSol,
71
- collTokenToDeposit,
72
- debtTokenToBorrow,
73
- requiredCollateral: collTokenToDeposit.sub(depositAmount), // Assuming netValue is requiredCollateral, adjust as needed
74
- singleSidedDeposit: flashBorrowInDebtToken,
75
- };
39
+ else if (obligationTypeTagOverride === utils_1.ObligationTypeTag.Leverage) {
40
+ // leverage
41
+ obligationType = new utils_1.LeverageObligation(collTokenMint, debtTokenMint, kaminoMarket.programId);
76
42
  }
77
43
  else {
78
- const y = targetLeverage.mul(priceDebtToColl);
79
- // although we will only swap ~half of the debt token, we account for the slippage on the entire amount as we are working backwards from the minimum collateral and do not know the exact swap proportion in advance
80
- // This also allows for some variation in the pool ratios between calculation + submitting the tx
81
- const x = flashLoanFee.add('1').mul(slippage.add('1')).div(priceDebtToColl);
82
- // Calculate the amount of collateral tokens we will deposit in order to achieve the desired leverage after swapping a portion of the debt token and flash loan fees
83
- const finalColl = depositAmount.div(x.sub(targetLeverage.sub('1').div(y)));
84
- // Calculate how many A and B tokens we will need to actually mint the desired amount of ktoken collateral
85
- // The actual amount of ktokens received may be less than the finalColl due to smart proportional contract logic
86
- // So we use the actualColl as the amount we will deposit
87
- const [estimatedA, estimatedB, actualColl] = await (0, calcs_1.simulateMintKToken)(kamino, strategy, finalColl, strategyHoldings);
88
- const pxAinB = await priceAinB(strategy.strategy.tokenAMint, strategy.strategy.tokenBMint);
89
- const isTokenADeposit = strategy.strategy.tokenAMint.equals(debtTokenMint);
90
- // Calculate the amount we need to flash borrow by combining value of A and B into the debt token
91
- const singleSidedDepositAmount = isTokenADeposit
92
- ? estimatedA.add(estimatedB.div(pxAinB))
93
- : estimatedB.add(estimatedA.mul(pxAinB));
94
- // Add slippage to the entire amount, add flash loan fee to part we will flash borrow
95
- flashBorrowInDebtToken = singleSidedDepositAmount
96
- .div(new decimal_js_1.default('1').sub(slippage))
97
- .sub(depositAmount)
98
- .div(new decimal_js_1.default('1').sub(flashLoanFee));
99
- // Deposit the min ktoken amount we calculated at the beginning
100
- // Any slippage will be left in the user's wallet as ktokens
101
- collTokenToDeposit = actualColl;
102
- debtTokenToBorrow = flashBorrowInDebtToken.div(new decimal_js_1.default('1').sub(flashLoanFee));
103
- // Add slippage to ensure we try to swap/deposit as much as possible after flash loan fees
104
- const singleSidedDeposit = singleSidedDepositAmount.div(new decimal_js_1.default('1').sub(slippage));
105
- return {
106
- flashBorrowInDebtToken,
107
- initDepositInSol,
108
- collTokenToDeposit,
109
- debtTokenToBorrow,
110
- requiredCollateral: collTokenToDeposit, // Assuming collTokenToDeposit is requiredCollateral, adjust as needed
111
- singleSidedDeposit,
112
- };
44
+ throw Error('Obligation type tag not supported for leverage, please use 1 - multiply or 3 - leverage');
45
+ }
46
+ // Build the repay & withdraw collateral tx to get the number of accounts
47
+ const klendIxs = await buildDepositWithLeverageIxns(kaminoMarket, debtReserve, collReserve, owner, obligation ? obligation : obligationType, referrer, currentSlot, depositTokenIsSol, scopeFeed, calcs, budgetAndPriorityFeeIxs, {
48
+ preActionIxs: [],
49
+ swapIxs: [],
50
+ lookupTables: [],
51
+ }, strategy, collIsKtoken);
52
+ const uniqueKlendAccounts = (0, utils_1.uniqueAccounts)(klendIxs);
53
+ const swapInputAmount = (0, classes_2.numberToLamportsDecimal)(!collIsKtoken ? calcs.swapDebtTokenIn : calcs.singleSidedDepositKtokenOnly, debtReserve.stats.decimals).ceil();
54
+ const swapInputsForQuote = {
55
+ inputAmountLamports: swapInputAmount.mul(new decimal_js_1.default(1).add(quoteBufferBps.div(CreationParameters_1.FullBPS))),
56
+ inputMint: debtTokenMint,
57
+ outputMint: collTokenMint,
58
+ amountDebtAtaBalance: new decimal_js_1.default(0), // Only needed for ktokens swaps
59
+ };
60
+ const swapQuote = await quoter(swapInputsForQuote, uniqueKlendAccounts);
61
+ const quotePriceCalcs = await getDepositWithLeverageCalcs(depositAmount, selectedTokenIsCollToken, collIsKtoken, depositTokenIsSol, swapQuote.priceAInB, targetLeverage, slippagePct, flashLoanFee, kamino, strategy, debtTokenMint, priceAinB, debtReserve);
62
+ const swapInputAmountQuotePrice = (0, classes_2.numberToLamportsDecimal)(!collIsKtoken ? quotePriceCalcs.swapDebtTokenIn : quotePriceCalcs.singleSidedDepositKtokenOnly, debtReserve.stats.decimals).ceil();
63
+ let expectedDebtTokenAtaBalance = new decimal_js_1.default(0);
64
+ if (collIsKtoken) {
65
+ let futureBalanceInAta = new decimal_js_1.default(0);
66
+ if (debtTokenMint.equals(utils_1.WRAPPED_SOL_MINT)) {
67
+ futureBalanceInAta = futureBalanceInAta.add(!collIsKtoken ? quotePriceCalcs.initDepositInSol : quotePriceCalcs.initDepositInSol);
68
+ }
69
+ futureBalanceInAta = futureBalanceInAta.add(!collIsKtoken ? quotePriceCalcs.debtTokenToBorrow : quotePriceCalcs.flashBorrowInDebtTokenKtokenOnly);
70
+ 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());
113
71
  }
114
- };
115
- exports.depositLeverageKtokenCalcs = depositLeverageKtokenCalcs;
116
- const getDepositWithLeverageSwapInputs = (props) => {
117
- const { depositAmount, priceDebtToColl, slippagePct, targetLeverage, kaminoMarket, selectedTokenMint, debtTokenMint, collTokenMint, } = props;
118
- const debtReserve = kaminoMarket.getReserveByMint(debtTokenMint);
119
- const selectedTokenIsCollToken = selectedTokenMint.equals(collTokenMint);
120
- const solTokenReserve = kaminoMarket.getReserveByMint(utils_1.WRAPPED_SOL_MINT);
121
- const depositTokenIsSol = !solTokenReserve ? false : selectedTokenMint.equals(solTokenReserve.getLiquidityMint());
122
- const flashLoanFee = debtReserve?.getFlashLoanFee() || new decimal_js_1.default(0);
123
- const calcs = (0, exports.depositLeverageCalcs)({
124
- depositAmount,
125
- depositTokenIsCollToken: selectedTokenIsCollToken,
126
- depositTokenIsSol,
127
- priceDebtToColl,
128
- targetLeverage,
129
- slippagePct,
130
- flashLoanFee,
131
- });
132
72
  return {
133
73
  swapInputs: {
134
- inputAmountLamports: (0, classes_2.numberToLamportsDecimal)(calcs.swapDebtTokenIn, debtReserve.state.liquidity.mintDecimals.toNumber()).ceil(),
74
+ inputAmountLamports: swapInputAmountQuotePrice,
135
75
  inputMint: debtTokenMint,
136
76
  outputMint: collTokenMint,
77
+ amountDebtAtaBalance: expectedDebtTokenAtaBalance,
78
+ },
79
+ initialInputs: {
80
+ calcs: quotePriceCalcs,
81
+ swapQuote,
82
+ currentSlot,
83
+ collIsKtoken,
84
+ strategy,
85
+ obligation: obligation ? obligation : obligationType,
86
+ klendAccounts: uniqueKlendAccounts,
137
87
  },
138
88
  };
139
- };
140
- exports.getDepositWithLeverageSwapInputs = getDepositWithLeverageSwapInputs;
141
- const getDepositWithLeverageIxns = async (props) => {
142
- const { connection, budgetAndPriorityFeeIxns, user, amount, selectedTokenMint, collTokenMint, debtTokenMint, targetLeverage, kaminoMarket, slippagePct, priceDebtToColl, swapper, referrer, isKtoken, priceAinB, kamino, obligationTypeTagOverride = 1, obligation, currentSlot, getTotalKlendAccountsOnly, scopeFeed, } = props;
143
- const collReserve = kaminoMarket.getReserveByMint(collTokenMint);
144
- const debtReserve = kaminoMarket.getReserveByMint(debtTokenMint);
145
- const solTokenReserve = kaminoMarket.getReserveByMint(utils_1.WRAPPED_SOL_MINT);
146
- const flashLoanFee = collReserve.getFlashLoanFee() || new decimal_js_1.default(0);
147
- const selectedTokenIsCollToken = selectedTokenMint.equals(collTokenMint);
148
- const depositTokenIsSol = !solTokenReserve ? false : selectedTokenMint.equals(solTokenReserve.getLiquidityMint());
149
- const collIsKtoken = await isKtoken(collTokenMint);
150
- const strategy = collIsKtoken ? (await kamino.getStrategyByKTokenMint(collTokenMint)) : undefined;
151
- const calcs = (0, exports.depositLeverageCalcs)({
152
- depositAmount: amount,
153
- depositTokenIsCollToken: selectedTokenIsCollToken,
154
- depositTokenIsSol,
155
- priceDebtToColl,
156
- targetLeverage,
157
- slippagePct,
158
- flashLoanFee,
159
- });
160
- let calcsKtoken;
161
- if (collIsKtoken) {
162
- calcsKtoken = await (0, exports.depositLeverageKtokenCalcs)({
89
+ }
90
+ async function getDepositWithLeverageCalcs(depositAmount, selectedTokenIsCollToken, collIsKtoken, depositTokenIsSol, priceDebtToColl, targetLeverage, slippagePct, flashLoanFee, kamino, strategy, debtTokenMint, priceAinB, debtReserve) {
91
+ let calcs;
92
+ if (!collIsKtoken) {
93
+ calcs = (0, calcs_1.depositLeverageCalcs)({
94
+ depositAmount: depositAmount,
95
+ depositTokenIsCollToken: selectedTokenIsCollToken,
96
+ depositTokenIsSol,
97
+ priceDebtToColl,
98
+ targetLeverage,
99
+ slippagePct,
100
+ flashLoanFee,
101
+ });
102
+ }
103
+ else {
104
+ calcs = await (0, calcs_1.depositLeverageKtokenCalcs)({
163
105
  kamino: kamino,
164
106
  strategy: strategy,
165
107
  debtTokenMint,
166
- depositAmount: amount,
108
+ depositAmount: depositAmount,
167
109
  depositTokenIsCollToken: selectedTokenIsCollToken,
168
110
  depositTokenIsSol,
169
111
  priceDebtToColl,
@@ -173,23 +115,84 @@ const getDepositWithLeverageIxns = async (props) => {
173
115
  priceAinB,
174
116
  });
175
117
  // Rounding to exact number of decimals so this value is passed through in all calcs without rounding inconsistencies
176
- calcsKtoken.flashBorrowInDebtToken = calcsKtoken.flashBorrowInDebtToken.toDecimalPlaces(debtReserve?.state.liquidity.mintDecimals.toNumber(), decimal_js_1.default.ROUND_CEIL);
177
- calcsKtoken.debtTokenToBorrow = calcsKtoken.debtTokenToBorrow.toDecimalPlaces(debtReserve?.state.liquidity.mintDecimals.toNumber(), decimal_js_1.default.ROUND_CEIL);
178
- calcsKtoken.singleSidedDeposit = calcsKtoken.singleSidedDeposit.toDecimalPlaces(debtReserve?.state.liquidity.mintDecimals.toNumber(), decimal_js_1.default.ROUND_CEIL);
118
+ calcs.flashBorrowInDebtTokenKtokenOnly = calcs.flashBorrowInDebtTokenKtokenOnly.toDecimalPlaces(debtReserve.state.liquidity.mintDecimals.toNumber(), decimal_js_1.default.ROUND_CEIL);
119
+ calcs.debtTokenToBorrow = calcs.debtTokenToBorrow.toDecimalPlaces(debtReserve.state.liquidity.mintDecimals.toNumber(), decimal_js_1.default.ROUND_CEIL);
120
+ calcs.singleSidedDepositKtokenOnly = calcs.singleSidedDepositKtokenOnly.toDecimalPlaces(debtReserve.state.liquidity.mintDecimals.toNumber(), decimal_js_1.default.ROUND_CEIL);
121
+ }
122
+ return calcs;
123
+ }
124
+ async function getDepositWithLeverageIxns({ owner, kaminoMarket, debtTokenMint, collTokenMint, depositAmount, priceDebtToColl, slippagePct, obligation, referrer, currentSlot, targetLeverage, selectedTokenMint, kamino, obligationTypeTagOverride, scopeFeed, budgetAndPriorityFeeIxs, quoteBufferBps, priceAinB, isKtoken, quoter, swapper, }) {
125
+ const { swapInputs, initialInputs } = await getDepositWithLeverageSwapInputs({
126
+ owner,
127
+ kaminoMarket,
128
+ debtTokenMint,
129
+ collTokenMint,
130
+ depositAmount,
131
+ priceDebtToColl,
132
+ slippagePct,
133
+ obligation,
134
+ referrer,
135
+ currentSlot,
136
+ targetLeverage,
137
+ selectedTokenMint,
138
+ kamino,
139
+ obligationTypeTagOverride,
140
+ scopeFeed,
141
+ budgetAndPriorityFeeIxs,
142
+ quoteBufferBps,
143
+ priceAinB,
144
+ isKtoken,
145
+ quoter,
146
+ });
147
+ let depositSwapper;
148
+ if (!initialInputs.collIsKtoken) {
149
+ depositSwapper = swapper;
150
+ }
151
+ else {
152
+ if (kamino === undefined) {
153
+ throw Error('Ktoken use as collateral for leverage without Kamino instance');
154
+ }
155
+ depositSwapper = await (0, utils_2.getTokenToKtokenSwapper)(kaminoMarket, kamino, owner, slippagePct, swapper, priceAinB, false);
156
+ }
157
+ const { swapIxs, lookupTables } = await depositSwapper(swapInputs, initialInputs.klendAccounts, initialInputs.swapQuote);
158
+ if (initialInputs.collIsKtoken) {
159
+ if (initialInputs.strategy.strategy.strategyLookupTable) {
160
+ const strategyLut = await (0, utils_1.getLookupTableAccount)(kaminoMarket.getConnection(), initialInputs.strategy.strategy.strategyLookupTable);
161
+ lookupTables.push(strategyLut);
162
+ }
163
+ else {
164
+ console.log('Strategy lookup table not found');
165
+ }
179
166
  }
180
- console.log('Ops Calcs', (0, calcs_1.toJson)(!collIsKtoken ? calcs : calcsKtoken));
181
- console.log('Infos', (0, calcs_1.toJson)({
182
- depositTokenIsSol,
183
- selectedTokenIsCollToken,
184
- initDepositInSol: calcs.initDepositInSol,
185
- }));
167
+ const collReserve = kaminoMarket.getReserveByMint(collTokenMint);
168
+ const debtReserve = kaminoMarket.getReserveByMint(debtTokenMint);
169
+ const solTokenReserve = kaminoMarket.getReserveByMint(utils_1.WRAPPED_SOL_MINT);
170
+ const depositTokenIsSol = !solTokenReserve ? false : selectedTokenMint.equals(solTokenReserve.getLiquidityMint());
171
+ const ixs = await buildDepositWithLeverageIxns(kaminoMarket, debtReserve, collReserve, owner, initialInputs.obligation, referrer, currentSlot, depositTokenIsSol, scopeFeed, initialInputs.calcs, budgetAndPriorityFeeIxs, {
172
+ preActionIxs: [],
173
+ swapIxs: swapIxs,
174
+ lookupTables: lookupTables,
175
+ }, initialInputs.strategy, initialInputs.collIsKtoken);
176
+ return {
177
+ ixs,
178
+ lookupTables,
179
+ swapInputs,
180
+ initialInputs,
181
+ };
182
+ }
183
+ async function buildDepositWithLeverageIxns(market, debtReserve, collReserve, owner, obligation, referrer, currentSlot, depositTokenIsSol, scopeFeed, calcs, budgetAndPriorityFeeIxs, swapQuoteIxs, strategy, collIsKtoken) {
184
+ const budgetIxns = budgetAndPriorityFeeIxs || (0, utils_1.getComputeBudgetAndPriorityFeeIxns)(3000000);
185
+ const collTokenMint = collReserve.getLiquidityMint();
186
+ const debtTokenMint = debtReserve.getLiquidityMint();
187
+ const collTokenAta = (0, spl_token_1.getAssociatedTokenAddressSync)(collTokenMint, owner);
188
+ const debtTokenAta = (0, spl_token_1.getAssociatedTokenAddressSync)(debtTokenMint, owner);
186
189
  // 1. Create atas & budget txns
187
190
  let mintsToCreateAtas;
188
191
  if (collIsKtoken) {
189
192
  const secondTokenAta = strategy.strategy.tokenAMint.equals(debtTokenMint)
190
193
  ? strategy.strategy.tokenBMint
191
194
  : strategy.strategy.tokenAMint;
192
- const secondTokenTokenProgarm = strategy?.strategy.tokenAMint.equals(debtTokenMint)
195
+ const secondTokenTokenProgarm = strategy.strategy.tokenAMint.equals(debtTokenMint)
193
196
  ? strategy.strategy.tokenBTokenProgram.equals(web3_js_1.PublicKey.default)
194
197
  ? spl_token_1.TOKEN_PROGRAM_ID
195
198
  : strategy.strategy.tokenBTokenProgram
@@ -231,269 +234,183 @@ const getDepositWithLeverageIxns = async (props) => {
231
234
  },
232
235
  ];
233
236
  }
234
- const budgetIxns = budgetAndPriorityFeeIxns || (0, utils_1.getComputeBudgetAndPriorityFeeIxns)(3000000);
235
- const { atas: [collTokenAta, debtTokenAta], createAtaIxs, } = await (0, utils_1.getAtasWithCreateIxnsIfMissing)(connection, user, mintsToCreateAtas);
236
- // TODO: this needs to work the other way around also
237
- // TODO: marius test this with shorting leverage and with leverage looping
237
+ const atasAndCreateIxns = (0, utils_1.createAtasIdempotent)(owner, mintsToCreateAtas);
238
238
  const fillWsolAtaIxns = [];
239
239
  if (depositTokenIsSol) {
240
- fillWsolAtaIxns.push(...(0, utils_1.getDepositWsolIxns)(user, selectedTokenIsCollToken ? collTokenAta : debtTokenAta, (0, classes_2.numberToLamportsDecimal)(calcs.initDepositInSol, solTokenReserve.stats.decimals).ceil()));
240
+ fillWsolAtaIxns.push(...(0, utils_1.getDepositWsolIxns)(owner, (0, spl_token_1.getAssociatedTokenAddressSync)(utils_1.WRAPPED_SOL_MINT, owner), (0, classes_2.numberToLamportsDecimal)(calcs.initDepositInSol, utils_1.SOL_DECIMALS).ceil()));
241
241
  }
242
- // 1. Flash borrow & repay the collateral amount needed for given leverage
242
+ // 2. Flash borrow & repay the collateral amount needed for given leverage
243
243
  // if user deposits coll, then we borrow the diff, else we borrow the entire amount
244
244
  const { flashBorrowIxn, flashRepayIxn } = (0, instructions_1.getFlashLoanInstructions)({
245
- borrowIxnIndex: budgetIxns.length + createAtaIxs.length + fillWsolAtaIxns.length,
246
- walletPublicKey: user,
247
- lendingMarketAuthority: kaminoMarket.getLendingMarketAuthority(),
248
- lendingMarketAddress: kaminoMarket.getAddress(),
245
+ borrowIxnIndex: budgetIxns.length + atasAndCreateIxns.length + fillWsolAtaIxns.length,
246
+ walletPublicKey: owner,
247
+ lendingMarketAuthority: market.getLendingMarketAuthority(),
248
+ lendingMarketAddress: market.getAddress(),
249
249
  reserve: !collIsKtoken ? collReserve : debtReserve,
250
- amountLamports: (0, classes_2.numberToLamportsDecimal)(!collIsKtoken ? calcs.flashBorrowInCollToken : calcsKtoken.flashBorrowInDebtToken, !collIsKtoken ? collReserve.stats.decimals : debtReserve.stats.decimals),
250
+ amountLamports: (0, classes_2.numberToLamportsDecimal)(!collIsKtoken ? calcs.flashBorrowInCollToken : calcs.flashBorrowInDebtTokenKtokenOnly, !collIsKtoken ? collReserve.stats.decimals : debtReserve.stats.decimals),
251
251
  destinationAta: !collIsKtoken ? collTokenAta : debtTokenAta,
252
- referrerAccount: kaminoMarket.programId,
253
- referrerTokenState: kaminoMarket.programId,
254
- programId: kaminoMarket.programId,
252
+ referrerAccount: market.programId,
253
+ referrerTokenState: market.programId,
254
+ programId: market.programId,
255
255
  });
256
- console.log('Borrowing: ', (0, classes_2.numberToLamportsDecimal)(!collIsKtoken ? calcs.debtTokenToBorrow : calcsKtoken.debtTokenToBorrow, debtReserve.stats.decimals)
257
- .ceil()
258
- .toString());
259
256
  // 3. Deposit initial tokens + borrowed tokens into reserve
260
- let obligationType;
261
- if (obligationTypeTagOverride === utils_1.ObligationTypeTag.Multiply) {
262
- // multiply
263
- obligationType = new utils_1.MultiplyObligation(collTokenMint, debtTokenMint, kaminoMarket.programId);
264
- }
265
- else if (obligationTypeTagOverride === utils_1.ObligationTypeTag.Leverage) {
266
- // leverage
267
- obligationType = new utils_1.LeverageObligation(collTokenMint, debtTokenMint, kaminoMarket.programId);
268
- }
269
- else {
270
- throw Error('Obligation type tag not supported for leverage, please use 1 - multiply or 3 - leverage');
271
- }
272
257
  const scopeRefresh = scopeFeed ? { includeScopeRefresh: true, scopeFeed: scopeFeed } : undefined;
273
- const kaminoDepositAndBorrowAction = await classes_1.KaminoAction.buildDepositAndBorrowTxns(kaminoMarket, (0, classes_2.numberToLamportsDecimal)(!collIsKtoken ? calcs.collTokenToDeposit : calcsKtoken.collTokenToDeposit, collReserve.stats.decimals)
258
+ const kaminoDepositAndBorrowAction = await classes_1.KaminoAction.buildDepositAndBorrowTxns(market, (0, classes_2.numberToLamportsDecimal)(!collIsKtoken ? calcs.collTokenToDeposit : calcs.collTokenToDeposit, collReserve.stats.decimals)
274
259
  .floor()
275
- .toString(), collTokenMint, (0, classes_2.numberToLamportsDecimal)(!collIsKtoken ? calcs.debtTokenToBorrow : calcsKtoken.debtTokenToBorrow, debtReserve.stats.decimals)
260
+ .toString(), collTokenMint, (0, classes_2.numberToLamportsDecimal)(!collIsKtoken ? calcs.debtTokenToBorrow : calcs.debtTokenToBorrow, debtReserve.stats.decimals)
276
261
  .ceil()
277
- .toString(), debtTokenMint, user, obligation ? obligation : obligationType, 0, false, true, // emode
262
+ .toString(), debtTokenMint, owner, obligation, 0, false, true, // emode
278
263
  false, // to be checked and created in a setup tx in the UI
279
264
  referrer, currentSlot, scopeRefresh);
280
- console.log('Expected to swap in', !collIsKtoken ? calcs.swapDebtTokenIn.toNumber().toString() : calcsKtoken.singleSidedDeposit.toNumber().toString(), 'debt for', !collIsKtoken ? calcs.swapCollTokenExpectedOut.toString() : calcsKtoken.requiredCollateral.toNumber().toString(), 'coll');
281
- const ixns = [
282
- ...budgetIxns,
283
- ...createAtaIxs,
284
- ...fillWsolAtaIxns,
285
- ...[flashBorrowIxn],
286
- ...kaminoDepositAndBorrowAction.setupIxs,
287
- ...[kaminoDepositAndBorrowAction.lendingIxs[0]],
288
- ...kaminoDepositAndBorrowAction.inBetweenIxs,
289
- ...[kaminoDepositAndBorrowAction.lendingIxs[1]],
290
- ...kaminoDepositAndBorrowAction.cleanupIxs,
291
- ...[flashRepayIxn],
292
- ];
293
- const uniqueAccounts = new utils_1.PublicKeySet([]);
294
- ixns.forEach((ixn) => {
295
- ixn.keys.forEach((key) => {
296
- uniqueAccounts.add(key.pubkey);
297
- });
298
- });
299
- const totalKlendAccounts = uniqueAccounts.toArray().length;
300
- // return early to avoid extra swapper calls
301
- if (getTotalKlendAccountsOnly) {
302
- return {
303
- ixns: [],
304
- lookupTablesAddresses: [],
305
- swapInputs: {
306
- inputAmountLamports: new decimal_js_1.default('0'),
307
- inputMint: web3_js_1.PublicKey.default,
308
- outputMint: web3_js_1.PublicKey.default,
309
- },
310
- totalKlendAccounts: totalKlendAccounts,
311
- };
312
- }
313
- let depositSwapper;
314
- let expectedDebtTokenAtaBalance = new decimal_js_1.default(0); // only needed for kTokens
265
+ // 4. Swap
266
+ const { swapIxs } = swapQuoteIxs;
267
+ const swapInstructions = (0, utils_1.removeBudgetAndAtaIxns)(swapIxs, []);
315
268
  if (!collIsKtoken) {
316
- depositSwapper = swapper;
317
- }
318
- else {
319
- if (kamino === undefined) {
320
- throw Error('Ktoken use as collateral for leverage without Kamino instance');
321
- }
322
- depositSwapper = await (0, utils_2.getTokenToKtokenSwapper)(connection, kaminoMarket, kamino, user, swapper, priceAinB, false);
323
- let futureBalanceInAta = new decimal_js_1.default(0);
324
- if (debtTokenMint.equals(utils_1.WRAPPED_SOL_MINT)) {
325
- futureBalanceInAta = futureBalanceInAta.add(!collIsKtoken ? calcs.initDepositInSol : calcsKtoken.initDepositInSol);
326
- }
327
- futureBalanceInAta = futureBalanceInAta.add(!collIsKtoken ? calcs.debtTokenToBorrow : calcsKtoken.flashBorrowInDebtToken);
328
- expectedDebtTokenAtaBalance = await (0, utils_2.getExpectedTokenBalanceAfterBorrow)(connection, debtTokenMint, user, (0, classes_2.numberToLamportsDecimal)(futureBalanceInAta.toDecimalPlaces(debtReserve.stats.decimals), debtReserve.stats.decimals), debtReserve.state.liquidity.mintDecimals.toNumber());
329
- }
330
- const swapInputs = {
331
- inputAmountLamports: (0, classes_2.numberToLamportsDecimal)(!collIsKtoken ? calcs.swapDebtTokenIn : calcsKtoken.singleSidedDeposit, debtReserve.stats.decimals).ceil(),
332
- inputMint: debtTokenMint,
333
- outputMint: collTokenMint,
334
- };
335
- const [swapIxns, lookupTablesAddresses] = await depositSwapper(swapInputs.inputAmountLamports.toNumber(), swapInputs.inputMint, swapInputs.outputMint, slippagePct.toNumber(), expectedDebtTokenAtaBalance);
336
- if (collIsKtoken) {
337
- if (strategy?.strategy.strategyLookupTable) {
338
- lookupTablesAddresses.push(strategy?.strategy.strategyLookupTable);
339
- }
340
- else {
341
- console.log('Strategy lookup table not found');
342
- }
343
- }
344
- const swapInstructions = (0, utils_1.removeBudgetAndAtaIxns)(swapIxns, []);
345
- if (!collIsKtoken) {
346
- return {
347
- ixns: [
348
- ...budgetIxns,
349
- ...createAtaIxs,
350
- ...fillWsolAtaIxns,
351
- ...[flashBorrowIxn],
352
- ...kaminoDepositAndBorrowAction.setupIxs,
353
- ...[kaminoDepositAndBorrowAction.lendingIxs[0]],
354
- ...kaminoDepositAndBorrowAction.inBetweenIxs,
355
- ...[kaminoDepositAndBorrowAction.lendingIxs[1]],
356
- ...kaminoDepositAndBorrowAction.cleanupIxs,
357
- ...swapInstructions,
358
- ...[flashRepayIxn],
359
- ],
360
- lookupTablesAddresses,
361
- swapInputs,
362
- totalKlendAccounts: totalKlendAccounts,
363
- };
269
+ return [
270
+ ...budgetIxns,
271
+ ...atasAndCreateIxns.map((x) => x.createAtaIx),
272
+ ...fillWsolAtaIxns,
273
+ ...[flashBorrowIxn],
274
+ ...kaminoDepositAndBorrowAction.setupIxs,
275
+ ...[kaminoDepositAndBorrowAction.lendingIxs[0]],
276
+ ...kaminoDepositAndBorrowAction.inBetweenIxs,
277
+ ...[kaminoDepositAndBorrowAction.lendingIxs[1]],
278
+ ...kaminoDepositAndBorrowAction.cleanupIxs,
279
+ ...swapInstructions,
280
+ ...[flashRepayIxn],
281
+ ];
364
282
  }
365
283
  else {
366
- return {
367
- ixns: [
368
- ...budgetIxns,
369
- ...createAtaIxs,
370
- ...fillWsolAtaIxns,
371
- ...[flashBorrowIxn],
372
- ...swapInstructions,
373
- ...kaminoDepositAndBorrowAction.setupIxs,
374
- ...[kaminoDepositAndBorrowAction.lendingIxs[0]],
375
- ...kaminoDepositAndBorrowAction.inBetweenIxs,
376
- ...[kaminoDepositAndBorrowAction.lendingIxs[1]],
377
- ...kaminoDepositAndBorrowAction.cleanupIxs,
378
- ...[flashRepayIxn],
379
- ],
380
- lookupTablesAddresses,
381
- swapInputs,
382
- totalKlendAccounts: totalKlendAccounts,
383
- };
284
+ return [
285
+ ...budgetIxns,
286
+ ...atasAndCreateIxns.map((x) => x.createAtaIx),
287
+ ...fillWsolAtaIxns,
288
+ ...[flashBorrowIxn],
289
+ ...swapInstructions,
290
+ ...kaminoDepositAndBorrowAction.setupIxs,
291
+ ...[kaminoDepositAndBorrowAction.lendingIxs[0]],
292
+ ...kaminoDepositAndBorrowAction.inBetweenIxs,
293
+ ...[kaminoDepositAndBorrowAction.lendingIxs[1]],
294
+ ...kaminoDepositAndBorrowAction.cleanupIxs,
295
+ ...[flashRepayIxn],
296
+ ];
384
297
  }
385
- };
386
- exports.getDepositWithLeverageIxns = getDepositWithLeverageIxns;
387
- const getWithdrawWithLeverageSwapInputs = (props) => {
388
- const { amount, deposited, borrowed, priceCollToDebt, slippagePct, isClosingPosition, kaminoMarket, selectedTokenMint, debtTokenMint, collTokenMint, userObligation, currentSlot, } = props;
298
+ }
299
+ async function getWithdrawWithLeverageSwapInputs({ owner, kaminoMarket, debtTokenMint, collTokenMint, deposited, borrowed, obligation, referrer, currentSlot, withdrawAmount, priceCollToDebt, slippagePct, isClosingPosition, selectedTokenMint, budgetAndPriorityFeeIxs, kamino, scopeFeed, quoteBufferBps, isKtoken, quoter, }) {
389
300
  const collReserve = kaminoMarket.getReserveByMint(collTokenMint);
390
301
  const debtReserve = kaminoMarket.getReserveByMint(debtTokenMint);
391
- const flashLoanFee = debtReserve?.getFlashLoanFee() || new decimal_js_1.default(0);
302
+ const flashLoanFee = debtReserve.getFlashLoanFee() || new decimal_js_1.default(0);
392
303
  const selectedTokenIsCollToken = selectedTokenMint.equals(collTokenMint);
393
- const { adjustDepositPosition: withdrawAmount, adjustBorrowPosition: initialRepayAmount } = isClosingPosition
394
- ? { adjustDepositPosition: deposited, adjustBorrowPosition: borrowed }
395
- : (0, calcs_1.calcWithdrawAmounts)({
396
- collTokenMint: collTokenMint,
397
- priceCollToDebt: new decimal_js_1.default(priceCollToDebt),
398
- currentDepositPosition: deposited,
399
- currentBorrowPosition: borrowed,
400
- withdrawAmount: new decimal_js_1.default(amount),
401
- selectedTokenMint: selectedTokenMint,
402
- });
403
- const irSlippageBpsForDebt = userObligation
404
- .estimateObligationInterestRate(kaminoMarket, debtReserve, userObligation.state.borrows[0], currentSlot)
405
- .toDecimalPlaces(debtReserve?.state.liquidity.mintDecimals.toNumber(), decimal_js_1.default.ROUND_CEIL);
406
- const repayAmount = initialRepayAmount
407
- .mul(irSlippageBpsForDebt.add('0.1').div('10_000').add('1'))
408
- .toDecimalPlaces(debtReserve?.state.liquidity.mintDecimals.toNumber(), decimal_js_1.default.ROUND_CEIL);
409
- const swapAmountIfWithdrawingColl = repayAmount
410
- .mul(new decimal_js_1.default(1).plus(flashLoanFee))
411
- .mul(new decimal_js_1.default(1 + slippagePct / 100))
412
- .div(priceCollToDebt);
413
- const swapAmountIfWithdrawingDebt = withdrawAmount;
414
- const collTokenSwapIn = selectedTokenIsCollToken ? swapAmountIfWithdrawingColl : swapAmountIfWithdrawingDebt;
304
+ const collIsKtoken = await isKtoken(collTokenMint);
305
+ const strategy = collIsKtoken ? (await kamino.getStrategyByKTokenMint(collTokenMint)) : undefined;
306
+ const solTokenReserve = kaminoMarket.getReserveByMint(utils_1.WRAPPED_SOL_MINT);
307
+ const depositTokenIsSol = !solTokenReserve ? false : selectedTokenMint.equals(solTokenReserve.getLiquidityMint());
308
+ const calcs = (0, calcs_1.withdrawLeverageCalcs)(kaminoMarket, collReserve, debtReserve, priceCollToDebt, withdrawAmount, deposited, borrowed, currentSlot, isClosingPosition, selectedTokenIsCollToken, selectedTokenMint, obligation, flashLoanFee, slippagePct);
309
+ const klendIxs = await buildWithdrawWithLeverageIxns(kaminoMarket, debtReserve, collReserve, owner, obligation, referrer, currentSlot, isClosingPosition, depositTokenIsSol, scopeFeed, calcs, budgetAndPriorityFeeIxs, {
310
+ preActionIxs: [],
311
+ swapIxs: [],
312
+ lookupTables: [],
313
+ }, strategy, collIsKtoken);
314
+ const uniqueKlendAccounts = (0, utils_1.uniqueAccounts)(klendIxs);
315
+ const swapInputAmount = (0, classes_2.numberToLamportsDecimal)(calcs.collTokenSwapIn, collReserve.state.liquidity.mintDecimals.toNumber()).ceil();
316
+ const swapInputsForQuote = {
317
+ inputAmountLamports: swapInputAmount.mul(new decimal_js_1.default(1).add(quoteBufferBps.div(CreationParameters_1.FullBPS))),
318
+ inputMint: collTokenMint,
319
+ outputMint: debtTokenMint,
320
+ amountDebtAtaBalance: new decimal_js_1.default(0), // Only needed for ktokens deposits
321
+ };
322
+ const swapQuote = await quoter(swapInputsForQuote, uniqueKlendAccounts);
323
+ const calcsQuotePrice = (0, calcs_1.withdrawLeverageCalcs)(kaminoMarket, collReserve, debtReserve, collIsKtoken ? swapQuote.priceAInB : priceCollToDebt, withdrawAmount, deposited, borrowed, currentSlot, isClosingPosition, selectedTokenIsCollToken, selectedTokenMint, obligation, flashLoanFee, slippagePct);
324
+ const swapInputAmountQuotePrice = (0, classes_2.numberToLamportsDecimal)(calcsQuotePrice.collTokenSwapIn, collReserve.state.liquidity.mintDecimals.toNumber()).ceil();
415
325
  return {
416
326
  swapInputs: {
417
- inputAmountLamports: (0, classes_2.numberToLamportsDecimal)(collTokenSwapIn, collReserve.state.liquidity.mintDecimals.toNumber()).ceil(),
327
+ inputAmountLamports: swapInputAmountQuotePrice,
418
328
  inputMint: collTokenMint,
419
329
  outputMint: debtTokenMint,
330
+ amountDebtAtaBalance: new decimal_js_1.default(0), // Only needed for ktokens deposits
331
+ },
332
+ initialInputs: {
333
+ calcs: calcsQuotePrice,
334
+ swapQuote,
335
+ currentSlot,
336
+ collIsKtoken,
337
+ strategy,
338
+ obligation,
339
+ klendAccounts: uniqueKlendAccounts,
420
340
  },
421
341
  };
422
- };
423
- exports.getWithdrawWithLeverageSwapInputs = getWithdrawWithLeverageSwapInputs;
424
- const getWithdrawWithLeverageIxns = async (props) => {
425
- const { connection, budgetAndPriorityFeeIxns, user, amount, deposited, borrowed, collTokenMint, debtTokenMint, priceCollToDebt, selectedTokenMint, isClosingPosition, kaminoMarket, slippagePct, swapper, referrer, isKtoken, kamino, obligationTypeTagOverride, obligation, currentSlot, getTotalKlendAccountsOnly, scopeFeed, } = props;
342
+ }
343
+ async function getWithdrawWithLeverageIxns({ owner, kaminoMarket, debtTokenMint, collTokenMint, obligation, deposited, borrowed, referrer, currentSlot, withdrawAmount, priceCollToDebt, slippagePct, isClosingPosition, selectedTokenMint, budgetAndPriorityFeeIxs, kamino, scopeFeed, quoteBufferBps, isKtoken, quoter, swapper, }) {
426
344
  const collReserve = kaminoMarket.getReserveByMint(collTokenMint);
427
345
  const debtReserve = kaminoMarket.getReserveByMint(debtTokenMint);
428
- const flashLoanFee = debtReserve?.getFlashLoanFee() || new decimal_js_1.default(0);
429
- const collIsKtoken = await isKtoken(collTokenMint);
430
346
  const solTokenReserve = kaminoMarket.getReserveByMint(utils_1.WRAPPED_SOL_MINT);
431
- const selectedTokenIsCollToken = selectedTokenMint.equals(collTokenMint);
432
347
  const depositTokenIsSol = !solTokenReserve ? false : selectedTokenMint.equals(solTokenReserve.getLiquidityMint());
433
- let obligationType;
434
- if (obligationTypeTagOverride == utils_1.ObligationTypeTag.Multiply) {
435
- // multiply
436
- obligationType = new utils_1.MultiplyObligation(collTokenMint, debtTokenMint, kaminoMarket.programId);
437
- }
438
- else if (obligationTypeTagOverride == utils_1.ObligationTypeTag.Leverage) {
439
- // leverage
440
- obligationType = new utils_1.LeverageObligation(collTokenMint, debtTokenMint, kaminoMarket.programId);
348
+ const { swapInputs, initialInputs } = await getWithdrawWithLeverageSwapInputs({
349
+ owner,
350
+ kaminoMarket,
351
+ debtTokenMint,
352
+ collTokenMint,
353
+ deposited,
354
+ borrowed,
355
+ obligation,
356
+ referrer,
357
+ currentSlot,
358
+ withdrawAmount,
359
+ priceCollToDebt,
360
+ slippagePct,
361
+ isClosingPosition,
362
+ selectedTokenMint,
363
+ budgetAndPriorityFeeIxs,
364
+ kamino,
365
+ scopeFeed,
366
+ quoteBufferBps,
367
+ isKtoken,
368
+ quoter,
369
+ });
370
+ let withdrawSwapper;
371
+ if (initialInputs.collIsKtoken) {
372
+ if (kamino === undefined) {
373
+ throw Error('Ktoken use as collateral for leverage without Kamino instance');
374
+ }
375
+ withdrawSwapper = await (0, utils_2.getKtokenToTokenSwapper)(kaminoMarket, kamino, owner, swapper);
441
376
  }
442
377
  else {
443
- throw Error(`Obligation type tag ${obligationTypeTagOverride} not supported for leverage, please use multiply (1) or leverage (3) obligation type`);
378
+ withdrawSwapper = swapper;
444
379
  }
445
- // 1. Calculate coll_amount and debt_amount to repay such that we maintain leverage and we withdraw to
446
- // the wallet `amountInDepositTokenToWithdrawToWallet` amount of collateral token
447
- // We need to withdraw withdrawAmountInDepositToken coll tokens
448
- // and repay repayAmountInBorrowToken debt tokens
449
- // TODO marius: do the same in useDeposit
450
- const { adjustDepositPosition: withdrawAmount, adjustBorrowPosition: initialRepayAmount } = isClosingPosition
451
- ? { adjustDepositPosition: deposited, adjustBorrowPosition: borrowed }
452
- : (0, calcs_1.calcWithdrawAmounts)({
453
- collTokenMint: collTokenMint,
454
- priceCollToDebt: new decimal_js_1.default(priceCollToDebt),
455
- currentDepositPosition: deposited,
456
- currentBorrowPosition: borrowed,
457
- withdrawAmount: new decimal_js_1.default(amount),
458
- selectedTokenMint: selectedTokenMint,
459
- });
460
- // Add slippage for the accrued interest rate amount
461
- const userObligation = obligation
462
- ? obligation
463
- : await kaminoMarket.getObligationByAddress(obligationType.toPda(kaminoMarket.getAddress(), user));
464
- const irSlippageBpsForDebt = userObligation
465
- .estimateObligationInterestRate(kaminoMarket, debtReserve, userObligation?.state.borrows[0], currentSlot)
466
- .toDecimalPlaces(debtReserve?.state.liquidity.mintDecimals.toNumber(), decimal_js_1.default.ROUND_CEIL);
467
- // add 0.1 to irSlippageBpsForDebt because we don't want to estimate slightly less than SC and end up not reapying enough
468
- const repayAmount = initialRepayAmount
469
- .mul(irSlippageBpsForDebt.add('0.1').div('10_000').add('1'))
470
- .toDecimalPlaces(debtReserve?.state.liquidity.mintDecimals.toNumber(), decimal_js_1.default.ROUND_CEIL);
471
- // 6. Get swap ixns
472
- // 5. Get swap estimations to understand how much we need to borrow from borrow reserve
473
- // prevent withdrawing more then deposited if we close position
474
- const depositTokenWithdrawAmount = !isClosingPosition
475
- ? withdrawAmount.mul(new decimal_js_1.default(1).plus(flashLoanFee))
476
- : withdrawAmount;
477
- // We are swapping debt token
478
- // When withdrawing coll, it means we just need to swap enough to pay for the flash borrow
479
- const swapAmountIfWithdrawingColl = repayAmount
480
- .mul(new decimal_js_1.default(1).plus(flashLoanFee))
481
- .mul(new decimal_js_1.default(1 + slippagePct / 100))
482
- .div(priceCollToDebt);
483
- // When withdrawing debt, it means we need to swap just the collateral we are withdrwaing
484
- // enough to cover the debt we are repaying, leaving the remaining in the wallet
485
- const swapAmountIfWithdrawingDebt = withdrawAmount;
486
- const collTokenSwapIn = selectedTokenIsCollToken ? swapAmountIfWithdrawingColl : swapAmountIfWithdrawingDebt;
487
- const debtTokenExpectedSwapOut = collTokenSwapIn.mul(priceCollToDebt).div(new decimal_js_1.default(1 + slippagePct / 100));
488
- const strategy = collIsKtoken ? await kamino?.getStrategyByKTokenMint(collTokenMint) : undefined;
489
- console.log('Expecting to swap', collTokenSwapIn.toString(), 'coll for', debtTokenExpectedSwapOut.toString(), 'debt');
380
+ const { swapIxs, lookupTables } = await withdrawSwapper(swapInputs, initialInputs.klendAccounts, initialInputs.swapQuote);
381
+ if (initialInputs.collIsKtoken) {
382
+ if (initialInputs.strategy.strategy.strategyLookupTable) {
383
+ const strategyLut = await (0, utils_1.getLookupTableAccount)(kaminoMarket.getConnection(), initialInputs.strategy.strategy.strategyLookupTable);
384
+ lookupTables.push(strategyLut);
385
+ }
386
+ else {
387
+ console.log('Strategy lookup table not found');
388
+ }
389
+ }
390
+ const ixs = await buildWithdrawWithLeverageIxns(kaminoMarket, debtReserve, collReserve, owner, obligation, referrer, currentSlot, isClosingPosition, depositTokenIsSol, scopeFeed, initialInputs.calcs, budgetAndPriorityFeeIxs, {
391
+ preActionIxs: [],
392
+ swapIxs,
393
+ lookupTables,
394
+ }, initialInputs.strategy, initialInputs.collIsKtoken);
395
+ // Send ixns and lookup tables
396
+ return {
397
+ ixs,
398
+ lookupTables,
399
+ swapInputs,
400
+ initialInputs: initialInputs,
401
+ };
402
+ }
403
+ async function buildWithdrawWithLeverageIxns(market, debtReserve, collReserve, owner, obligation, referrer, currentSlot, isClosingPosition, depositTokenIsSol, scopeFeed, calcs, budgetAndPriorityFeeIxs, swapQuoteIxs, strategy, collIsKtoken) {
404
+ const collTokenMint = collReserve.getLiquidityMint();
405
+ const debtTokenMint = debtReserve.getLiquidityMint();
406
+ const debtTokenAta = (0, spl_token_1.getAssociatedTokenAddressSync)(debtTokenMint, owner);
490
407
  // 1. Create atas & budget txns & user metadata
491
408
  let mintsToCreateAtas;
492
409
  if (collIsKtoken) {
493
410
  const secondTokenAta = strategy.strategy.tokenAMint.equals(debtTokenMint)
494
411
  ? strategy.strategy.tokenBMint
495
412
  : strategy.strategy.tokenAMint;
496
- const secondTokenTokenProgram = strategy?.strategy.tokenAMint.equals(debtTokenMint)
413
+ const secondTokenTokenProgram = strategy.strategy.tokenAMint.equals(debtTokenMint)
497
414
  ? strategy.strategy.tokenBTokenProgram.equals(web3_js_1.PublicKey.default)
498
415
  ? spl_token_1.TOKEN_PROGRAM_ID
499
416
  : strategy.strategy.tokenBTokenProgram
@@ -535,96 +452,45 @@ const getWithdrawWithLeverageIxns = async (props) => {
535
452
  },
536
453
  ];
537
454
  }
538
- const { atas: [, debtTokenAta], createAtaIxs, } = await (0, utils_1.getAtasWithCreateIxnsIfMissing)(connection, user, mintsToCreateAtas);
455
+ const atasAndCreateIxns = (0, utils_1.createAtasIdempotent)(owner, mintsToCreateAtas);
539
456
  const closeWsolAtaIxns = [];
540
457
  if (depositTokenIsSol || debtTokenMint.equals(utils_1.WRAPPED_SOL_MINT)) {
541
- const wsolAta = (0, utils_1.getAssociatedTokenAddress)(utils_1.WRAPPED_SOL_MINT, user, false);
542
- closeWsolAtaIxns.push((0, spl_token_1.createCloseAccountInstruction)(wsolAta, user, user, [], spl_token_1.TOKEN_PROGRAM_ID));
458
+ const wsolAta = (0, utils_1.getAssociatedTokenAddress)(utils_1.WRAPPED_SOL_MINT, owner, false);
459
+ closeWsolAtaIxns.push((0, spl_token_1.createCloseAccountInstruction)(wsolAta, owner, owner, [], spl_token_1.TOKEN_PROGRAM_ID));
543
460
  }
544
- const budgetIxns = budgetAndPriorityFeeIxns || (0, utils_1.getComputeBudgetAndPriorityFeeIxns)(3000000);
545
- // TODO: marius test this with shorting leverage and with leverage looping
461
+ const budgetIxns = budgetAndPriorityFeeIxs || (0, utils_1.getComputeBudgetAndPriorityFeeIxns)(3000000);
462
+ // TODO: Might be worth removing as it's only needed for Ktokens
546
463
  // This is here so that we have enough wsol to repay in case the kAB swapped to sol after estimates is not enough
547
464
  const fillWsolAtaIxns = [];
548
465
  if (debtTokenMint.equals(utils_1.WRAPPED_SOL_MINT)) {
549
- const halfSolBalance = (await connection.getBalance(user)) / web3_js_1.LAMPORTS_PER_SOL / 2;
466
+ const halfSolBalance = (await market.getConnection().getBalance(owner)) / web3_js_1.LAMPORTS_PER_SOL / 2;
550
467
  const balanceToWrap = halfSolBalance < 0.1 ? halfSolBalance : 0.1;
551
- fillWsolAtaIxns.push(...(0, utils_1.getDepositWsolIxns)(user, debtTokenAta, (0, classes_2.numberToLamportsDecimal)(balanceToWrap, solTokenReserve.stats.decimals).ceil()));
468
+ fillWsolAtaIxns.push(...(0, utils_1.getDepositWsolIxns)(owner, (0, spl_token_1.getAssociatedTokenAddressSync)(utils_1.WRAPPED_SOL_MINT, owner), (0, classes_2.numberToLamportsDecimal)(balanceToWrap, utils_1.SOL_DECIMALS).ceil()));
552
469
  }
553
470
  // 2. Prepare the flash borrow and flash repay amounts and ixns
554
471
  // We borrow exactly how much we need to repay
555
472
  // and repay that + flash amount fee
556
473
  const { flashBorrowIxn, flashRepayIxn } = (0, instructions_1.getFlashLoanInstructions)({
557
- borrowIxnIndex: budgetIxns.length + createAtaIxs.length + fillWsolAtaIxns.length,
558
- walletPublicKey: user,
559
- lendingMarketAuthority: kaminoMarket.getLendingMarketAuthority(),
560
- lendingMarketAddress: kaminoMarket.getAddress(),
474
+ borrowIxnIndex: budgetIxns.length + atasAndCreateIxns.length + fillWsolAtaIxns.length,
475
+ walletPublicKey: owner,
476
+ lendingMarketAuthority: market.getLendingMarketAuthority(),
477
+ lendingMarketAddress: market.getAddress(),
561
478
  reserve: debtReserve,
562
- amountLamports: (0, classes_2.numberToLamportsDecimal)(repayAmount, debtReserve.stats.decimals),
479
+ amountLamports: (0, classes_2.numberToLamportsDecimal)(calcs.repayAmount, debtReserve.stats.decimals),
563
480
  destinationAta: debtTokenAta,
564
- referrerAccount: kaminoMarket.programId,
565
- referrerTokenState: kaminoMarket.programId,
566
- programId: kaminoMarket.programId,
481
+ referrerAccount: market.programId,
482
+ referrerTokenState: market.programId,
483
+ programId: market.programId,
567
484
  });
568
485
  // 6. Repay borrowed tokens and Withdraw tokens from reserve that will be swapped to repay flash loan
569
- const repayAndWithdrawAction = await classes_1.KaminoAction.buildRepayAndWithdrawTxns(kaminoMarket, isClosingPosition ? utils_1.U64_MAX : (0, classes_2.numberToLamportsDecimal)(repayAmount, debtReserve.stats.decimals).floor().toString(), debtTokenMint, isClosingPosition ? utils_1.U64_MAX : (0, classes_2.numberToLamportsDecimal)(depositTokenWithdrawAmount, collReserve.stats.decimals).ceil().toString(), collTokenMint, user, currentSlot, userObligation ? userObligation : obligationType, 0, false, false, false, // 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)
486
+ 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
487
+ ? utils_1.U64_MAX
488
+ : (0, classes_2.numberToLamportsDecimal)(calcs.depositTokenWithdrawAmount, collReserve.stats.decimals).ceil().toString(), collTokenMint, owner, currentSlot, obligation, 0, false, false, false, // 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)
570
489
  isClosingPosition, referrer, { includeScopeRefresh: true, scopeFeed: scopeFeed });
571
- const klendIxns = [
490
+ const swapInstructions = (0, utils_1.removeBudgetAndAtaIxns)(swapQuoteIxs.swapIxs, []);
491
+ return [
572
492
  ...budgetIxns,
573
- ...createAtaIxs,
574
- ...fillWsolAtaIxns,
575
- ...[flashBorrowIxn],
576
- ...repayAndWithdrawAction.setupIxs,
577
- ...[repayAndWithdrawAction.lendingIxs[0]],
578
- ...repayAndWithdrawAction.inBetweenIxs,
579
- ...[repayAndWithdrawAction.lendingIxs[1]],
580
- ...repayAndWithdrawAction.cleanupIxs,
581
- ...[flashRepayIxn],
582
- ...closeWsolAtaIxns,
583
- ];
584
- const uniqueAccs = (0, utils_1.uniqueAccounts)(klendIxns);
585
- const totalKlendAccounts = uniqueAccs.length;
586
- // return early to avoid extra swapper calls
587
- if (getTotalKlendAccountsOnly) {
588
- return {
589
- ixns: [],
590
- lookupTablesAddresses: [],
591
- swapInputs: {
592
- inputAmountLamports: new decimal_js_1.default('0'),
593
- inputMint: web3_js_1.PublicKey.default,
594
- outputMint: web3_js_1.PublicKey.default,
595
- },
596
- totalKlendAccounts: totalKlendAccounts,
597
- };
598
- }
599
- let withdrawSwapper;
600
- if (collIsKtoken) {
601
- if (kamino === undefined) {
602
- throw Error('Ktoken use as collateral for leverage without Kamino instance');
603
- }
604
- withdrawSwapper = await (0, utils_2.getKtokenToTokenSwapper)(kaminoMarket, kamino, user, swapper);
605
- }
606
- else {
607
- withdrawSwapper = swapper;
608
- }
609
- const swapInputs = {
610
- inputAmountLamports: (0, classes_2.numberToLamportsDecimal)(collTokenSwapIn, collReserve.stats.decimals).ceil(),
611
- inputMint: collTokenMint,
612
- outputMint: debtTokenMint,
613
- };
614
- const [swapIxns, lookupTablesAddresses] = await withdrawSwapper(swapInputs.inputAmountLamports.toNumber(), swapInputs.inputMint, swapInputs.outputMint, slippagePct);
615
- // TODO MARIUS: remove first instruction that is setBudget ixn
616
- if (collIsKtoken) {
617
- if (strategy?.strategy.strategyLookupTable) {
618
- lookupTablesAddresses.push(strategy?.strategy.strategyLookupTable);
619
- }
620
- else {
621
- console.log('Strategy lookup table not found');
622
- }
623
- }
624
- const swapInstructions = (0, utils_1.removeBudgetAndAtaIxns)(swapIxns, []);
625
- const ixns = [
626
- ...budgetIxns,
627
- ...createAtaIxs,
493
+ ...atasAndCreateIxns.map((x) => x.createAtaIx),
628
494
  ...fillWsolAtaIxns,
629
495
  ...[flashBorrowIxn],
630
496
  ...repayAndWithdrawAction.setupIxs,
@@ -636,20 +502,24 @@ const getWithdrawWithLeverageIxns = async (props) => {
636
502
  ...[flashRepayIxn],
637
503
  ...closeWsolAtaIxns,
638
504
  ];
639
- // Send ixns and lookup tables
640
- return {
641
- ixns,
642
- lookupTablesAddresses,
643
- swapInputs,
644
- totalKlendAccounts: totalKlendAccounts,
645
- };
646
- };
647
- exports.getWithdrawWithLeverageIxns = getWithdrawWithLeverageIxns;
648
- const getAdjustLeverageSwapInputs = (props) => {
649
- const { deposited, borrowed, priceCollToDebt, priceDebtToColl, slippagePct, targetLeverage, kaminoMarket, debtTokenMint, collTokenMint, } = props;
505
+ }
506
+ async function getAdjustLeverageSwapInputs({ owner, kaminoMarket, debtTokenMint, collTokenMint, obligation, depositedLamports, borrowedLamports, referrer, currentSlot, targetLeverage, priceCollToDebt, priceDebtToColl, slippagePct, budgetAndPriorityFeeIxs, kamino, scopeFeed, quoteBufferBps, isKtoken, quoter, }) {
650
507
  const collReserve = kaminoMarket.getReserveByMint(collTokenMint);
651
508
  const debtReserve = kaminoMarket.getReserveByMint(debtTokenMint);
652
- const flashLoanFee = debtReserve?.getFlashLoanFee() || new decimal_js_1.default(0);
509
+ const deposited = (0, classes_1.lamportsToNumberDecimal)(depositedLamports, collReserve.stats.decimals);
510
+ const borrowed = (0, classes_1.lamportsToNumberDecimal)(borrowedLamports, debtReserve.stats.decimals);
511
+ const collIsKtoken = await isKtoken(collTokenMint);
512
+ const strategy = collIsKtoken ? (await kamino.getStrategyByKTokenMint(collTokenMint)) : undefined;
513
+ // Getting current flash loan fee
514
+ const currentLeverage = obligation.refreshedStats.leverage;
515
+ const isDepositViaLeverage = targetLeverage.gte(new decimal_js_1.default(currentLeverage));
516
+ let flashLoanFee;
517
+ if (isDepositViaLeverage) {
518
+ flashLoanFee = collReserve.getFlashLoanFee() || new decimal_js_1.default(0);
519
+ }
520
+ else {
521
+ flashLoanFee = debtReserve.getFlashLoanFee() || new decimal_js_1.default(0);
522
+ }
653
523
  const { adjustDepositPosition, adjustBorrowPosition } = (0, calcs_1.calcAdjustAmounts)({
654
524
  currentDepositPosition: deposited,
655
525
  currentBorrowPosition: borrowed,
@@ -658,147 +528,187 @@ const getAdjustLeverageSwapInputs = (props) => {
658
528
  flashLoanFee: new decimal_js_1.default(flashLoanFee),
659
529
  });
660
530
  const isDeposit = adjustDepositPosition.gte(0) && adjustBorrowPosition.gte(0);
531
+ if (isDepositViaLeverage !== isDeposit) {
532
+ throw new Error('Invalid target leverage');
533
+ }
661
534
  if (isDeposit) {
662
- const borrowAmount = adjustDepositPosition
663
- .mul(new decimal_js_1.default(1).plus(flashLoanFee))
664
- .mul(new decimal_js_1.default(1 + slippagePct / 100))
665
- .div(priceDebtToColl);
535
+ const calcs = await (0, calcs_1.adjustDepositLeverageCalcs)(kaminoMarket, owner, debtReserve, adjustDepositPosition, adjustBorrowPosition, priceDebtToColl, flashLoanFee, slippagePct, collIsKtoken);
536
+ // Build the repay & withdraw collateral tx to get the number of accounts
537
+ const klendIxs = await buildIncreaseLeverageIxns(owner, kaminoMarket, collTokenMint, debtTokenMint, obligation, referrer, currentSlot, calcs, strategy, scopeFeed, collIsKtoken, {
538
+ preActionIxs: [],
539
+ swapIxs: [],
540
+ lookupTables: [],
541
+ }, budgetAndPriorityFeeIxs);
542
+ const uniqueKlendAccounts = (0, utils_1.uniqueAccounts)(klendIxs);
543
+ const swapInputAmount = (0, classes_2.numberToLamportsDecimal)(!collIsKtoken ? calcs.borrowAmount : calcs.amountToFlashBorrowDebt, debtReserve.state.liquidity.mintDecimals.toNumber()).ceil();
544
+ const swapInputsForQuote = {
545
+ inputAmountLamports: swapInputAmount.mul(new decimal_js_1.default(1).add(quoteBufferBps.div(CreationParameters_1.FullBPS))),
546
+ inputMint: debtTokenMint,
547
+ outputMint: collTokenMint,
548
+ amountDebtAtaBalance: new decimal_js_1.default(0), // Only needed for ktokens swaps
549
+ };
550
+ const swapQuote = await quoter(swapInputsForQuote, uniqueKlendAccounts);
551
+ const { adjustDepositPosition: adjustDepositPositionQuotePrice, adjustBorrowPosition: adjustBorrowPositionQuotePrice, } = (0, calcs_1.calcAdjustAmounts)({
552
+ currentDepositPosition: deposited,
553
+ currentBorrowPosition: borrowed,
554
+ targetLeverage: targetLeverage,
555
+ priceCollToDebt: new decimal_js_1.default(1).div(swapQuote.priceAInB),
556
+ flashLoanFee: new decimal_js_1.default(flashLoanFee),
557
+ });
558
+ const calcsQuotePrice = await (0, calcs_1.adjustDepositLeverageCalcs)(kaminoMarket, owner, debtReserve, adjustDepositPositionQuotePrice, adjustBorrowPositionQuotePrice, swapQuote.priceAInB, flashLoanFee, slippagePct, collIsKtoken);
559
+ const swapInputAmountQuotePrice = (0, classes_2.numberToLamportsDecimal)(!collIsKtoken ? calcsQuotePrice.borrowAmount : calcsQuotePrice.amountToFlashBorrowDebt, debtReserve.state.liquidity.mintDecimals.toNumber()).ceil();
560
+ let expectedDebtTokenAtaBalance = new decimal_js_1.default(0);
561
+ if (collIsKtoken) {
562
+ expectedDebtTokenAtaBalance = await (0, utils_2.getExpectedTokenBalanceAfterBorrow)(kaminoMarket.getConnection(), debtTokenMint, owner, (0, classes_2.numberToLamportsDecimal)(!collIsKtoken ? calcsQuotePrice.borrowAmount : calcsQuotePrice.amountToFlashBorrowDebt, debtReserve.stats.decimals).floor(), debtReserve.state.liquidity.mintDecimals.toNumber());
563
+ }
666
564
  return {
667
565
  swapInputs: {
668
- inputAmountLamports: (0, classes_2.numberToLamportsDecimal)(borrowAmount, debtReserve.state.liquidity.mintDecimals.toNumber()).ceil(),
566
+ inputAmountLamports: swapInputAmountQuotePrice,
669
567
  inputMint: debtTokenMint,
670
568
  outputMint: collTokenMint,
569
+ amountDebtAtaBalance: expectedDebtTokenAtaBalance,
570
+ },
571
+ initialInputs: {
572
+ calcs: calcsQuotePrice,
573
+ swapQuote,
574
+ currentSlot,
575
+ collIsKtoken,
576
+ strategy,
577
+ obligation: obligation,
578
+ klendAccounts: uniqueKlendAccounts,
579
+ isDeposit: isDeposit,
671
580
  },
672
581
  };
673
582
  }
674
583
  else {
675
- const withdrawAmountWithSlippageAndFlashLoanFee = decimal_js_1.default.abs(adjustDepositPosition)
676
- .mul(new decimal_js_1.default(1).plus(flashLoanFee))
677
- .mul(1 + slippagePct / 100);
584
+ const calcs = (0, calcs_1.adjustWithdrawLeverageCalcs)(adjustDepositPosition, adjustBorrowPosition, flashLoanFee, slippagePct);
585
+ const klendIxs = await buildDecreaseLeverageIxns(owner, kaminoMarket, collTokenMint, debtTokenMint, obligation, referrer, currentSlot, calcs, strategy, scopeFeed, collIsKtoken, {
586
+ preActionIxs: [],
587
+ swapIxs: [],
588
+ lookupTables: [],
589
+ }, budgetAndPriorityFeeIxs);
590
+ const uniqueKlendAccounts = (0, utils_1.uniqueAccounts)(klendIxs);
591
+ const swapInputAmount = (0, classes_2.numberToLamportsDecimal)(calcs.withdrawAmountWithSlippageAndFlashLoanFee, collReserve.state.liquidity.mintDecimals.toNumber()).ceil();
592
+ const swapInputsForQuote = {
593
+ inputAmountLamports: swapInputAmount.mul(new decimal_js_1.default(1).add(quoteBufferBps.div(CreationParameters_1.FullBPS))),
594
+ inputMint: collTokenMint,
595
+ outputMint: debtTokenMint,
596
+ amountDebtAtaBalance: new decimal_js_1.default(0), // Only needed for ktokens deposits
597
+ };
598
+ const swapQuote = await quoter(swapInputsForQuote, uniqueKlendAccounts);
599
+ const { adjustDepositPosition: adjustDepositPositionQuotePrice, adjustBorrowPosition: adjustBorrowPositionQuotePrice, } = (0, calcs_1.calcAdjustAmounts)({
600
+ currentDepositPosition: deposited,
601
+ currentBorrowPosition: borrowed,
602
+ targetLeverage: targetLeverage,
603
+ priceCollToDebt: swapQuote.priceAInB,
604
+ flashLoanFee: new decimal_js_1.default(flashLoanFee),
605
+ });
606
+ const calcsQuotePrice = (0, calcs_1.adjustWithdrawLeverageCalcs)(adjustDepositPositionQuotePrice, adjustBorrowPositionQuotePrice, flashLoanFee, slippagePct);
607
+ const swapInputAmountQuotePrice = (0, classes_2.numberToLamportsDecimal)(calcsQuotePrice.withdrawAmountWithSlippageAndFlashLoanFee, collReserve.state.liquidity.mintDecimals.toNumber()).ceil();
678
608
  return {
679
609
  swapInputs: {
680
- inputAmountLamports: (0, classes_2.numberToLamportsDecimal)(withdrawAmountWithSlippageAndFlashLoanFee, collReserve.state.liquidity.mintDecimals.toNumber()).ceil(),
610
+ inputAmountLamports: swapInputAmountQuotePrice,
681
611
  inputMint: collTokenMint,
682
612
  outputMint: debtTokenMint,
613
+ amountDebtAtaBalance: new decimal_js_1.default(0), // Only needed for ktokens deposits
614
+ },
615
+ initialInputs: {
616
+ calcs: calcsQuotePrice,
617
+ swapQuote,
618
+ currentSlot,
619
+ collIsKtoken,
620
+ strategy,
621
+ obligation,
622
+ klendAccounts: uniqueKlendAccounts,
623
+ isDeposit,
683
624
  },
684
625
  };
685
626
  }
686
- };
687
- exports.getAdjustLeverageSwapInputs = getAdjustLeverageSwapInputs;
688
- const getAdjustLeverageIxns = async (props) => {
689
- const { connection, budgetAndPriorityFeeIxns, user, kaminoMarket, priceDebtToColl, priceCollToDebt, targetLeverage, slippagePct, depositedLamports, borrowedLamports, collTokenMint, debtTokenMint, swapper, referrer, isKtoken, priceAinB, kamino, obligationTypeTagOverride, obligation, currentSlot, getTotalKlendAccountsOnly, scopeFeed, } = props;
690
- const collReserve = kaminoMarket.getReserveByMint(collTokenMint);
691
- const debtReserve = kaminoMarket.getReserveByMint(debtTokenMint);
692
- const deposited = (0, classes_1.lamportsToNumberDecimal)(depositedLamports, collReserve.stats.decimals);
693
- const borrowed = (0, classes_1.lamportsToNumberDecimal)(borrowedLamports, debtReserve.stats.decimals);
694
- const userObligation = obligation
695
- ? obligation
696
- : (await kaminoMarket.getUserObligationsByTag(obligationTypeTagOverride, user)).filter((obligation) => obligation.getBorrowByMint(debtReserve.getLiquidityMint()) !== undefined &&
697
- obligation.getDepositByMint(collReserve.getLiquidityMint()) !== undefined)[0];
698
- const currentLeverage = userObligation.refreshedStats.leverage;
699
- const isDepositViaLeverage = targetLeverage.gte(new decimal_js_1.default(currentLeverage));
700
- let flashLoanFee;
701
- if (isDepositViaLeverage) {
702
- flashLoanFee = collReserve.getFlashLoanFee() || new decimal_js_1.default(0);
703
- }
704
- else {
705
- flashLoanFee = debtReserve.getFlashLoanFee() || new decimal_js_1.default(0);
706
- }
707
- const { adjustDepositPosition, adjustBorrowPosition } = (0, calcs_1.calcAdjustAmounts)({
708
- currentDepositPosition: deposited,
709
- currentBorrowPosition: borrowed,
710
- targetLeverage: targetLeverage,
711
- priceCollToDebt: priceCollToDebt,
712
- flashLoanFee: new decimal_js_1.default(flashLoanFee),
627
+ }
628
+ async function getAdjustLeverageIxns({ owner, kaminoMarket, debtTokenMint, collTokenMint, obligation, depositedLamports, borrowedLamports, referrer, currentSlot, targetLeverage, priceCollToDebt, priceDebtToColl, slippagePct, budgetAndPriorityFeeIxs, kamino, scopeFeed, quoteBufferBps, priceAinB, isKtoken, quoter, swapper, }) {
629
+ const { swapInputs, initialInputs } = await getAdjustLeverageSwapInputs({
630
+ owner,
631
+ kaminoMarket,
632
+ debtTokenMint,
633
+ collTokenMint,
634
+ obligation,
635
+ depositedLamports,
636
+ borrowedLamports,
637
+ referrer,
638
+ currentSlot,
639
+ targetLeverage,
640
+ priceCollToDebt,
641
+ priceDebtToColl,
642
+ slippagePct,
643
+ budgetAndPriorityFeeIxs,
644
+ kamino,
645
+ scopeFeed,
646
+ quoteBufferBps,
647
+ priceAinB,
648
+ isKtoken,
649
+ quoter,
713
650
  });
714
- let ixns = [];
715
- let lookupTablesAddresses = [];
716
- let swapInputs;
717
- let totalKlendAccounts = 0;
718
- const isDeposit = adjustDepositPosition.gte(0) && adjustBorrowPosition.gte(0);
719
- if (isDepositViaLeverage !== isDeposit) {
720
- throw new Error('Invalid target leverage');
721
- }
722
651
  // leverage increased so we need to deposit and borrow more
723
- if (isDeposit) {
724
- console.log('Increasing leaverage');
652
+ if (initialInputs.isDeposit) {
653
+ let depositSwapper;
654
+ if (initialInputs.collIsKtoken) {
655
+ if (kamino === undefined) {
656
+ throw Error('Ktoken use as collateral for leverage without Kamino instance');
657
+ }
658
+ depositSwapper = await (0, utils_2.getTokenToKtokenSwapper)(kaminoMarket, kamino, owner, slippagePct, swapper, priceAinB, false);
659
+ }
660
+ else {
661
+ depositSwapper = swapper;
662
+ }
663
+ const { swapIxs, lookupTables } = await depositSwapper(swapInputs, initialInputs.klendAccounts, initialInputs.swapQuote);
725
664
  // TODO: marius why are we not using both adjustDepositPosition & adjustBorrowPosition
726
- const res = await (0, exports.getIncreaseLeverageIxns)({
727
- connection,
728
- budgetAndPriorityFeeIxns,
729
- user,
730
- kaminoMarket,
731
- depositAmount: adjustDepositPosition,
732
- collTokenMint,
733
- debtTokenMint,
734
- slippagePct,
735
- priceDebtToColl,
736
- priceCollToDebt,
737
- swapper,
738
- referrer,
739
- isKtoken,
740
- priceAinB,
741
- kamino,
742
- obligationTypeTagOverride,
743
- obligation: userObligation,
744
- currentSlot,
745
- getTotalKlendAccountsOnly,
746
- scopeFeed,
747
- });
748
- ixns = res.ixns;
749
- lookupTablesAddresses = res.lookupTablesAddresses;
750
- swapInputs = res.swapInputs;
751
- totalKlendAccounts = res.totalKlendAccounts;
665
+ const ixs = await buildIncreaseLeverageIxns(owner, kaminoMarket, collTokenMint, debtTokenMint, obligation, referrer, currentSlot, initialInputs.calcs, initialInputs.strategy, scopeFeed, initialInputs.collIsKtoken, {
666
+ preActionIxs: [],
667
+ swapIxs,
668
+ lookupTables,
669
+ }, budgetAndPriorityFeeIxs);
670
+ return {
671
+ ixs,
672
+ lookupTables,
673
+ swapInputs,
674
+ initialInputs,
675
+ };
752
676
  }
753
677
  else {
754
678
  console.log('Decreasing leverage');
755
- const res = await (0, exports.getDecreaseLeverageIxns)({
756
- connection,
757
- budgetAndPriorityFeeIxns,
758
- user,
759
- kaminoMarket,
760
- withdrawAmount: decimal_js_1.default.abs(adjustDepositPosition),
761
- repayAmount: decimal_js_1.default.abs(adjustBorrowPosition),
762
- collTokenMint,
763
- debtTokenMint,
764
- slippagePct,
765
- swapper,
766
- referrer,
767
- isKtoken,
768
- kamino,
769
- obligationTypeTagOverride,
770
- obligation: userObligation,
771
- currentSlot,
772
- getTotalKlendAccountsOnly,
773
- scopeFeed,
774
- });
775
- ixns = res.ixns;
776
- lookupTablesAddresses = res.lookupTablesAddresses;
777
- swapInputs = res.swapInputs;
778
- totalKlendAccounts = res.totalKlendAccounts;
679
+ let withdrawSwapper;
680
+ if (initialInputs.collIsKtoken) {
681
+ if (kamino === undefined) {
682
+ throw Error('Ktoken use as collateral for leverage without Kamino instance');
683
+ }
684
+ withdrawSwapper = await (0, utils_2.getKtokenToTokenSwapper)(kaminoMarket, kamino, owner, swapper);
685
+ }
686
+ else {
687
+ withdrawSwapper = swapper;
688
+ }
689
+ // 5. Get swap ixns
690
+ const { swapIxs, lookupTables } = await withdrawSwapper(swapInputs, initialInputs.klendAccounts, initialInputs.swapQuote);
691
+ const ixs = await buildDecreaseLeverageIxns(owner, kaminoMarket, collTokenMint, debtTokenMint, obligation, referrer, currentSlot, initialInputs.calcs, initialInputs.strategy, scopeFeed, initialInputs.collIsKtoken, {
692
+ preActionIxs: [],
693
+ swapIxs,
694
+ lookupTables,
695
+ }, budgetAndPriorityFeeIxs);
696
+ return {
697
+ ixs,
698
+ lookupTables,
699
+ swapInputs,
700
+ initialInputs,
701
+ };
779
702
  }
780
- return {
781
- ixns,
782
- lookupTablesAddresses,
783
- swapInputs,
784
- totalKlendAccounts,
785
- };
786
- };
787
- exports.getAdjustLeverageIxns = getAdjustLeverageIxns;
703
+ }
788
704
  /**
789
705
  * Deposit and borrow tokens if leverage increased
790
706
  */
791
- const getIncreaseLeverageIxns = async (props) => {
792
- const { connection, budgetAndPriorityFeeIxns, user, kaminoMarket, depositAmount, collTokenMint, debtTokenMint, slippagePct, priceDebtToColl, priceCollToDebt, swapper, referrer, isKtoken, priceAinB, kamino, obligationTypeTagOverride = 1, obligation, currentSlot, getTotalKlendAccountsOnly, scopeFeed, } = props;
707
+ async function buildIncreaseLeverageIxns(owner, kaminoMarket, collTokenMint, debtTokenMint, obligation, referrer, currentSlot, calcs, strategy, scopeFeed, collIsKtoken, swapQuoteIxs, budgetAndPriorityFeeIxns) {
793
708
  const collReserve = kaminoMarket.getReserveByMint(collTokenMint);
794
709
  const debtReserve = kaminoMarket.getReserveByMint(debtTokenMint);
795
- const collIsKtoken = await isKtoken(collTokenMint);
796
- const flashLoanFee = collReserve.getFlashLoanFee() || new decimal_js_1.default(0);
797
- if (!priceDebtToColl || !priceCollToDebt) {
798
- throw new Error('Price is not loaded. Please, reload the page and try again');
799
- }
800
- // TODO: why are we recalculating here again
801
- const strategy = collIsKtoken ? await kamino?.getStrategyByKTokenMint(collTokenMint) : undefined;
710
+ const debtTokenAta = (0, spl_token_1.getAssociatedTokenAddressSync)(debtTokenMint, owner);
711
+ const collTokenAta = (0, spl_token_1.getAssociatedTokenAddressSync)(collTokenMint, owner);
802
712
  // 1. Create atas & budget txns
803
713
  const budgetIxns = budgetAndPriorityFeeIxns || (0, utils_1.getComputeBudgetAndPriorityFeeIxns)(3000000);
804
714
  let mintsToCreateAtas;
@@ -848,105 +758,30 @@ const getIncreaseLeverageIxns = async (props) => {
848
758
  },
849
759
  ];
850
760
  }
851
- const { atas: [collTokenAta, debtTokenAta], createAtaIxs, } = await (0, utils_1.getAtasWithCreateIxnsIfMissing)(connection, user, mintsToCreateAtas);
761
+ const atasAndCreateIxns = (0, utils_1.createAtasIdempotent)(owner, mintsToCreateAtas);
852
762
  // 2. Create borrow flash loan instruction
853
- // used if coll is Ktoken and we borrow debt token instead
854
- const amountToFashBorrowDebt = depositAmount
855
- .div(priceDebtToColl)
856
- .mul(new decimal_js_1.default(1 + slippagePct / 100))
857
- .toDecimalPlaces(debtReserve.stats.decimals, decimal_js_1.default.ROUND_UP);
858
- // .toDecimalPlaces(debtReserve?.state.liquidity.mintDecimals.toNumber());
859
763
  const { flashBorrowIxn, flashRepayIxn } = (0, instructions_1.getFlashLoanInstructions)({
860
- borrowIxnIndex: budgetIxns.length + createAtaIxs.length, // TODO: how about user metadata ixns
861
- walletPublicKey: user,
764
+ borrowIxnIndex: budgetIxns.length + atasAndCreateIxns.length, // TODO: how about user metadata ixns
765
+ walletPublicKey: owner,
862
766
  lendingMarketAuthority: kaminoMarket.getLendingMarketAuthority(),
863
767
  lendingMarketAddress: kaminoMarket.getAddress(),
864
768
  reserve: !collIsKtoken ? collReserve : debtReserve,
865
- amountLamports: (0, classes_2.numberToLamportsDecimal)(!collIsKtoken ? depositAmount : amountToFashBorrowDebt, !collIsKtoken ? collReserve.stats.decimals : debtReserve.stats.decimals),
769
+ amountLamports: (0, classes_2.numberToLamportsDecimal)(!collIsKtoken ? calcs.adjustDepositPosition : calcs.amountToFlashBorrowDebt, !collIsKtoken ? collReserve.stats.decimals : debtReserve.stats.decimals),
866
770
  destinationAta: !collIsKtoken ? collTokenAta : debtTokenAta,
867
771
  referrerAccount: kaminoMarket.programId,
868
772
  referrerTokenState: kaminoMarket.programId,
869
773
  programId: kaminoMarket.programId,
870
774
  });
871
- // 3. Deposit initial tokens + borrowed tokens into reserve
872
- let obligationType;
873
- if (obligationTypeTagOverride === utils_1.ObligationTypeTag.Multiply) {
874
- // multiply
875
- obligationType = new utils_1.MultiplyObligation(collTokenMint, debtTokenMint, kaminoMarket.programId);
876
- }
877
- else if (obligationTypeTagOverride === utils_1.ObligationTypeTag.Leverage) {
878
- // leverage
879
- obligationType = new utils_1.LeverageObligation(collTokenMint, debtTokenMint, kaminoMarket.programId);
880
- }
881
- else {
882
- throw Error('Obligation type tag not supported for leverage, please use 1 - multiply or 3 - leverage');
883
- }
884
- const depositAction = await classes_1.KaminoAction.buildDepositTxns(kaminoMarket, (0, classes_2.numberToLamportsDecimal)(depositAmount, collReserve.stats.decimals).floor().toString(), collTokenMint, user, obligation ? obligation : obligationType, 0, false, false, false, // 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)
775
+ const depositAction = await classes_1.KaminoAction.buildDepositTxns(kaminoMarket, (0, classes_2.numberToLamportsDecimal)(calcs.adjustDepositPosition, collReserve.stats.decimals).floor().toString(), collTokenMint, owner, obligation, 0, false, false, false, // 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)
885
776
  referrer, currentSlot, { includeScopeRefresh: true, scopeFeed: scopeFeed });
886
- // 4. Get swap estimations to understand how much we need to borrow from borrow reserve
887
- const borrowAmount = depositAmount
888
- .mul(new decimal_js_1.default(1).plus(flashLoanFee))
889
- .mul(new decimal_js_1.default(1 + slippagePct / 100))
890
- .div(priceDebtToColl);
891
- const _collTokenExpectedSwapOut = depositAmount.mul(new decimal_js_1.default(1).plus(flashLoanFee));
892
- // 5. Borrow tokens in borrow token reserve that will be swapped to repay flash loan
893
- const borrowAction = await classes_1.KaminoAction.buildBorrowTxns(kaminoMarket, (0, classes_2.numberToLamportsDecimal)(borrowAmount, debtReserve.stats.decimals).ceil().toString(), debtTokenMint, user, obligation ? obligation : obligationType, 0, false, false, false, // 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)
777
+ // 4. Borrow tokens in borrow token reserve that will be swapped to repay flash loan
778
+ const borrowAction = await classes_1.KaminoAction.buildBorrowTxns(kaminoMarket, (0, classes_2.numberToLamportsDecimal)(calcs.borrowAmount, debtReserve.stats.decimals).ceil().toString(), debtTokenMint, owner, obligation, 0, false, false, false, // 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)
894
779
  referrer, currentSlot, { includeScopeRefresh: true, scopeFeed: scopeFeed });
895
- const klendIxns = [
896
- ...budgetIxns,
897
- ...createAtaIxs,
898
- ...[flashBorrowIxn],
899
- ...depositAction.setupIxs,
900
- ...depositAction.lendingIxs,
901
- ...depositAction.cleanupIxs,
902
- ...borrowAction.setupIxs,
903
- ...borrowAction.lendingIxs,
904
- ...borrowAction.cleanupIxs,
905
- ...[flashRepayIxn],
906
- ];
907
- const uniqueAccounts = new utils_1.PublicKeySet([]);
908
- klendIxns.forEach((ixn) => {
909
- ixn.keys.forEach((key) => {
910
- uniqueAccounts.add(key.pubkey);
911
- });
912
- });
913
- const totalKlendAccounts = uniqueAccounts.toArray().length;
914
- // return early to avoid extra swapper calls
915
- if (getTotalKlendAccountsOnly) {
916
- return {
917
- ixns: [],
918
- lookupTablesAddresses: [],
919
- swapInputs: {
920
- inputAmountLamports: new decimal_js_1.default('0'),
921
- inputMint: web3_js_1.PublicKey.default,
922
- outputMint: web3_js_1.PublicKey.default,
923
- },
924
- totalKlendAccounts: totalKlendAccounts,
925
- };
926
- }
927
- let depositSwapper;
928
- let expectedDebtTokenAtaBalance = new decimal_js_1.default(0);
929
- if (collIsKtoken) {
930
- if (kamino === undefined) {
931
- throw Error('Ktoken use as collateral for leverage without Kamino instance');
932
- }
933
- depositSwapper = await (0, utils_2.getTokenToKtokenSwapper)(connection, kaminoMarket, kamino, user, swapper, priceAinB, false);
934
- expectedDebtTokenAtaBalance = await (0, utils_2.getExpectedTokenBalanceAfterBorrow)(connection, debtTokenMint, user, (0, classes_2.numberToLamportsDecimal)(!collIsKtoken ? borrowAmount : amountToFashBorrowDebt, debtReserve.stats.decimals).floor(), debtReserve.state.liquidity.mintDecimals.toNumber());
935
- }
936
- else {
937
- depositSwapper = swapper;
938
- }
939
- const swapInputs = {
940
- inputAmountLamports: (0, classes_2.numberToLamportsDecimal)(!collIsKtoken ? borrowAmount : amountToFashBorrowDebt, debtReserve.stats.decimals).ceil(),
941
- inputMint: debtTokenMint,
942
- outputMint: collTokenMint,
943
- };
944
- const [swapIxns, lookupTablesAddresses] = await depositSwapper(swapInputs.inputAmountLamports.toNumber(), swapInputs.inputMint, swapInputs.outputMint, slippagePct, expectedDebtTokenAtaBalance);
945
- const swapInstructions = (0, utils_1.removeBudgetAndAtaIxns)(swapIxns, []);
946
- const ixns = !collIsKtoken
780
+ const swapInstructions = (0, utils_1.removeBudgetAndAtaIxns)(swapQuoteIxs.swapIxs, []);
781
+ const ixs = !collIsKtoken
947
782
  ? [
948
783
  ...budgetIxns,
949
- ...createAtaIxs,
784
+ ...atasAndCreateIxns.map((x) => x.createAtaIx),
950
785
  ...[flashBorrowIxn],
951
786
  ...depositAction.setupIxs,
952
787
  ...depositAction.lendingIxs,
@@ -959,7 +794,7 @@ const getIncreaseLeverageIxns = async (props) => {
959
794
  ]
960
795
  : [
961
796
  ...budgetIxns,
962
- ...createAtaIxs,
797
+ ...atasAndCreateIxns.map((x) => x.createAtaIx),
963
798
  ...[flashBorrowIxn],
964
799
  ...swapInstructions,
965
800
  ...depositAction.setupIxs,
@@ -970,37 +805,15 @@ const getIncreaseLeverageIxns = async (props) => {
970
805
  ...borrowAction.cleanupIxs,
971
806
  ...[flashRepayIxn],
972
807
  ];
973
- ixns.forEach((ixn, i) => {
974
- console.log(`ixn ${i + 1}: ${ixn.programId.toString()}`);
975
- });
976
- // Create and send transaction
977
- if (collIsKtoken) {
978
- if (strategy?.strategy.strategyLookupTable) {
979
- lookupTablesAddresses.push(strategy?.strategy.strategyLookupTable);
980
- }
981
- else {
982
- console.log('Strategy lookup table not found');
983
- }
984
- }
985
- return {
986
- ixns,
987
- lookupTablesAddresses,
988
- swapInputs,
989
- totalKlendAccounts,
990
- };
991
- };
992
- exports.getIncreaseLeverageIxns = getIncreaseLeverageIxns;
808
+ return ixs;
809
+ }
993
810
  /**
994
811
  * Withdraw and repay tokens if leverage decreased
995
812
  */
996
- const getDecreaseLeverageIxns = async (props) => {
997
- const { connection, budgetAndPriorityFeeIxns, user, kaminoMarket, withdrawAmount, repayAmount, collTokenMint, debtTokenMint, slippagePct, swapper, referrer, isKtoken, kamino, obligationTypeTagOverride = 1, obligation, currentSlot, getTotalKlendAccountsOnly, scopeFeed, } = props;
998
- console.log('getDecreaseLeverageIxns', (0, calcs_1.toJson)({ withdrawAmount, repayAmount, collTokenMint, debtTokenMint, slippagePct }));
813
+ async function buildDecreaseLeverageIxns(owner, kaminoMarket, collTokenMint, debtTokenMint, obligation, referrer, currentSlot, calcs, strategy, scopeFeed, collIsKtoken, swapQuoteIxs, budgetAndPriorityFeeIxns) {
999
814
  const collReserve = kaminoMarket.getReserveByMint(collTokenMint);
1000
815
  const debtReserve = kaminoMarket.getReserveByMint(debtTokenMint);
1001
- const collIsKtoken = await isKtoken(collTokenMint);
1002
- const flashLoanFee = debtReserve?.getFlashLoanFee() || new decimal_js_1.default(0);
1003
- const strategy = collIsKtoken ? await kamino?.getStrategyByKTokenMint(collTokenMint) : undefined;
816
+ const debtTokenAta = (0, spl_token_1.getAssociatedTokenAddressSync)(debtTokenMint, owner);
1004
817
  // 1. Create atas & budget txns
1005
818
  const budgetIxns = budgetAndPriorityFeeIxns || (0, utils_1.getComputeBudgetAndPriorityFeeIxns)(3000000);
1006
819
  let mintsToCreateAtas;
@@ -1050,105 +863,42 @@ const getDecreaseLeverageIxns = async (props) => {
1050
863
  },
1051
864
  ];
1052
865
  }
1053
- const { atas: [, debtTokenAta], createAtaIxs, } = await (0, utils_1.getAtasWithCreateIxnsIfMissing)(connection, user, mintsToCreateAtas);
866
+ const atasAndCreateIxns = (0, utils_1.createAtasIdempotent)(owner, mintsToCreateAtas);
1054
867
  // TODO: Mihai/Marius check if we can improve this logic and not convert any SOL
1055
868
  // This is here so that we have enough wsol to repay in case the kAB swapped to sol after estimates is not enough
1056
869
  const closeWsolAtaIxns = [];
1057
- if (debtTokenMint.equals(utils_1.WRAPPED_SOL_MINT)) {
1058
- const wsolAta = await (0, utils_1.getAssociatedTokenAddress)(utils_1.WRAPPED_SOL_MINT, user, false);
1059
- closeWsolAtaIxns.push((0, spl_token_1.createCloseAccountInstruction)(wsolAta, user, user, [], spl_token_1.TOKEN_PROGRAM_ID));
1060
- }
1061
870
  const fillWsolAtaIxns = [];
1062
871
  if (debtTokenMint.equals(utils_1.WRAPPED_SOL_MINT)) {
1063
- const halfSolBalance = (await connection.getBalance(user)) / web3_js_1.LAMPORTS_PER_SOL / 2;
872
+ const wsolAta = (0, utils_1.getAssociatedTokenAddress)(utils_1.WRAPPED_SOL_MINT, owner, false);
873
+ closeWsolAtaIxns.push((0, spl_token_1.createCloseAccountInstruction)(wsolAta, owner, owner, [], spl_token_1.TOKEN_PROGRAM_ID));
874
+ const halfSolBalance = (await kaminoMarket.getConnection().getBalance(owner)) / web3_js_1.LAMPORTS_PER_SOL / 2;
1064
875
  const balanceToWrap = halfSolBalance < 0.1 ? halfSolBalance : 0.1;
1065
- fillWsolAtaIxns.push(...(0, utils_1.getDepositWsolIxns)(user, debtTokenAta, (0, classes_2.numberToLamportsDecimal)(balanceToWrap, debtReserve.stats.decimals).ceil()));
876
+ fillWsolAtaIxns.push(...(0, utils_1.getDepositWsolIxns)(owner, wsolAta, (0, classes_2.numberToLamportsDecimal)(balanceToWrap, debtReserve.stats.decimals).ceil()));
1066
877
  }
1067
878
  // 3. Flash borrow & repay amount to repay (debt)
1068
879
  const { flashBorrowIxn, flashRepayIxn } = (0, instructions_1.getFlashLoanInstructions)({
1069
- borrowIxnIndex: budgetIxns.length + createAtaIxs.length + fillWsolAtaIxns.length,
1070
- walletPublicKey: user,
880
+ borrowIxnIndex: budgetIxns.length + atasAndCreateIxns.length + fillWsolAtaIxns.length,
881
+ walletPublicKey: owner,
1071
882
  lendingMarketAuthority: kaminoMarket.getLendingMarketAuthority(),
1072
883
  lendingMarketAddress: kaminoMarket.getAddress(),
1073
884
  reserve: debtReserve,
1074
- amountLamports: (0, classes_2.numberToLamportsDecimal)(repayAmount, debtReserve.stats.decimals),
885
+ amountLamports: (0, classes_2.numberToLamportsDecimal)(decimal_js_1.default.abs(calcs.adjustBorrowPosition), debtReserve.stats.decimals),
1075
886
  destinationAta: debtTokenAta,
1076
887
  referrerAccount: kaminoMarket.programId,
1077
888
  referrerTokenState: kaminoMarket.programId,
1078
889
  programId: kaminoMarket.programId,
1079
890
  });
1080
891
  // 4. Actually do the repay of the flash borrowed amounts
1081
- let obligationType;
1082
- if (obligationTypeTagOverride === utils_1.ObligationTypeTag.Multiply) {
1083
- // multiply
1084
- obligationType = new utils_1.MultiplyObligation(collTokenMint, debtTokenMint, kaminoMarket.programId);
1085
- }
1086
- else if (obligationTypeTagOverride === utils_1.ObligationTypeTag.Leverage) {
1087
- // leverage
1088
- obligationType = new utils_1.LeverageObligation(collTokenMint, debtTokenMint, kaminoMarket.programId);
1089
- }
1090
- else {
1091
- throw Error('Obligation type tag not supported for leverage, please use 1 - multiply or 3 - leverage');
1092
- }
1093
892
  const scopeRefresh = scopeFeed ? { includeScopeRefresh: true, scopeFeed: scopeFeed } : undefined;
1094
- const repayAction = await classes_1.KaminoAction.buildRepayTxns(kaminoMarket, (0, classes_2.numberToLamportsDecimal)(repayAmount, debtReserve.stats.decimals).floor().toString(), debtTokenMint, user, obligation ? obligation : obligationType, currentSlot, undefined, 0, false, false, false, // 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)
893
+ 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, currentSlot, undefined, 0, false, false, false, // 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)
1095
894
  referrer, scopeRefresh);
1096
895
  // 6. Withdraw collateral (a little bit more to be able to pay for the slippage on swap)
1097
- const withdrawAmountWithSlippageAndFlashLoanFee = withdrawAmount
1098
- .mul(new decimal_js_1.default(1).plus(flashLoanFee))
1099
- .mul(1 + slippagePct / 100);
1100
- const withdrawAction = await classes_1.KaminoAction.buildWithdrawTxns(kaminoMarket, (0, classes_2.numberToLamportsDecimal)(withdrawAmountWithSlippageAndFlashLoanFee, collReserve.stats.decimals).ceil().toString(), collTokenMint, user, obligation ? obligation : obligationType, 0, false, false, false, // 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)
896
+ const withdrawAction = await classes_1.KaminoAction.buildWithdrawTxns(kaminoMarket, (0, classes_2.numberToLamportsDecimal)(calcs.withdrawAmountWithSlippageAndFlashLoanFee, collReserve.stats.decimals).ceil().toString(), collTokenMint, owner, obligation, 0, false, false, false, // 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)
1101
897
  referrer, currentSlot, { includeScopeRefresh: true, scopeFeed: scopeFeed });
1102
- const klendIxns = [
1103
- ...budgetIxns,
1104
- ...createAtaIxs,
1105
- ...fillWsolAtaIxns,
1106
- ...[flashBorrowIxn],
1107
- ...repayAction.setupIxs,
1108
- ...repayAction.lendingIxs,
1109
- ...repayAction.cleanupIxs,
1110
- ...withdrawAction.setupIxs,
1111
- ...withdrawAction.lendingIxs,
1112
- ...withdrawAction.cleanupIxs,
1113
- ...[flashRepayIxn],
1114
- ...closeWsolAtaIxns,
1115
- ];
1116
- const uniqueAccs = (0, utils_1.uniqueAccounts)(klendIxns);
1117
- const totalKlendAccounts = uniqueAccs.length;
1118
- // return early to avoid extra swapper calls
1119
- if (getTotalKlendAccountsOnly) {
1120
- return {
1121
- ixns: [],
1122
- lookupTablesAddresses: [],
1123
- swapInputs: {
1124
- inputAmountLamports: new decimal_js_1.default('0'),
1125
- inputMint: web3_js_1.PublicKey.default,
1126
- outputMint: web3_js_1.PublicKey.default,
1127
- },
1128
- totalKlendAccounts: totalKlendAccounts,
1129
- };
1130
- }
1131
- let withdrawSwapper;
1132
- if (collIsKtoken) {
1133
- if (kamino === undefined) {
1134
- throw Error('Ktoken use as collateral for leverage without Kamino instance');
1135
- }
1136
- withdrawSwapper = await (0, utils_2.getKtokenToTokenSwapper)(kaminoMarket, kamino, user, swapper);
1137
- }
1138
- else {
1139
- withdrawSwapper = swapper;
1140
- }
1141
- const swapInputs = {
1142
- inputAmountLamports: (0, classes_2.numberToLamportsDecimal)(withdrawAmountWithSlippageAndFlashLoanFee, collReserve.stats.decimals).ceil(),
1143
- inputMint: collTokenMint,
1144
- outputMint: debtTokenMint,
1145
- };
1146
- // 5. Get swap ixns
1147
- const [swapIxns, lookupTablesAddresses] = await withdrawSwapper(swapInputs.inputAmountLamports.toNumber(), swapInputs.inputMint, swapInputs.outputMint, slippagePct);
1148
- const swapInstructions = (0, utils_1.removeBudgetAndAtaIxns)(swapIxns, []);
898
+ const swapInstructions = (0, utils_1.removeBudgetAndAtaIxns)(swapQuoteIxs.swapIxs, []);
1149
899
  const ixns = [
1150
900
  ...budgetIxns,
1151
- ...createAtaIxs,
901
+ ...atasAndCreateIxns.map((x) => x.createAtaIx),
1152
902
  ...fillWsolAtaIxns,
1153
903
  ...[flashBorrowIxn],
1154
904
  ...repayAction.setupIxs,
@@ -1161,24 +911,6 @@ const getDecreaseLeverageIxns = async (props) => {
1161
911
  ...[flashRepayIxn],
1162
912
  ...closeWsolAtaIxns,
1163
913
  ];
1164
- ixns.forEach((ixn, i) => {
1165
- console.log(`ixn ${i + 1}: ${ixn.programId.toString()}`);
1166
- });
1167
- if (collIsKtoken) {
1168
- if (strategy?.strategy.strategyLookupTable) {
1169
- lookupTablesAddresses.push(strategy?.strategy.strategyLookupTable);
1170
- }
1171
- else {
1172
- console.log('Strategy lookup table not found');
1173
- }
1174
- }
1175
- // Create and send transaction
1176
- return {
1177
- ixns,
1178
- lookupTablesAddresses,
1179
- swapInputs,
1180
- totalKlendAccounts,
1181
- };
1182
- };
1183
- exports.getDecreaseLeverageIxns = getDecreaseLeverageIxns;
914
+ return ixns;
915
+ }
1184
916
  //# sourceMappingURL=operations.js.map