@kamino-finance/klend-sdk 5.0.3 → 5.0.5

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 (230) hide show
  1. package/dist/classes/manager.d.ts +38 -4
  2. package/dist/classes/manager.d.ts.map +1 -1
  3. package/dist/classes/manager.js +98 -4
  4. package/dist/classes/manager.js.map +1 -1
  5. package/dist/classes/market.d.ts.map +1 -1
  6. package/dist/classes/market.js +2 -25
  7. package/dist/classes/market.js.map +1 -1
  8. package/dist/classes/vault.d.ts +12 -8
  9. package/dist/classes/vault.d.ts.map +1 -1
  10. package/dist/classes/vault.js +94 -66
  11. package/dist/classes/vault.js.map +1 -1
  12. package/dist/client_kamino_manager.d.ts.map +1 -1
  13. package/dist/client_kamino_manager.js +14 -0
  14. package/dist/client_kamino_manager.js.map +1 -1
  15. package/dist/idl_codegen_kamino_vault/accounts/Reserve.d.ts +3 -3
  16. package/dist/idl_codegen_kamino_vault/accounts/Reserve.d.ts.map +1 -1
  17. package/dist/idl_codegen_kamino_vault/accounts/Reserve.js +22 -18
  18. package/dist/idl_codegen_kamino_vault/accounts/Reserve.js.map +1 -1
  19. package/dist/idl_codegen_kamino_vault/accounts/VaultState.d.ts +45 -15
  20. package/dist/idl_codegen_kamino_vault/accounts/VaultState.d.ts.map +1 -1
  21. package/dist/idl_codegen_kamino_vault/accounts/VaultState.js +101 -39
  22. package/dist/idl_codegen_kamino_vault/accounts/VaultState.js.map +1 -1
  23. package/dist/idl_codegen_kamino_vault/accounts/index.d.ts +4 -4
  24. package/dist/idl_codegen_kamino_vault/accounts/index.d.ts.map +1 -1
  25. package/dist/idl_codegen_kamino_vault/accounts/index.js.map +1 -1
  26. package/dist/idl_codegen_kamino_vault/errors/anchor.d.ts.map +1 -1
  27. package/dist/idl_codegen_kamino_vault/errors/anchor.js +162 -162
  28. package/dist/idl_codegen_kamino_vault/errors/anchor.js.map +1 -1
  29. package/dist/idl_codegen_kamino_vault/errors/custom.d.ts +144 -32
  30. package/dist/idl_codegen_kamino_vault/errors/custom.d.ts.map +1 -1
  31. package/dist/idl_codegen_kamino_vault/errors/custom.js +305 -109
  32. package/dist/idl_codegen_kamino_vault/errors/custom.js.map +1 -1
  33. package/dist/idl_codegen_kamino_vault/errors/index.d.ts +3 -3
  34. package/dist/idl_codegen_kamino_vault/errors/index.d.ts.map +1 -1
  35. package/dist/idl_codegen_kamino_vault/errors/index.js +7 -2
  36. package/dist/idl_codegen_kamino_vault/errors/index.js.map +1 -1
  37. package/dist/idl_codegen_kamino_vault/instructions/deposit.d.ts +4 -2
  38. package/dist/idl_codegen_kamino_vault/instructions/deposit.d.ts.map +1 -1
  39. package/dist/idl_codegen_kamino_vault/instructions/deposit.js +3 -1
  40. package/dist/idl_codegen_kamino_vault/instructions/deposit.js.map +1 -1
  41. package/dist/idl_codegen_kamino_vault/instructions/giveUpPendingFees.d.ts +13 -0
  42. package/dist/idl_codegen_kamino_vault/instructions/giveUpPendingFees.d.ts.map +1 -0
  43. package/dist/idl_codegen_kamino_vault/instructions/giveUpPendingFees.js +47 -0
  44. package/dist/idl_codegen_kamino_vault/instructions/giveUpPendingFees.js.map +1 -0
  45. package/dist/idl_codegen_kamino_vault/instructions/index.d.ts +18 -10
  46. package/dist/idl_codegen_kamino_vault/instructions/index.d.ts.map +1 -1
  47. package/dist/idl_codegen_kamino_vault/instructions/index.js +9 -1
  48. package/dist/idl_codegen_kamino_vault/instructions/index.js.map +1 -1
  49. package/dist/idl_codegen_kamino_vault/instructions/initVault.d.ts +2 -1
  50. package/dist/idl_codegen_kamino_vault/instructions/initVault.d.ts.map +1 -1
  51. package/dist/idl_codegen_kamino_vault/instructions/initVault.js +1 -0
  52. package/dist/idl_codegen_kamino_vault/instructions/initVault.js.map +1 -1
  53. package/dist/idl_codegen_kamino_vault/instructions/invest.d.ts +6 -2
  54. package/dist/idl_codegen_kamino_vault/instructions/invest.d.ts.map +1 -1
  55. package/dist/idl_codegen_kamino_vault/instructions/invest.js +9 -1
  56. package/dist/idl_codegen_kamino_vault/instructions/invest.js.map +1 -1
  57. package/dist/idl_codegen_kamino_vault/instructions/updateAdmin.d.ts +7 -0
  58. package/dist/idl_codegen_kamino_vault/instructions/updateAdmin.d.ts.map +1 -0
  59. package/dist/idl_codegen_kamino_vault/instructions/updateAdmin.js +16 -0
  60. package/dist/idl_codegen_kamino_vault/instructions/updateAdmin.js.map +1 -0
  61. package/dist/idl_codegen_kamino_vault/instructions/updateReserveAllocation.d.ts +2 -2
  62. package/dist/idl_codegen_kamino_vault/instructions/updateReserveAllocation.d.ts.map +1 -1
  63. package/dist/idl_codegen_kamino_vault/instructions/updateReserveAllocation.js +1 -1
  64. package/dist/idl_codegen_kamino_vault/instructions/updateReserveAllocation.js.map +1 -1
  65. package/dist/idl_codegen_kamino_vault/instructions/updateVaultConfig.d.ts +14 -0
  66. package/dist/idl_codegen_kamino_vault/instructions/updateVaultConfig.d.ts.map +1 -0
  67. package/dist/idl_codegen_kamino_vault/instructions/updateVaultConfig.js +52 -0
  68. package/dist/idl_codegen_kamino_vault/instructions/updateVaultConfig.js.map +1 -0
  69. package/dist/idl_codegen_kamino_vault/instructions/withdraw.d.ts +4 -2
  70. package/dist/idl_codegen_kamino_vault/instructions/withdraw.d.ts.map +1 -1
  71. package/dist/idl_codegen_kamino_vault/instructions/withdraw.js +8 -2
  72. package/dist/idl_codegen_kamino_vault/instructions/withdraw.js.map +1 -1
  73. package/dist/idl_codegen_kamino_vault/instructions/withdrawPendingFees.d.ts +23 -0
  74. package/dist/idl_codegen_kamino_vault/instructions/withdrawPendingFees.d.ts.map +1 -0
  75. package/dist/idl_codegen_kamino_vault/instructions/withdrawPendingFees.js +51 -0
  76. package/dist/idl_codegen_kamino_vault/instructions/withdrawPendingFees.js.map +1 -0
  77. package/dist/idl_codegen_kamino_vault/programId.d.ts +1 -1
  78. package/dist/idl_codegen_kamino_vault/programId.d.ts.map +1 -1
  79. package/dist/idl_codegen_kamino_vault/programId.js +1 -1
  80. package/dist/idl_codegen_kamino_vault/programId.js.map +1 -1
  81. package/dist/idl_codegen_kamino_vault/types/BigFractionBytes.d.ts +2 -2
  82. package/dist/idl_codegen_kamino_vault/types/BigFractionBytes.d.ts.map +1 -1
  83. package/dist/idl_codegen_kamino_vault/types/BigFractionBytes.js +4 -1
  84. package/dist/idl_codegen_kamino_vault/types/BigFractionBytes.js.map +1 -1
  85. package/dist/idl_codegen_kamino_vault/types/BorrowRateCurve.d.ts +1 -1
  86. package/dist/idl_codegen_kamino_vault/types/BorrowRateCurve.d.ts.map +1 -1
  87. package/dist/idl_codegen_kamino_vault/types/BorrowRateCurve.js +1 -1
  88. package/dist/idl_codegen_kamino_vault/types/BorrowRateCurve.js.map +1 -1
  89. package/dist/idl_codegen_kamino_vault/types/CurvePoint.d.ts +1 -1
  90. package/dist/idl_codegen_kamino_vault/types/CurvePoint.d.ts.map +1 -1
  91. package/dist/idl_codegen_kamino_vault/types/CurvePoint.js +1 -1
  92. package/dist/idl_codegen_kamino_vault/types/CurvePoint.js.map +1 -1
  93. package/dist/idl_codegen_kamino_vault/types/LastUpdate.d.ts +2 -2
  94. package/dist/idl_codegen_kamino_vault/types/LastUpdate.d.ts.map +1 -1
  95. package/dist/idl_codegen_kamino_vault/types/LastUpdate.js +6 -1
  96. package/dist/idl_codegen_kamino_vault/types/LastUpdate.js.map +1 -1
  97. package/dist/idl_codegen_kamino_vault/types/PriceHeuristic.d.ts +2 -2
  98. package/dist/idl_codegen_kamino_vault/types/PriceHeuristic.d.ts.map +1 -1
  99. package/dist/idl_codegen_kamino_vault/types/PriceHeuristic.js +1 -1
  100. package/dist/idl_codegen_kamino_vault/types/PriceHeuristic.js.map +1 -1
  101. package/dist/idl_codegen_kamino_vault/types/PythConfiguration.d.ts +2 -2
  102. package/dist/idl_codegen_kamino_vault/types/PythConfiguration.d.ts.map +1 -1
  103. package/dist/idl_codegen_kamino_vault/types/PythConfiguration.js +1 -1
  104. package/dist/idl_codegen_kamino_vault/types/PythConfiguration.js.map +1 -1
  105. package/dist/idl_codegen_kamino_vault/types/ReserveCollateral.d.ts +3 -3
  106. package/dist/idl_codegen_kamino_vault/types/ReserveCollateral.d.ts.map +1 -1
  107. package/dist/idl_codegen_kamino_vault/types/ReserveCollateral.js +5 -5
  108. package/dist/idl_codegen_kamino_vault/types/ReserveCollateral.js.map +1 -1
  109. package/dist/idl_codegen_kamino_vault/types/ReserveConfig.d.ts +27 -24
  110. package/dist/idl_codegen_kamino_vault/types/ReserveConfig.d.ts.map +1 -1
  111. package/dist/idl_codegen_kamino_vault/types/ReserveConfig.js +59 -53
  112. package/dist/idl_codegen_kamino_vault/types/ReserveConfig.js.map +1 -1
  113. package/dist/idl_codegen_kamino_vault/types/ReserveFees.d.ts +2 -2
  114. package/dist/idl_codegen_kamino_vault/types/ReserveFees.d.ts.map +1 -1
  115. package/dist/idl_codegen_kamino_vault/types/ReserveFees.js +5 -1
  116. package/dist/idl_codegen_kamino_vault/types/ReserveFees.js.map +1 -1
  117. package/dist/idl_codegen_kamino_vault/types/ReserveLiquidity.d.ts +11 -3
  118. package/dist/idl_codegen_kamino_vault/types/ReserveLiquidity.d.ts.map +1 -1
  119. package/dist/idl_codegen_kamino_vault/types/ReserveLiquidity.js +25 -17
  120. package/dist/idl_codegen_kamino_vault/types/ReserveLiquidity.js.map +1 -1
  121. package/dist/idl_codegen_kamino_vault/types/ScopeConfiguration.d.ts +2 -2
  122. package/dist/idl_codegen_kamino_vault/types/ScopeConfiguration.d.ts.map +1 -1
  123. package/dist/idl_codegen_kamino_vault/types/ScopeConfiguration.js +3 -3
  124. package/dist/idl_codegen_kamino_vault/types/ScopeConfiguration.js.map +1 -1
  125. package/dist/idl_codegen_kamino_vault/types/SwitchboardConfiguration.d.ts +2 -2
  126. package/dist/idl_codegen_kamino_vault/types/SwitchboardConfiguration.d.ts.map +1 -1
  127. package/dist/idl_codegen_kamino_vault/types/SwitchboardConfiguration.js +1 -1
  128. package/dist/idl_codegen_kamino_vault/types/SwitchboardConfiguration.js.map +1 -1
  129. package/dist/idl_codegen_kamino_vault/types/TokenInfo.d.ts +3 -3
  130. package/dist/idl_codegen_kamino_vault/types/TokenInfo.d.ts.map +1 -1
  131. package/dist/idl_codegen_kamino_vault/types/TokenInfo.js +11 -11
  132. package/dist/idl_codegen_kamino_vault/types/TokenInfo.js.map +1 -1
  133. package/dist/idl_codegen_kamino_vault/types/VaultAllocation.d.ts +18 -8
  134. package/dist/idl_codegen_kamino_vault/types/VaultAllocation.d.ts.map +1 -1
  135. package/dist/idl_codegen_kamino_vault/types/VaultAllocation.js +27 -13
  136. package/dist/idl_codegen_kamino_vault/types/VaultAllocation.js.map +1 -1
  137. package/dist/idl_codegen_kamino_vault/types/VaultConfigField.d.ts +110 -0
  138. package/dist/idl_codegen_kamino_vault/types/VaultConfigField.d.ts.map +1 -0
  139. package/dist/idl_codegen_kamino_vault/types/VaultConfigField.js +242 -0
  140. package/dist/idl_codegen_kamino_vault/types/VaultConfigField.js.map +1 -0
  141. package/dist/idl_codegen_kamino_vault/types/index.d.ts +34 -30
  142. package/dist/idl_codegen_kamino_vault/types/index.d.ts.map +1 -1
  143. package/dist/idl_codegen_kamino_vault/types/index.js +26 -1
  144. package/dist/idl_codegen_kamino_vault/types/index.js.map +1 -1
  145. package/dist/lending_operations/repay_with_collateral_operations.d.ts.map +1 -1
  146. package/dist/lending_operations/repay_with_collateral_operations.js +6 -0
  147. package/dist/lending_operations/repay_with_collateral_operations.js.map +1 -1
  148. package/dist/leverage/calcs.d.ts +28 -1
  149. package/dist/leverage/calcs.d.ts.map +1 -1
  150. package/dist/leverage/calcs.js +204 -8
  151. package/dist/leverage/calcs.js.map +1 -1
  152. package/dist/leverage/index.d.ts +1 -0
  153. package/dist/leverage/index.d.ts.map +1 -1
  154. package/dist/leverage/index.js +1 -0
  155. package/dist/leverage/index.js.map +1 -1
  156. package/dist/leverage/operations.d.ts +14 -241
  157. package/dist/leverage/operations.d.ts.map +1 -1
  158. package/dist/leverage/operations.js +508 -776
  159. package/dist/leverage/operations.js.map +1 -1
  160. package/dist/leverage/types.d.ts +173 -0
  161. package/dist/leverage/types.d.ts.map +1 -0
  162. package/dist/leverage/types.js +3 -0
  163. package/dist/leverage/types.js.map +1 -0
  164. package/dist/leverage/utils.d.ts +5 -5
  165. package/dist/leverage/utils.d.ts.map +1 -1
  166. package/dist/leverage/utils.js +68 -33
  167. package/dist/leverage/utils.js.map +1 -1
  168. package/dist/utils/constants.d.ts +1 -0
  169. package/dist/utils/constants.d.ts.map +1 -1
  170. package/dist/utils/constants.js +2 -1
  171. package/dist/utils/constants.js.map +1 -1
  172. package/dist/utils/fuzz.d.ts +3 -0
  173. package/dist/utils/fuzz.d.ts.map +1 -0
  174. package/dist/utils/fuzz.js +11 -0
  175. package/dist/utils/fuzz.js.map +1 -0
  176. package/dist/utils/index.d.ts +1 -0
  177. package/dist/utils/index.d.ts.map +1 -1
  178. package/dist/utils/index.js +1 -0
  179. package/dist/utils/index.js.map +1 -1
  180. package/dist/utils/rpc.d.ts.map +1 -1
  181. package/dist/utils/rpc.js +2 -1
  182. package/dist/utils/rpc.js.map +1 -1
  183. package/package.json +2 -1
  184. package/src/classes/manager.ts +122 -4
  185. package/src/classes/market.ts +1 -1
  186. package/src/classes/vault.ts +123 -49
  187. package/src/client_kamino_manager.ts +18 -0
  188. package/src/idl_codegen_kamino_vault/accounts/Reserve.ts +132 -105
  189. package/src/idl_codegen_kamino_vault/accounts/VaultState.ts +217 -124
  190. package/src/idl_codegen_kamino_vault/accounts/index.ts +4 -4
  191. package/src/idl_codegen_kamino_vault/errors/anchor.ts +335 -326
  192. package/src/idl_codegen_kamino_vault/errors/custom.ts +353 -157
  193. package/src/idl_codegen_kamino_vault/errors/index.ts +35 -22
  194. package/src/idl_codegen_kamino_vault/instructions/deposit.ts +33 -25
  195. package/src/idl_codegen_kamino_vault/instructions/giveUpPendingFees.ts +40 -0
  196. package/src/idl_codegen_kamino_vault/instructions/index.ts +27 -10
  197. package/src/idl_codegen_kamino_vault/instructions/initVault.ts +25 -20
  198. package/src/idl_codegen_kamino_vault/instructions/invest.ts +40 -25
  199. package/src/idl_codegen_kamino_vault/instructions/updateAdmin.ts +24 -0
  200. package/src/idl_codegen_kamino_vault/instructions/updateReserveAllocation.ts +24 -24
  201. package/src/idl_codegen_kamino_vault/instructions/updateVaultConfig.ts +49 -0
  202. package/src/idl_codegen_kamino_vault/instructions/withdraw.ts +45 -33
  203. package/src/idl_codegen_kamino_vault/instructions/withdrawPendingFees.ts +75 -0
  204. package/src/idl_codegen_kamino_vault/programId.ts +5 -3
  205. package/src/idl_codegen_kamino_vault/types/BigFractionBytes.ts +24 -18
  206. package/src/idl_codegen_kamino_vault/types/BorrowRateCurve.ts +21 -16
  207. package/src/idl_codegen_kamino_vault/types/CurvePoint.ts +21 -18
  208. package/src/idl_codegen_kamino_vault/types/LastUpdate.ts +32 -27
  209. package/src/idl_codegen_kamino_vault/types/PriceHeuristic.ts +25 -22
  210. package/src/idl_codegen_kamino_vault/types/PythConfiguration.ts +14 -14
  211. package/src/idl_codegen_kamino_vault/types/ReserveCollateral.ts +35 -35
  212. package/src/idl_codegen_kamino_vault/types/ReserveConfig.ts +232 -191
  213. package/src/idl_codegen_kamino_vault/types/ReserveFees.ts +27 -23
  214. package/src/idl_codegen_kamino_vault/types/ReserveLiquidity.ts +116 -98
  215. package/src/idl_codegen_kamino_vault/types/ScopeConfiguration.ts +25 -25
  216. package/src/idl_codegen_kamino_vault/types/SwitchboardConfiguration.ts +21 -18
  217. package/src/idl_codegen_kamino_vault/types/TokenInfo.ts +92 -74
  218. package/src/idl_codegen_kamino_vault/types/VaultAllocation.ts +67 -49
  219. package/src/idl_codegen_kamino_vault/types/VaultConfigField.ts +270 -0
  220. package/src/idl_codegen_kamino_vault/types/index.ts +77 -30
  221. package/src/lending_operations/repay_with_collateral_operations.ts +2 -0
  222. package/src/leverage/calcs.ts +315 -8
  223. package/src/leverage/index.ts +1 -0
  224. package/src/leverage/operations.ts +1079 -1331
  225. package/src/leverage/types.ts +211 -0
  226. package/src/leverage/utils.ts +103 -64
  227. package/src/utils/constants.ts +2 -0
  228. package/src/utils/fuzz.ts +5 -0
  229. package/src/utils/index.ts +1 -0
  230. package/src/utils/rpc.ts +2 -1
@@ -1,12 +1,12 @@
1
- import {
2
- AddressLookupTableAccount,
3
- Connection,
4
- LAMPORTS_PER_SOL,
5
- PublicKey,
6
- TransactionInstruction,
7
- } from '@solana/web3.js';
1
+ import { LAMPORTS_PER_SOL, PublicKey, TransactionInstruction } from '@solana/web3.js';
8
2
  import Decimal from 'decimal.js';
9
- import { KaminoAction, KaminoMarket, KaminoObligation, lamportsToNumberDecimal as fromLamports } from '../classes';
3
+ import {
4
+ KaminoAction,
5
+ KaminoMarket,
6
+ KaminoObligation,
7
+ KaminoReserve,
8
+ lamportsToNumberDecimal as fromLamports,
9
+ } from '../classes';
10
10
  import { getFlashLoanInstructions } from './instructions';
11
11
 
12
12
  import { numberToLamportsDecimal as toLamports } from '../classes';
@@ -15,373 +15,248 @@ import {
15
15
  MultiplyObligation,
16
16
  ObligationType,
17
17
  ObligationTypeTag,
18
- PublicKeySet,
18
+ SOL_DECIMALS,
19
19
  U64_MAX,
20
20
  WRAPPED_SOL_MINT,
21
+ createAtasIdempotent,
21
22
  getAssociatedTokenAddress,
22
- getAtasWithCreateIxnsIfMissing,
23
23
  getComputeBudgetAndPriorityFeeIxns,
24
24
  getDepositWsolIxns,
25
+ getLookupTableAccount,
25
26
  removeBudgetAndAtaIxns,
26
27
  uniqueAccounts,
27
28
  } from '../utils';
28
- import { calcAdjustAmounts, calcWithdrawAmounts, simulateMintKToken, toJson } from './calcs';
29
- import { TOKEN_PROGRAM_ID, createCloseAccountInstruction } from '@solana/spl-token';
30
29
  import {
31
- Kamino,
32
- InstructionsWithLookupTables,
33
- StrategyWithAddress,
34
- TokenAmounts,
35
- } from '@kamino-finance/kliquidity-sdk';
30
+ adjustDepositLeverageCalcs,
31
+ adjustWithdrawLeverageCalcs,
32
+ calcAdjustAmounts,
33
+ depositLeverageCalcs,
34
+ depositLeverageKtokenCalcs,
35
+ toJson,
36
+ withdrawLeverageCalcs,
37
+ } from './calcs';
38
+ import { TOKEN_PROGRAM_ID, createCloseAccountInstruction, getAssociatedTokenAddressSync } from '@solana/spl-token';
39
+ import { Kamino, StrategyWithAddress } from '@kamino-finance/kliquidity-sdk';
36
40
  import { getExpectedTokenBalanceAfterBorrow, getKtokenToTokenSwapper, getTokenToKtokenSwapper } from './utils';
41
+ import { FullBPS } from '@kamino-finance/kliquidity-sdk/dist/utils/CreationParameters';
42
+ import {
43
+ AdjustLeverageCalcsResult,
44
+ AdjustLeverageInitialInputs,
45
+ AdjustLeverageIxsResponse,
46
+ AdjustLeverageProps,
47
+ AdjustLeverageSwapInputsProps,
48
+ DepositLeverageCalcsResult,
49
+ DepositLeverageInitialInputs,
50
+ DepositWithLeverageProps,
51
+ DepositWithLeverageSwapInputsProps,
52
+ DepsoitLeverageIxsResponse,
53
+ PriceAinBProvider,
54
+ SwapInputs,
55
+ SwapQuoteIxs,
56
+ SwapQuoteIxsProvider,
57
+ WithdrawLeverageCalcsResult,
58
+ WithdrawLeverageInitialInputs,
59
+ WithdrawLeverageIxsResponse,
60
+ WithdrawWithLeverageProps,
61
+ WithdrawWithLeverageSwapInputsProps,
62
+ } from './types';
63
+
64
+ export async function getDepositWithLeverageSwapInputs<QuoteResponse>({
65
+ owner,
66
+ kaminoMarket,
67
+ debtTokenMint,
68
+ collTokenMint,
69
+ depositAmount,
70
+ priceDebtToColl,
71
+ slippagePct,
72
+ obligation,
73
+ referrer,
74
+ currentSlot,
75
+ targetLeverage,
76
+ selectedTokenMint,
77
+ kamino,
78
+ obligationTypeTagOverride,
79
+ scopeFeed,
80
+ budgetAndPriorityFeeIxs,
81
+ quoteBufferBps,
82
+ priceAinB,
83
+ isKtoken,
84
+ quoter,
85
+ }: DepositWithLeverageSwapInputsProps<QuoteResponse>): Promise<{
86
+ swapInputs: SwapInputs;
87
+ initialInputs: DepositLeverageInitialInputs<QuoteResponse>;
88
+ }> {
89
+ const collReserve = kaminoMarket.getReserveByMint(collTokenMint);
90
+ const debtReserve = kaminoMarket.getReserveByMint(debtTokenMint);
91
+ const solTokenReserve = kaminoMarket.getReserveByMint(WRAPPED_SOL_MINT);
92
+ const flashLoanFee = collReserve!.getFlashLoanFee() || new Decimal(0);
37
93
 
38
- export type SwapIxnsProvider = (
39
- amountInLamports: number,
40
- inputMint: PublicKey,
41
- outputMint: PublicKey,
42
- slippagePct: number,
43
- amountDebtAtaBalance?: Decimal
44
- ) => Promise<[TransactionInstruction[], PublicKey[]]>;
45
-
46
- export type SwapQuoteProvider<QuoteResponse> = (
47
- inputs: SwapInputs,
48
- klendAccounts: Array<PublicKey>
49
- ) => Promise<SwapQuote<QuoteResponse>>;
50
-
51
- export type SwapQuoteIxsProvider<QuoteResponse> = (
52
- inputs: SwapInputs,
53
- klendAccounts: Array<PublicKey>,
54
- quote: SwapQuote<QuoteResponse>
55
- ) => Promise<SwapQuoteIxs>;
56
-
57
- export type SwapQuote<QuoteResponse> = {
58
- priceAInB: Decimal;
59
- quoteResponse?: QuoteResponse;
60
- };
61
-
62
- export type SwapQuoteIxs = {
63
- preActionIxs: TransactionInstruction[];
64
- swapIxs: TransactionInstruction[];
65
- lookupTables: AddressLookupTableAccount[];
66
- };
67
-
68
- export type PriceAinBProvider = (mintA: PublicKey, mintB: PublicKey) => Promise<Decimal>;
69
-
70
- export type IsKtokenProvider = (token: PublicKey | string) => Promise<boolean>;
71
-
72
- export type SwapInputs = {
73
- inputAmountLamports: Decimal;
74
- inputMint: PublicKey;
75
- outputMint: PublicKey;
76
- };
77
-
78
- export type KaminoDepositSwapOverride = (
79
- kaminoMarket: KaminoMarket,
80
- kamino: Kamino,
81
- depositor: PublicKey,
82
- amountInMint: PublicKey,
83
- amountOutMint: PublicKey,
84
- amountIn: Decimal,
85
- slippageFactor: Decimal,
86
- amountDebtAtaBalance: Decimal
87
- ) => Promise<InstructionsWithLookupTables>;
88
-
89
- export const depositLeverageCalcs = (props: {
90
- depositAmount: Decimal;
91
- depositTokenIsCollToken: boolean;
92
- depositTokenIsSol: boolean;
93
- priceDebtToColl: Decimal;
94
- targetLeverage: Decimal;
95
- slippagePct: Decimal;
96
- flashLoanFee: Decimal;
97
- }): {
98
- flashBorrowInCollToken: Decimal;
99
- initDepositInSol: Decimal;
100
- debtTokenToBorrow: Decimal;
101
- collTokenToDeposit: Decimal;
102
- swapDebtTokenIn: Decimal;
103
- swapCollTokenExpectedOut: Decimal;
104
- } => {
105
- // Initialize local variables from the props object
106
- const {
94
+ const selectedTokenIsCollToken = selectedTokenMint.equals(collTokenMint);
95
+ const depositTokenIsSol = !solTokenReserve ? false : selectedTokenMint.equals(solTokenReserve!.getLiquidityMint());
96
+
97
+ const collIsKtoken = await isKtoken(collTokenMint);
98
+ const strategy = collIsKtoken ? (await kamino!.getStrategyByKTokenMint(collTokenMint))! : undefined;
99
+
100
+ const calcs = await getDepositWithLeverageCalcs(
107
101
  depositAmount,
108
- depositTokenIsCollToken,
102
+ selectedTokenIsCollToken,
103
+ collIsKtoken,
109
104
  depositTokenIsSol,
110
105
  priceDebtToColl,
111
106
  targetLeverage,
112
107
  slippagePct,
113
108
  flashLoanFee,
114
- } = props;
115
- const slippage = slippagePct.div('100');
116
-
117
- const initDepositInSol = depositTokenIsSol ? depositAmount : new Decimal(0);
118
-
119
- // Core logic
120
- if (depositTokenIsCollToken) {
121
- const y = targetLeverage.mul(priceDebtToColl);
122
- const x = flashLoanFee.add('1').mul(slippage.add('1')).div(priceDebtToColl);
123
- const finalColl = depositAmount.mul(x).div(x.sub(targetLeverage.sub('1').div(y)));
124
- const debt = finalColl.sub(depositAmount).mul(x);
125
- const flashBorrowColl = finalColl.sub(depositAmount).mul(flashLoanFee.add('1'));
126
-
127
- return {
128
- flashBorrowInCollToken: flashBorrowColl,
129
- initDepositInSol,
130
- debtTokenToBorrow: debt,
131
- collTokenToDeposit: finalColl,
132
- swapDebtTokenIn: debt,
133
- swapCollTokenExpectedOut: finalColl.sub(depositAmount),
134
- };
135
- } else {
136
- const y = targetLeverage.mul(priceDebtToColl);
137
- const x = flashLoanFee.add('1').mul(slippage.add('1')).div(priceDebtToColl);
138
- const finalColl = depositAmount.div(x.sub(targetLeverage.sub('1').div(y)));
139
- const flashBorrowColl = finalColl.mul(flashLoanFee.add('1'));
140
- const debt = targetLeverage.sub('1').mul(finalColl).div(y);
141
-
142
- return {
143
- flashBorrowInCollToken: flashBorrowColl,
144
- initDepositInSol,
145
- debtTokenToBorrow: debt,
146
- collTokenToDeposit: finalColl,
147
- swapDebtTokenIn: debt.add(depositAmount),
148
- swapCollTokenExpectedOut: finalColl,
149
- };
150
- }
151
- };
152
-
153
- export const depositLeverageKtokenCalcs = async (props: {
154
- kamino: Kamino;
155
- strategy: StrategyWithAddress;
156
- debtTokenMint: PublicKey;
157
- depositAmount: Decimal;
158
- depositTokenIsCollToken: boolean;
159
- depositTokenIsSol: boolean;
160
- priceDebtToColl: Decimal;
161
- targetLeverage: Decimal;
162
- slippagePct: Decimal;
163
- flashLoanFee: Decimal;
164
- priceAinB: PriceAinBProvider;
165
- strategyHoldings?: TokenAmounts;
166
- }): Promise<{
167
- flashBorrowInDebtToken: Decimal;
168
- initDepositInSol: Decimal;
169
- collTokenToDeposit: Decimal;
170
- debtTokenToBorrow: Decimal; // debtTokenToBorrow = flashBorrowInDebtToken + flashLoanFee
171
- requiredCollateral: Decimal;
172
- singleSidedDeposit: Decimal;
173
- }> => {
174
- const {
175
109
  kamino,
176
110
  strategy,
177
111
  debtTokenMint,
178
- depositAmount,
179
- depositTokenIsCollToken,
180
- depositTokenIsSol,
181
- priceDebtToColl,
182
- targetLeverage,
183
- slippagePct,
184
- flashLoanFee,
185
112
  priceAinB,
186
- strategyHoldings,
187
- } = props;
188
- const initDepositInSol = depositTokenIsSol ? depositAmount : new Decimal(0);
189
- const slippage = slippagePct.div('100');
190
-
191
- let flashBorrowInDebtToken: Decimal;
192
- let collTokenToDeposit: Decimal;
193
- let debtTokenToBorrow: Decimal;
194
-
195
- if (depositTokenIsCollToken) {
196
- const x = slippage.add('1').div(priceDebtToColl);
197
- const y = flashLoanFee.add('1').mul(priceDebtToColl);
198
- const z = targetLeverage.mul(y).div(targetLeverage.sub(1));
199
- flashBorrowInDebtToken = depositAmount.div(z.minus(new Decimal(1).div(x)));
200
- collTokenToDeposit = depositAmount.add(flashBorrowInDebtToken.div(x));
201
- debtTokenToBorrow = flashBorrowInDebtToken.mul(new Decimal(1).add(flashLoanFee));
113
+ debtReserve!
114
+ );
202
115
 
203
- return {
204
- flashBorrowInDebtToken,
205
- initDepositInSol,
206
- collTokenToDeposit,
207
- debtTokenToBorrow,
208
- requiredCollateral: collTokenToDeposit.sub(depositAmount), // Assuming netValue is requiredCollateral, adjust as needed
209
- singleSidedDeposit: flashBorrowInDebtToken,
210
- };
211
- } else {
212
- const y = targetLeverage.mul(priceDebtToColl);
213
- // 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
214
- // This also allows for some variation in the pool ratios between calculation + submitting the tx
215
- const x = flashLoanFee.add('1').mul(slippage.add('1')).div(priceDebtToColl);
216
- // 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
217
- const finalColl = depositAmount.div(x.sub(targetLeverage.sub('1').div(y)));
218
- // Calculate how many A and B tokens we will need to actually mint the desired amount of ktoken collateral
219
- // The actual amount of ktokens received may be less than the finalColl due to smart proportional contract logic
220
- // So we use the actualColl as the amount we will deposit
221
- const [estimatedA, estimatedB, actualColl] = await simulateMintKToken(
222
- kamino!,
223
- strategy!,
224
- finalColl,
225
- strategyHoldings
226
- );
227
- const pxAinB = await priceAinB(strategy!.strategy.tokenAMint, strategy!.strategy.tokenBMint);
228
- const isTokenADeposit = strategy.strategy.tokenAMint.equals(debtTokenMint);
229
- // Calculate the amount we need to flash borrow by combining value of A and B into the debt token
230
- const singleSidedDepositAmount = isTokenADeposit
231
- ? estimatedA.add(estimatedB.div(pxAinB))
232
- : estimatedB.add(estimatedA.mul(pxAinB));
233
-
234
- // Add slippage to the entire amount, add flash loan fee to part we will flash borrow
235
- flashBorrowInDebtToken = singleSidedDepositAmount
236
- .div(new Decimal('1').sub(slippage))
237
- .sub(depositAmount)
238
- .div(new Decimal('1').sub(flashLoanFee));
239
- // Deposit the min ktoken amount we calculated at the beginning
240
- // Any slippage will be left in the user's wallet as ktokens
241
- collTokenToDeposit = actualColl;
242
- debtTokenToBorrow = flashBorrowInDebtToken.div(new Decimal('1').sub(flashLoanFee));
243
- // Add slippage to ensure we try to swap/deposit as much as possible after flash loan fees
244
- const singleSidedDeposit = singleSidedDepositAmount.div(new Decimal('1').sub(slippage));
116
+ console.log('Ops Calcs', toJson(calcs));
245
117
 
246
- return {
247
- flashBorrowInDebtToken,
248
- initDepositInSol,
249
- collTokenToDeposit,
250
- debtTokenToBorrow,
251
- requiredCollateral: collTokenToDeposit, // Assuming collTokenToDeposit is requiredCollateral, adjust as needed
252
- singleSidedDeposit,
253
- };
118
+ let obligationType: ObligationType;
119
+ if (obligationTypeTagOverride === ObligationTypeTag.Multiply) {
120
+ // multiply
121
+ obligationType = new MultiplyObligation(collTokenMint, debtTokenMint, kaminoMarket.programId);
122
+ } else if (obligationTypeTagOverride === ObligationTypeTag.Leverage) {
123
+ // leverage
124
+ obligationType = new LeverageObligation(collTokenMint, debtTokenMint, kaminoMarket.programId);
125
+ } else {
126
+ throw Error('Obligation type tag not supported for leverage, please use 1 - multiply or 3 - leverage');
254
127
  }
255
- };
256
-
257
- export const getDepositWithLeverageSwapInputs = (props: {
258
- depositAmount: Decimal;
259
- priceDebtToColl: Decimal;
260
- slippagePct: Decimal;
261
- targetLeverage: Decimal;
262
- kaminoMarket: KaminoMarket;
263
- selectedTokenMint: PublicKey;
264
- debtTokenMint: PublicKey;
265
- collTokenMint: PublicKey;
266
- }): {
267
- swapInputs: SwapInputs;
268
- } => {
269
- const {
270
- depositAmount,
271
- priceDebtToColl,
272
- slippagePct,
273
- targetLeverage,
128
+
129
+ // Build the repay & withdraw collateral tx to get the number of accounts
130
+ const klendIxs = await buildDepositWithLeverageIxns(
274
131
  kaminoMarket,
275
- selectedTokenMint,
276
- debtTokenMint,
277
- collTokenMint,
278
- } = props;
279
- const debtReserve = kaminoMarket.getReserveByMint(debtTokenMint);
280
- const selectedTokenIsCollToken = selectedTokenMint.equals(collTokenMint);
281
- const solTokenReserve = kaminoMarket.getReserveByMint(WRAPPED_SOL_MINT);
282
- const depositTokenIsSol = !solTokenReserve ? false : selectedTokenMint.equals(solTokenReserve!.getLiquidityMint());
283
- const flashLoanFee = debtReserve?.getFlashLoanFee() || new Decimal(0);
132
+ debtReserve!,
133
+ collReserve!,
134
+ owner,
135
+ obligation ? obligation : obligationType,
136
+ referrer,
137
+ currentSlot,
138
+ depositTokenIsSol,
139
+ scopeFeed,
140
+ calcs,
141
+ budgetAndPriorityFeeIxs,
142
+ {
143
+ preActionIxs: [],
144
+ swapIxs: [],
145
+ lookupTables: [],
146
+ },
147
+ strategy,
148
+ collIsKtoken
149
+ );
150
+
151
+ const uniqueKlendAccounts = uniqueAccounts(klendIxs);
152
+
153
+ const swapInputAmount = toLamports(
154
+ !collIsKtoken ? calcs.swapDebtTokenIn : calcs.singleSidedDepositKtokenOnly,
155
+ debtReserve!.stats.decimals
156
+ ).ceil();
157
+
158
+ const swapInputsForQuote: SwapInputs = {
159
+ inputAmountLamports: swapInputAmount.mul(new Decimal(1).add(quoteBufferBps.div(FullBPS))),
160
+ inputMint: debtTokenMint,
161
+ outputMint: collTokenMint,
162
+ amountDebtAtaBalance: new Decimal(0), // Only needed for ktokens swaps
163
+ };
164
+
165
+ const swapQuote = await quoter(swapInputsForQuote, uniqueKlendAccounts);
284
166
 
285
- const calcs = depositLeverageCalcs({
167
+ const quotePriceCalcs = await getDepositWithLeverageCalcs(
286
168
  depositAmount,
287
- depositTokenIsCollToken: selectedTokenIsCollToken,
169
+ selectedTokenIsCollToken,
170
+ collIsKtoken,
288
171
  depositTokenIsSol,
289
- priceDebtToColl,
172
+ swapQuote.priceAInB,
290
173
  targetLeverage,
291
174
  slippagePct,
292
175
  flashLoanFee,
293
- });
176
+ kamino,
177
+ strategy,
178
+ debtTokenMint,
179
+ priceAinB,
180
+ debtReserve!
181
+ );
182
+
183
+ const swapInputAmountQuotePrice = toLamports(
184
+ !collIsKtoken ? quotePriceCalcs.swapDebtTokenIn : quotePriceCalcs.singleSidedDepositKtokenOnly,
185
+ debtReserve!.stats.decimals
186
+ ).ceil();
187
+
188
+ let expectedDebtTokenAtaBalance = new Decimal(0);
189
+
190
+ if (collIsKtoken) {
191
+ let futureBalanceInAta = new Decimal(0);
192
+ if (debtTokenMint.equals(WRAPPED_SOL_MINT)) {
193
+ futureBalanceInAta = futureBalanceInAta.add(
194
+ !collIsKtoken ? quotePriceCalcs.initDepositInSol : quotePriceCalcs.initDepositInSol
195
+ );
196
+ }
197
+ futureBalanceInAta = futureBalanceInAta.add(
198
+ !collIsKtoken ? quotePriceCalcs.debtTokenToBorrow : quotePriceCalcs.flashBorrowInDebtTokenKtokenOnly
199
+ );
200
+ expectedDebtTokenAtaBalance = await getExpectedTokenBalanceAfterBorrow(
201
+ kaminoMarket.getConnection(),
202
+ debtTokenMint,
203
+ owner,
204
+ toLamports(futureBalanceInAta.toDecimalPlaces(debtReserve!.stats.decimals), debtReserve!.stats.decimals),
205
+ debtReserve!.state.liquidity.mintDecimals.toNumber()
206
+ );
207
+ }
294
208
 
295
209
  return {
296
210
  swapInputs: {
297
- inputAmountLamports: toLamports(
298
- calcs.swapDebtTokenIn,
299
- debtReserve!.state.liquidity.mintDecimals.toNumber()
300
- ).ceil(),
211
+ inputAmountLamports: swapInputAmountQuotePrice,
301
212
  inputMint: debtTokenMint,
302
213
  outputMint: collTokenMint,
214
+ amountDebtAtaBalance: expectedDebtTokenAtaBalance,
215
+ },
216
+ initialInputs: {
217
+ calcs: quotePriceCalcs,
218
+ swapQuote,
219
+ currentSlot,
220
+ collIsKtoken,
221
+ strategy,
222
+ obligation: obligation ? obligation : obligationType,
223
+ klendAccounts: uniqueKlendAccounts,
303
224
  },
304
225
  };
305
- };
306
-
307
- export const getDepositWithLeverageIxns = async (props: {
308
- connection: Connection;
309
- budgetAndPriorityFeeIxns?: TransactionInstruction[];
310
- user: PublicKey;
311
- amount: Decimal;
312
- selectedTokenMint: PublicKey;
313
- collTokenMint: PublicKey;
314
- debtTokenMint: PublicKey;
315
- targetLeverage: Decimal;
316
- kaminoMarket: KaminoMarket;
317
- slippagePct: Decimal;
318
- priceDebtToColl: Decimal;
319
- swapper: SwapIxnsProvider;
320
- referrer: PublicKey;
321
- isKtoken: IsKtokenProvider;
322
- priceAinB: PriceAinBProvider;
323
- kamino: Kamino | undefined;
324
- obligationTypeTagOverride: ObligationTypeTag;
325
- obligation: KaminoObligation | null;
326
- currentSlot: number;
327
- getTotalKlendAccountsOnly: boolean;
328
- scopeFeed?: string;
329
- }): Promise<{
330
- ixns: TransactionInstruction[];
331
- lookupTablesAddresses: PublicKey[];
332
- swapInputs: SwapInputs;
333
- totalKlendAccounts: number;
334
- }> => {
335
- const {
336
- connection,
337
- budgetAndPriorityFeeIxns,
338
- user,
339
- amount,
340
- selectedTokenMint,
341
- collTokenMint,
342
- debtTokenMint,
343
- targetLeverage,
344
- kaminoMarket,
345
- slippagePct,
346
- priceDebtToColl,
347
- swapper,
348
- referrer,
349
- isKtoken,
350
- priceAinB,
351
- kamino,
352
- obligationTypeTagOverride = 1,
353
- obligation,
354
- currentSlot,
355
- getTotalKlendAccountsOnly,
356
- scopeFeed,
357
- } = props;
358
- const collReserve = kaminoMarket.getReserveByMint(collTokenMint);
359
- const debtReserve = kaminoMarket.getReserveByMint(debtTokenMint);
360
- const solTokenReserve = kaminoMarket.getReserveByMint(WRAPPED_SOL_MINT);
361
- const flashLoanFee = collReserve!.getFlashLoanFee() || new Decimal(0);
362
-
363
- const selectedTokenIsCollToken = selectedTokenMint.equals(collTokenMint);
364
- const depositTokenIsSol = !solTokenReserve ? false : selectedTokenMint.equals(solTokenReserve!.getLiquidityMint());
365
-
366
- const collIsKtoken = await isKtoken(collTokenMint);
367
- const strategy = collIsKtoken ? (await kamino!.getStrategyByKTokenMint(collTokenMint))! : undefined;
368
-
369
- const calcs = depositLeverageCalcs({
370
- depositAmount: amount,
371
- depositTokenIsCollToken: selectedTokenIsCollToken,
372
- depositTokenIsSol,
373
- priceDebtToColl,
374
- targetLeverage,
375
- slippagePct,
376
- flashLoanFee,
377
- });
378
- let calcsKtoken;
379
- if (collIsKtoken) {
380
- calcsKtoken = await depositLeverageKtokenCalcs({
226
+ }
227
+
228
+ async function getDepositWithLeverageCalcs(
229
+ depositAmount: Decimal,
230
+ selectedTokenIsCollToken: boolean,
231
+ collIsKtoken: boolean,
232
+ depositTokenIsSol: boolean,
233
+ priceDebtToColl: Decimal,
234
+ targetLeverage: Decimal,
235
+ slippagePct: Decimal,
236
+ flashLoanFee: Decimal,
237
+ kamino: Kamino | undefined,
238
+ strategy: StrategyWithAddress | undefined,
239
+ debtTokenMint: PublicKey,
240
+ priceAinB: PriceAinBProvider,
241
+ debtReserve: KaminoReserve
242
+ ): Promise<DepositLeverageCalcsResult> {
243
+ let calcs: DepositLeverageCalcsResult;
244
+ if (!collIsKtoken) {
245
+ calcs = depositLeverageCalcs({
246
+ depositAmount: depositAmount,
247
+ depositTokenIsCollToken: selectedTokenIsCollToken,
248
+ depositTokenIsSol,
249
+ priceDebtToColl,
250
+ targetLeverage,
251
+ slippagePct,
252
+ flashLoanFee,
253
+ });
254
+ } else {
255
+ calcs = await depositLeverageKtokenCalcs({
381
256
  kamino: kamino!,
382
257
  strategy: strategy!,
383
258
  debtTokenMint,
384
- depositAmount: amount,
259
+ depositAmount: depositAmount,
385
260
  depositTokenIsCollToken: selectedTokenIsCollToken,
386
261
  depositTokenIsSol,
387
262
  priceDebtToColl,
@@ -391,37 +266,160 @@ export const getDepositWithLeverageIxns = async (props: {
391
266
  priceAinB,
392
267
  });
393
268
  // Rounding to exact number of decimals so this value is passed through in all calcs without rounding inconsistencies
394
- calcsKtoken.flashBorrowInDebtToken = calcsKtoken.flashBorrowInDebtToken.toDecimalPlaces(
395
- debtReserve?.state.liquidity.mintDecimals.toNumber()!,
269
+ calcs.flashBorrowInDebtTokenKtokenOnly = calcs.flashBorrowInDebtTokenKtokenOnly.toDecimalPlaces(
270
+ debtReserve!.state.liquidity.mintDecimals.toNumber()!,
396
271
  Decimal.ROUND_CEIL
397
272
  );
398
- calcsKtoken.debtTokenToBorrow = calcsKtoken.debtTokenToBorrow.toDecimalPlaces(
399
- debtReserve?.state.liquidity.mintDecimals.toNumber()!,
273
+ calcs.debtTokenToBorrow = calcs.debtTokenToBorrow.toDecimalPlaces(
274
+ debtReserve!.state.liquidity.mintDecimals.toNumber()!,
400
275
  Decimal.ROUND_CEIL
401
276
  );
402
- calcsKtoken.singleSidedDeposit = calcsKtoken.singleSidedDeposit.toDecimalPlaces(
403
- debtReserve?.state.liquidity.mintDecimals.toNumber()!,
277
+ calcs.singleSidedDepositKtokenOnly = calcs.singleSidedDepositKtokenOnly.toDecimalPlaces(
278
+ debtReserve!.state.liquidity.mintDecimals.toNumber()!,
404
279
  Decimal.ROUND_CEIL
405
280
  );
406
281
  }
282
+ return calcs;
283
+ }
284
+
285
+ export async function getDepositWithLeverageIxns<QuoteResponse>({
286
+ owner,
287
+ kaminoMarket,
288
+ debtTokenMint,
289
+ collTokenMint,
290
+ depositAmount,
291
+ priceDebtToColl,
292
+ slippagePct,
293
+ obligation,
294
+ referrer,
295
+ currentSlot,
296
+ targetLeverage,
297
+ selectedTokenMint,
298
+ kamino,
299
+ obligationTypeTagOverride,
300
+ scopeFeed,
301
+ budgetAndPriorityFeeIxs,
302
+ quoteBufferBps,
303
+ priceAinB,
304
+ isKtoken,
305
+ quoter,
306
+ swapper,
307
+ }: DepositWithLeverageProps<QuoteResponse>): Promise<DepsoitLeverageIxsResponse<QuoteResponse>> {
308
+ const { swapInputs, initialInputs } = await getDepositWithLeverageSwapInputs({
309
+ owner,
310
+ kaminoMarket,
311
+ debtTokenMint,
312
+ collTokenMint,
313
+ depositAmount,
314
+ priceDebtToColl,
315
+ slippagePct,
316
+ obligation,
317
+ referrer,
318
+ currentSlot,
319
+ targetLeverage,
320
+ selectedTokenMint,
321
+ kamino,
322
+ obligationTypeTagOverride,
323
+ scopeFeed,
324
+ budgetAndPriorityFeeIxs,
325
+ quoteBufferBps,
326
+ priceAinB,
327
+ isKtoken,
328
+ quoter,
329
+ });
330
+
331
+ let depositSwapper: SwapQuoteIxsProvider<QuoteResponse>;
332
+
333
+ if (!initialInputs.collIsKtoken) {
334
+ depositSwapper = swapper;
335
+ } else {
336
+ if (kamino === undefined) {
337
+ throw Error('Ktoken use as collateral for leverage without Kamino instance');
338
+ }
339
+ depositSwapper = await getTokenToKtokenSwapper(kaminoMarket, kamino, owner, slippagePct, swapper, priceAinB, false);
340
+ }
407
341
 
408
- console.log('Ops Calcs', toJson(!collIsKtoken ? calcs : calcsKtoken));
409
- console.log(
410
- 'Infos',
411
- toJson({
412
- depositTokenIsSol,
413
- selectedTokenIsCollToken,
414
- initDepositInSol: calcs.initDepositInSol,
415
- })
342
+ const { swapIxs, lookupTables } = await depositSwapper(
343
+ swapInputs,
344
+ initialInputs.klendAccounts,
345
+ initialInputs.swapQuote
416
346
  );
417
347
 
348
+ if (initialInputs.collIsKtoken) {
349
+ if (initialInputs.strategy!.strategy.strategyLookupTable) {
350
+ const strategyLut = await getLookupTableAccount(
351
+ kaminoMarket.getConnection(),
352
+ initialInputs.strategy!.strategy.strategyLookupTable!
353
+ );
354
+ lookupTables.push(strategyLut!);
355
+ } else {
356
+ console.log('Strategy lookup table not found');
357
+ }
358
+ }
359
+
360
+ const collReserve = kaminoMarket.getReserveByMint(collTokenMint);
361
+ const debtReserve = kaminoMarket.getReserveByMint(debtTokenMint);
362
+ const solTokenReserve = kaminoMarket.getReserveByMint(WRAPPED_SOL_MINT);
363
+ const depositTokenIsSol = !solTokenReserve ? false : selectedTokenMint.equals(solTokenReserve!.getLiquidityMint());
364
+
365
+ const ixs = await buildDepositWithLeverageIxns(
366
+ kaminoMarket,
367
+ debtReserve!,
368
+ collReserve!,
369
+ owner,
370
+ initialInputs.obligation,
371
+ referrer,
372
+ currentSlot,
373
+ depositTokenIsSol,
374
+ scopeFeed,
375
+ initialInputs.calcs,
376
+ budgetAndPriorityFeeIxs,
377
+ {
378
+ preActionIxs: [],
379
+ swapIxs: swapIxs,
380
+ lookupTables: lookupTables,
381
+ },
382
+ initialInputs.strategy,
383
+ initialInputs.collIsKtoken
384
+ );
385
+
386
+ return {
387
+ ixs,
388
+ lookupTables,
389
+ swapInputs,
390
+ initialInputs,
391
+ };
392
+ }
393
+
394
+ async function buildDepositWithLeverageIxns(
395
+ market: KaminoMarket,
396
+ debtReserve: KaminoReserve,
397
+ collReserve: KaminoReserve,
398
+ owner: PublicKey,
399
+ obligation: KaminoObligation | ObligationType | undefined,
400
+ referrer: PublicKey,
401
+ currentSlot: number,
402
+ depositTokenIsSol: boolean,
403
+ scopeFeed: string | undefined,
404
+ calcs: DepositLeverageCalcsResult,
405
+ budgetAndPriorityFeeIxs: TransactionInstruction[] | undefined,
406
+ swapQuoteIxs: SwapQuoteIxs,
407
+ strategy: StrategyWithAddress | undefined,
408
+ collIsKtoken: boolean
409
+ ): Promise<TransactionInstruction[]> {
410
+ const budgetIxns = budgetAndPriorityFeeIxs || getComputeBudgetAndPriorityFeeIxns(3000000);
411
+ const collTokenMint = collReserve.getLiquidityMint();
412
+ const debtTokenMint = debtReserve.getLiquidityMint();
413
+ const collTokenAta = getAssociatedTokenAddressSync(collTokenMint, owner);
414
+ const debtTokenAta = getAssociatedTokenAddressSync(debtTokenMint, owner);
415
+
418
416
  // 1. Create atas & budget txns
419
417
  let mintsToCreateAtas: Array<{ mint: PublicKey; tokenProgram: PublicKey }>;
420
418
  if (collIsKtoken) {
421
419
  const secondTokenAta = strategy!.strategy.tokenAMint.equals(debtTokenMint)
422
420
  ? strategy!.strategy.tokenBMint
423
421
  : strategy!.strategy.tokenAMint;
424
- const secondTokenTokenProgarm = strategy?.strategy.tokenAMint.equals(debtTokenMint)
422
+ const secondTokenTokenProgarm = strategy!.strategy.tokenAMint.equals(debtTokenMint)
425
423
  ? strategy!.strategy.tokenBTokenProgram.equals(PublicKey.default)
426
424
  ? TOKEN_PROGRAM_ID
427
425
  : strategy!.strategy.tokenBTokenProgram
@@ -431,14 +429,14 @@ export const getDepositWithLeverageIxns = async (props: {
431
429
  mintsToCreateAtas = [
432
430
  {
433
431
  mint: collTokenMint,
434
- tokenProgram: collReserve!.getLiquidityTokenProgram(),
432
+ tokenProgram: collReserve.getLiquidityTokenProgram(),
435
433
  },
436
434
  {
437
435
  mint: debtTokenMint,
438
- tokenProgram: debtReserve!.getLiquidityTokenProgram(),
436
+ tokenProgram: debtReserve.getLiquidityTokenProgram(),
439
437
  },
440
438
  {
441
- mint: collReserve!.getCTokenMint(),
439
+ mint: collReserve.getCTokenMint(),
442
440
  tokenProgram: TOKEN_PROGRAM_ID,
443
441
  },
444
442
  {
@@ -450,11 +448,11 @@ export const getDepositWithLeverageIxns = async (props: {
450
448
  mintsToCreateAtas = [
451
449
  {
452
450
  mint: collTokenMint,
453
- tokenProgram: collReserve!.getLiquidityTokenProgram(),
451
+ tokenProgram: collReserve.getLiquidityTokenProgram(),
454
452
  },
455
453
  {
456
454
  mint: debtTokenMint,
457
- tokenProgram: debtReserve!.getLiquidityTokenProgram(),
455
+ tokenProgram: debtReserve.getLiquidityTokenProgram(),
458
456
  },
459
457
  {
460
458
  mint: collReserve!.getCTokenMint(),
@@ -463,75 +461,51 @@ export const getDepositWithLeverageIxns = async (props: {
463
461
  ];
464
462
  }
465
463
 
466
- const budgetIxns = budgetAndPriorityFeeIxns || getComputeBudgetAndPriorityFeeIxns(3000000);
467
- const {
468
- atas: [collTokenAta, debtTokenAta],
469
- createAtaIxs,
470
- } = await getAtasWithCreateIxnsIfMissing(connection, user, mintsToCreateAtas);
464
+ const atasAndCreateIxns = createAtasIdempotent(owner, mintsToCreateAtas);
471
465
 
472
- // TODO: this needs to work the other way around also
473
- // TODO: marius test this with shorting leverage and with leverage looping
474
466
  const fillWsolAtaIxns: TransactionInstruction[] = [];
475
467
  if (depositTokenIsSol) {
476
468
  fillWsolAtaIxns.push(
477
469
  ...getDepositWsolIxns(
478
- user,
479
- selectedTokenIsCollToken ? collTokenAta : debtTokenAta,
480
- toLamports(calcs.initDepositInSol, solTokenReserve!.stats.decimals).ceil()
470
+ owner,
471
+ getAssociatedTokenAddressSync(WRAPPED_SOL_MINT, owner),
472
+ toLamports(calcs.initDepositInSol, SOL_DECIMALS).ceil()
481
473
  )
482
474
  );
483
475
  }
484
476
 
485
- // 1. Flash borrow & repay the collateral amount needed for given leverage
477
+ // 2. Flash borrow & repay the collateral amount needed for given leverage
486
478
  // if user deposits coll, then we borrow the diff, else we borrow the entire amount
487
479
  const { flashBorrowIxn, flashRepayIxn } = getFlashLoanInstructions({
488
- borrowIxnIndex: budgetIxns.length + createAtaIxs.length + fillWsolAtaIxns.length,
489
- walletPublicKey: user,
490
- lendingMarketAuthority: kaminoMarket.getLendingMarketAuthority(),
491
- lendingMarketAddress: kaminoMarket.getAddress(),
492
- reserve: !collIsKtoken ? collReserve! : debtReserve!,
480
+ borrowIxnIndex: budgetIxns.length + atasAndCreateIxns.length + fillWsolAtaIxns.length,
481
+ walletPublicKey: owner,
482
+ lendingMarketAuthority: market.getLendingMarketAuthority(),
483
+ lendingMarketAddress: market.getAddress(),
484
+ reserve: !collIsKtoken ? collReserve : debtReserve,
493
485
  amountLamports: toLamports(
494
- !collIsKtoken ? calcs.flashBorrowInCollToken : calcsKtoken!.flashBorrowInDebtToken,
495
- !collIsKtoken ? collReserve!.stats.decimals : debtReserve!.stats.decimals
486
+ !collIsKtoken ? calcs.flashBorrowInCollToken : calcs.flashBorrowInDebtTokenKtokenOnly,
487
+ !collIsKtoken ? collReserve.stats.decimals : debtReserve.stats.decimals
496
488
  ),
497
489
  destinationAta: !collIsKtoken ? collTokenAta : debtTokenAta,
498
- referrerAccount: kaminoMarket.programId,
499
- referrerTokenState: kaminoMarket.programId,
500
- programId: kaminoMarket.programId,
490
+ referrerAccount: market.programId,
491
+ referrerTokenState: market.programId,
492
+ programId: market.programId,
501
493
  });
502
494
 
503
- console.log(
504
- 'Borrowing: ',
505
- toLamports(!collIsKtoken ? calcs.debtTokenToBorrow : calcsKtoken!.debtTokenToBorrow, debtReserve!.stats.decimals)
506
- .ceil()
507
- .toString()
508
- );
509
495
  // 3. Deposit initial tokens + borrowed tokens into reserve
510
- let obligationType: ObligationType;
511
- if (obligationTypeTagOverride === ObligationTypeTag.Multiply) {
512
- // multiply
513
- obligationType = new MultiplyObligation(collTokenMint, debtTokenMint, kaminoMarket.programId);
514
- } else if (obligationTypeTagOverride === ObligationTypeTag.Leverage) {
515
- // leverage
516
- obligationType = new LeverageObligation(collTokenMint, debtTokenMint, kaminoMarket.programId);
517
- } else {
518
- throw Error('Obligation type tag not supported for leverage, please use 1 - multiply or 3 - leverage');
519
- }
520
-
521
496
  const scopeRefresh = scopeFeed ? { includeScopeRefresh: true, scopeFeed: scopeFeed } : undefined;
522
-
523
497
  const kaminoDepositAndBorrowAction = await KaminoAction.buildDepositAndBorrowTxns(
524
- kaminoMarket,
525
- toLamports(!collIsKtoken ? calcs.collTokenToDeposit : calcsKtoken!.collTokenToDeposit, collReserve!.stats.decimals)
498
+ market,
499
+ toLamports(!collIsKtoken ? calcs.collTokenToDeposit : calcs.collTokenToDeposit, collReserve.stats.decimals)
526
500
  .floor()
527
501
  .toString(),
528
502
  collTokenMint,
529
- toLamports(!collIsKtoken ? calcs.debtTokenToBorrow : calcsKtoken!.debtTokenToBorrow, debtReserve!.stats.decimals)
503
+ toLamports(!collIsKtoken ? calcs.debtTokenToBorrow : calcs.debtTokenToBorrow, debtReserve.stats.decimals)
530
504
  .ceil()
531
505
  .toString(),
532
506
  debtTokenMint,
533
- user,
534
- obligation ? obligation : obligationType,
507
+ owner,
508
+ obligation!,
535
509
  0,
536
510
  false,
537
511
  true, // emode
@@ -541,353 +515,310 @@ export const getDepositWithLeverageIxns = async (props: {
541
515
  scopeRefresh
542
516
  );
543
517
 
544
- console.log(
545
- 'Expected to swap in',
546
- !collIsKtoken ? calcs.swapDebtTokenIn.toNumber().toString() : calcsKtoken!.singleSidedDeposit.toNumber().toString(),
547
- 'debt for',
548
- !collIsKtoken ? calcs.swapCollTokenExpectedOut.toString() : calcsKtoken!.requiredCollateral.toNumber().toString(),
549
- 'coll'
550
- );
551
-
552
- const ixns = [
553
- ...budgetIxns,
554
- ...createAtaIxs,
555
- ...fillWsolAtaIxns,
556
- ...[flashBorrowIxn],
557
- ...kaminoDepositAndBorrowAction.setupIxs,
558
- ...[kaminoDepositAndBorrowAction.lendingIxs[0]],
559
- ...kaminoDepositAndBorrowAction.inBetweenIxs,
560
- ...[kaminoDepositAndBorrowAction.lendingIxs[1]],
561
- ...kaminoDepositAndBorrowAction.cleanupIxs,
562
- ...[flashRepayIxn],
563
- ];
564
-
565
- const uniqueAccounts = new PublicKeySet<PublicKey>([]);
566
- ixns.forEach((ixn) => {
567
- ixn.keys.forEach((key) => {
568
- uniqueAccounts.add(key.pubkey);
569
- });
570
- });
571
- const totalKlendAccounts = uniqueAccounts.toArray().length;
572
-
573
- // return early to avoid extra swapper calls
574
- if (getTotalKlendAccountsOnly) {
575
- return {
576
- ixns: [],
577
- lookupTablesAddresses: [],
578
- swapInputs: {
579
- inputAmountLamports: new Decimal('0'),
580
- inputMint: PublicKey.default,
581
- outputMint: PublicKey.default,
582
- },
583
- totalKlendAccounts: totalKlendAccounts,
584
- };
585
- }
586
-
587
- let depositSwapper: SwapIxnsProvider;
588
- let expectedDebtTokenAtaBalance: Decimal = new Decimal(0); // only needed for kTokens
518
+ // 4. Swap
519
+ const { swapIxs } = swapQuoteIxs;
520
+ const swapInstructions = removeBudgetAndAtaIxns(swapIxs, []);
589
521
 
590
522
  if (!collIsKtoken) {
591
- depositSwapper = swapper;
592
- } else {
593
- if (kamino === undefined) {
594
- throw Error('Ktoken use as collateral for leverage without Kamino instance');
595
- }
596
- depositSwapper = await getTokenToKtokenSwapper(connection, kaminoMarket, kamino, user, swapper, priceAinB, false);
597
-
598
- let futureBalanceInAta = new Decimal(0);
599
- if (debtTokenMint.equals(WRAPPED_SOL_MINT)) {
600
- futureBalanceInAta = futureBalanceInAta.add(
601
- !collIsKtoken ? calcs.initDepositInSol : calcsKtoken!.initDepositInSol
602
- );
603
- }
604
- futureBalanceInAta = futureBalanceInAta.add(
605
- !collIsKtoken ? calcs.debtTokenToBorrow : calcsKtoken!.flashBorrowInDebtToken
606
- );
607
- expectedDebtTokenAtaBalance = await getExpectedTokenBalanceAfterBorrow(
608
- connection,
609
- debtTokenMint,
610
- user,
611
- toLamports(futureBalanceInAta.toDecimalPlaces(debtReserve!.stats.decimals), debtReserve!.stats.decimals),
612
- debtReserve!.state.liquidity.mintDecimals.toNumber()
613
- );
614
- }
615
-
616
- const swapInputs: SwapInputs = {
617
- inputAmountLamports: toLamports(
618
- !collIsKtoken ? calcs.swapDebtTokenIn : calcsKtoken!.singleSidedDeposit,
619
- debtReserve!.stats.decimals
620
- ).ceil(),
621
- inputMint: debtTokenMint,
622
- outputMint: collTokenMint,
623
- };
624
-
625
- const [swapIxns, lookupTablesAddresses] = await depositSwapper(
626
- swapInputs.inputAmountLamports.toNumber(),
627
- swapInputs.inputMint,
628
- swapInputs.outputMint,
629
- slippagePct.toNumber(),
630
- expectedDebtTokenAtaBalance
631
- );
632
-
633
- if (collIsKtoken) {
634
- if (strategy?.strategy.strategyLookupTable) {
635
- lookupTablesAddresses.push(strategy?.strategy.strategyLookupTable!);
636
- } else {
637
- console.log('Strategy lookup table not found');
638
- }
639
- }
640
-
641
- const swapInstructions = removeBudgetAndAtaIxns(swapIxns, []);
642
-
643
- if (!collIsKtoken) {
644
- return {
645
- ixns: [
646
- ...budgetIxns,
647
- ...createAtaIxs,
648
- ...fillWsolAtaIxns,
649
- ...[flashBorrowIxn],
650
- ...kaminoDepositAndBorrowAction.setupIxs,
651
- ...[kaminoDepositAndBorrowAction.lendingIxs[0]],
652
- ...kaminoDepositAndBorrowAction.inBetweenIxs,
653
- ...[kaminoDepositAndBorrowAction.lendingIxs[1]],
654
- ...kaminoDepositAndBorrowAction.cleanupIxs,
655
- ...swapInstructions,
656
- ...[flashRepayIxn],
657
- ],
658
- lookupTablesAddresses,
659
- swapInputs,
660
- totalKlendAccounts: totalKlendAccounts,
661
- };
523
+ return [
524
+ ...budgetIxns,
525
+ ...atasAndCreateIxns.map((x) => x.createAtaIx),
526
+ ...fillWsolAtaIxns,
527
+ ...[flashBorrowIxn],
528
+ ...kaminoDepositAndBorrowAction.setupIxs,
529
+ ...[kaminoDepositAndBorrowAction.lendingIxs[0]],
530
+ ...kaminoDepositAndBorrowAction.inBetweenIxs,
531
+ ...[kaminoDepositAndBorrowAction.lendingIxs[1]],
532
+ ...kaminoDepositAndBorrowAction.cleanupIxs,
533
+ ...swapInstructions,
534
+ ...[flashRepayIxn],
535
+ ];
662
536
  } else {
663
- return {
664
- ixns: [
665
- ...budgetIxns,
666
- ...createAtaIxs,
667
- ...fillWsolAtaIxns,
668
- ...[flashBorrowIxn],
669
- ...swapInstructions,
670
- ...kaminoDepositAndBorrowAction.setupIxs,
671
- ...[kaminoDepositAndBorrowAction.lendingIxs[0]],
672
- ...kaminoDepositAndBorrowAction.inBetweenIxs,
673
- ...[kaminoDepositAndBorrowAction.lendingIxs[1]],
674
- ...kaminoDepositAndBorrowAction.cleanupIxs,
675
- ...[flashRepayIxn],
676
- ],
677
- lookupTablesAddresses,
678
- swapInputs,
679
- totalKlendAccounts: totalKlendAccounts,
680
- };
537
+ return [
538
+ ...budgetIxns,
539
+ ...atasAndCreateIxns.map((x) => x.createAtaIx),
540
+ ...fillWsolAtaIxns,
541
+ ...[flashBorrowIxn],
542
+ ...swapInstructions,
543
+ ...kaminoDepositAndBorrowAction.setupIxs,
544
+ ...[kaminoDepositAndBorrowAction.lendingIxs[0]],
545
+ ...kaminoDepositAndBorrowAction.inBetweenIxs,
546
+ ...[kaminoDepositAndBorrowAction.lendingIxs[1]],
547
+ ...kaminoDepositAndBorrowAction.cleanupIxs,
548
+ ...[flashRepayIxn],
549
+ ];
681
550
  }
682
- };
683
-
684
- export const getWithdrawWithLeverageSwapInputs = (props: {
685
- amount: Decimal;
686
- deposited: Decimal;
687
- borrowed: Decimal;
688
- priceCollToDebt: Decimal;
689
- slippagePct: number;
690
- isClosingPosition: boolean;
691
- kaminoMarket: KaminoMarket;
692
- selectedTokenMint: PublicKey;
693
- debtTokenMint: PublicKey;
694
- collTokenMint: PublicKey;
695
- userObligation: KaminoObligation;
696
- currentSlot: number;
697
- }): {
551
+ }
552
+
553
+ export async function getWithdrawWithLeverageSwapInputs<QuoteResponse>({
554
+ owner,
555
+ kaminoMarket,
556
+ debtTokenMint,
557
+ collTokenMint,
558
+ deposited,
559
+ borrowed,
560
+ obligation,
561
+ referrer,
562
+ currentSlot,
563
+ withdrawAmount,
564
+ priceCollToDebt,
565
+ slippagePct,
566
+ isClosingPosition,
567
+ selectedTokenMint,
568
+ budgetAndPriorityFeeIxs,
569
+ kamino,
570
+ scopeFeed,
571
+ quoteBufferBps,
572
+ isKtoken,
573
+ quoter,
574
+ }: WithdrawWithLeverageSwapInputsProps<QuoteResponse>): Promise<{
698
575
  swapInputs: SwapInputs;
699
- } => {
700
- const {
701
- amount,
576
+ initialInputs: WithdrawLeverageInitialInputs<QuoteResponse>;
577
+ }> {
578
+ const collReserve = kaminoMarket.getReserveByMint(collTokenMint);
579
+ const debtReserve = kaminoMarket.getReserveByMint(debtTokenMint);
580
+ const flashLoanFee = debtReserve!.getFlashLoanFee() || new Decimal(0);
581
+ const selectedTokenIsCollToken = selectedTokenMint.equals(collTokenMint);
582
+ const collIsKtoken = await isKtoken(collTokenMint);
583
+ const strategy = collIsKtoken ? (await kamino!.getStrategyByKTokenMint(collTokenMint))! : undefined;
584
+
585
+ const solTokenReserve = kaminoMarket.getReserveByMint(WRAPPED_SOL_MINT);
586
+ const depositTokenIsSol = !solTokenReserve ? false : selectedTokenMint.equals(solTokenReserve!.getLiquidityMint());
587
+
588
+ const calcs = withdrawLeverageCalcs(
589
+ kaminoMarket,
590
+ collReserve!,
591
+ debtReserve!,
592
+ priceCollToDebt,
593
+ withdrawAmount,
702
594
  deposited,
703
595
  borrowed,
704
- priceCollToDebt,
705
- slippagePct,
596
+ currentSlot,
706
597
  isClosingPosition,
707
- kaminoMarket,
598
+ selectedTokenIsCollToken,
708
599
  selectedTokenMint,
709
- debtTokenMint,
710
- collTokenMint,
711
- userObligation,
600
+ obligation,
601
+ flashLoanFee,
602
+ slippagePct
603
+ );
604
+
605
+ const klendIxs = await buildWithdrawWithLeverageIxns(
606
+ kaminoMarket,
607
+ debtReserve!,
608
+ collReserve!,
609
+ owner,
610
+ obligation,
611
+ referrer,
712
612
  currentSlot,
713
- } = props;
714
- const collReserve = kaminoMarket.getReserveByMint(collTokenMint);
715
- const debtReserve = kaminoMarket.getReserveByMint(debtTokenMint);
716
- const flashLoanFee = debtReserve?.getFlashLoanFee() || new Decimal(0);
717
- const selectedTokenIsCollToken = selectedTokenMint.equals(collTokenMint);
613
+ isClosingPosition,
614
+ depositTokenIsSol,
615
+ scopeFeed,
616
+ calcs,
617
+ budgetAndPriorityFeeIxs,
618
+ {
619
+ preActionIxs: [],
620
+ swapIxs: [],
621
+ lookupTables: [],
622
+ },
623
+ strategy,
624
+ collIsKtoken
625
+ );
718
626
 
719
- const { adjustDepositPosition: withdrawAmount, adjustBorrowPosition: initialRepayAmount } = isClosingPosition
720
- ? { adjustDepositPosition: deposited, adjustBorrowPosition: borrowed }
721
- : calcWithdrawAmounts({
722
- collTokenMint: collTokenMint,
723
- priceCollToDebt: new Decimal(priceCollToDebt),
724
- currentDepositPosition: deposited,
725
- currentBorrowPosition: borrowed,
726
- withdrawAmount: new Decimal(amount),
727
- selectedTokenMint: selectedTokenMint,
728
- });
627
+ const uniqueKlendAccounts = uniqueAccounts(klendIxs);
729
628
 
730
- const irSlippageBpsForDebt = userObligation
731
- .estimateObligationInterestRate(kaminoMarket, debtReserve!, userObligation.state.borrows[0]!, currentSlot)
732
- .toDecimalPlaces(debtReserve?.state.liquidity.mintDecimals.toNumber()!, Decimal.ROUND_CEIL);
629
+ const swapInputAmount = toLamports(
630
+ calcs.collTokenSwapIn,
631
+ collReserve!.state.liquidity.mintDecimals.toNumber()
632
+ ).ceil();
733
633
 
734
- const repayAmount = initialRepayAmount
735
- .mul(irSlippageBpsForDebt.add('0.1').div('10_000').add('1'))
736
- .toDecimalPlaces(debtReserve?.state.liquidity.mintDecimals.toNumber()!, Decimal.ROUND_CEIL);
634
+ const swapInputsForQuote: SwapInputs = {
635
+ inputAmountLamports: swapInputAmount.mul(new Decimal(1).add(quoteBufferBps.div(FullBPS))),
636
+ inputMint: collTokenMint,
637
+ outputMint: debtTokenMint,
638
+ amountDebtAtaBalance: new Decimal(0), // Only needed for ktokens deposits
639
+ };
737
640
 
738
- const swapAmountIfWithdrawingColl = repayAmount
739
- .mul(new Decimal(1).plus(flashLoanFee))
740
- .mul(new Decimal(1 + slippagePct / 100))
741
- .div(priceCollToDebt);
641
+ const swapQuote = await quoter(swapInputsForQuote, uniqueKlendAccounts);
742
642
 
743
- const swapAmountIfWithdrawingDebt = withdrawAmount;
643
+ const calcsQuotePrice = withdrawLeverageCalcs(
644
+ kaminoMarket,
645
+ collReserve!,
646
+ debtReserve!,
647
+ collIsKtoken ? swapQuote.priceAInB : priceCollToDebt,
648
+ withdrawAmount,
649
+ deposited,
650
+ borrowed,
651
+ currentSlot,
652
+ isClosingPosition,
653
+ selectedTokenIsCollToken,
654
+ selectedTokenMint,
655
+ obligation,
656
+ flashLoanFee,
657
+ slippagePct
658
+ );
744
659
 
745
- const collTokenSwapIn = selectedTokenIsCollToken ? swapAmountIfWithdrawingColl : swapAmountIfWithdrawingDebt;
660
+ const swapInputAmountQuotePrice = toLamports(
661
+ calcsQuotePrice.collTokenSwapIn,
662
+ collReserve!.state.liquidity.mintDecimals.toNumber()
663
+ ).ceil();
746
664
 
747
665
  return {
748
666
  swapInputs: {
749
- inputAmountLamports: toLamports(collTokenSwapIn, collReserve!.state.liquidity.mintDecimals.toNumber()).ceil(),
667
+ inputAmountLamports: swapInputAmountQuotePrice,
750
668
  inputMint: collTokenMint,
751
669
  outputMint: debtTokenMint,
670
+ amountDebtAtaBalance: new Decimal(0), // Only needed for ktokens deposits
671
+ },
672
+ initialInputs: {
673
+ calcs: calcsQuotePrice,
674
+ swapQuote,
675
+ currentSlot,
676
+ collIsKtoken,
677
+ strategy,
678
+ obligation,
679
+ klendAccounts: uniqueKlendAccounts,
752
680
  },
753
681
  };
754
- };
755
-
756
- export const getWithdrawWithLeverageIxns = async (props: {
757
- connection: Connection;
758
- budgetAndPriorityFeeIxns: TransactionInstruction[];
759
- user: PublicKey;
760
- amount: Decimal;
761
- deposited: Decimal;
762
- borrowed: Decimal;
763
- collTokenMint: PublicKey;
764
- debtTokenMint: PublicKey;
765
- priceCollToDebt: Decimal;
766
- selectedTokenMint: PublicKey;
767
- isClosingPosition: boolean;
768
- kaminoMarket: KaminoMarket;
769
- slippagePct: number;
770
- swapper: SwapIxnsProvider;
771
- referrer: PublicKey;
772
- isKtoken: IsKtokenProvider;
773
- kamino: Kamino | undefined;
774
- obligationTypeTagOverride: ObligationTypeTag;
775
- obligation: KaminoObligation | null;
776
- currentSlot: number;
777
- getTotalKlendAccountsOnly: boolean;
778
- scopeFeed?: string;
779
- }): Promise<{
780
- ixns: TransactionInstruction[];
781
- lookupTablesAddresses: PublicKey[];
782
- swapInputs: SwapInputs;
783
- totalKlendAccounts: number;
784
- }> => {
785
- const {
786
- connection,
787
- budgetAndPriorityFeeIxns,
788
- user,
789
- amount,
682
+ }
683
+
684
+ export async function getWithdrawWithLeverageIxns<QuoteResponse>({
685
+ owner,
686
+ kaminoMarket,
687
+ debtTokenMint,
688
+ collTokenMint,
689
+ obligation,
690
+ deposited,
691
+ borrowed,
692
+ referrer,
693
+ currentSlot,
694
+ withdrawAmount,
695
+ priceCollToDebt,
696
+ slippagePct,
697
+ isClosingPosition,
698
+ selectedTokenMint,
699
+ budgetAndPriorityFeeIxs,
700
+ kamino,
701
+ scopeFeed,
702
+ quoteBufferBps,
703
+ isKtoken,
704
+ quoter,
705
+ swapper,
706
+ }: WithdrawWithLeverageProps<QuoteResponse>): Promise<WithdrawLeverageIxsResponse<QuoteResponse>> {
707
+ const collReserve = kaminoMarket.getReserveByMint(collTokenMint);
708
+ const debtReserve = kaminoMarket.getReserveByMint(debtTokenMint);
709
+
710
+ const solTokenReserve = kaminoMarket.getReserveByMint(WRAPPED_SOL_MINT);
711
+ const depositTokenIsSol = !solTokenReserve ? false : selectedTokenMint.equals(solTokenReserve!.getLiquidityMint());
712
+ const { swapInputs, initialInputs } = await getWithdrawWithLeverageSwapInputs({
713
+ owner,
714
+ kaminoMarket,
715
+ debtTokenMint,
716
+ collTokenMint,
790
717
  deposited,
791
718
  borrowed,
792
- collTokenMint,
793
- debtTokenMint,
719
+ obligation,
720
+ referrer,
721
+ currentSlot,
722
+ withdrawAmount,
794
723
  priceCollToDebt,
795
- selectedTokenMint,
796
- isClosingPosition,
797
- kaminoMarket,
798
724
  slippagePct,
799
- swapper,
800
- referrer,
801
- isKtoken,
725
+ isClosingPosition,
726
+ selectedTokenMint,
727
+ budgetAndPriorityFeeIxs,
802
728
  kamino,
803
- obligationTypeTagOverride,
804
- obligation,
805
- currentSlot,
806
- getTotalKlendAccountsOnly,
807
729
  scopeFeed,
808
- } = props;
809
-
810
- const collReserve = kaminoMarket.getReserveByMint(collTokenMint);
811
- const debtReserve = kaminoMarket.getReserveByMint(debtTokenMint);
812
- const flashLoanFee = debtReserve?.getFlashLoanFee() || new Decimal(0);
813
- const collIsKtoken = await isKtoken(collTokenMint);
730
+ quoteBufferBps,
731
+ isKtoken,
732
+ quoter,
733
+ });
814
734
 
815
- const solTokenReserve = kaminoMarket.getReserveByMint(WRAPPED_SOL_MINT);
816
- const selectedTokenIsCollToken = selectedTokenMint.equals(collTokenMint);
817
- const depositTokenIsSol = !solTokenReserve ? false : selectedTokenMint.equals(solTokenReserve!.getLiquidityMint());
735
+ let withdrawSwapper: SwapQuoteIxsProvider<QuoteResponse>;
818
736
 
819
- let obligationType: ObligationType;
820
- if (obligationTypeTagOverride == ObligationTypeTag.Multiply) {
821
- // multiply
822
- obligationType = new MultiplyObligation(collTokenMint, debtTokenMint, kaminoMarket.programId);
823
- } else if (obligationTypeTagOverride == ObligationTypeTag.Leverage) {
824
- // leverage
825
- obligationType = new LeverageObligation(collTokenMint, debtTokenMint, kaminoMarket.programId);
737
+ if (initialInputs.collIsKtoken) {
738
+ if (kamino === undefined) {
739
+ throw Error('Ktoken use as collateral for leverage without Kamino instance');
740
+ }
741
+ withdrawSwapper = await getKtokenToTokenSwapper(kaminoMarket, kamino, owner, swapper);
826
742
  } else {
827
- throw Error(
828
- `Obligation type tag ${obligationTypeTagOverride} not supported for leverage, please use multiply (1) or leverage (3) obligation type`
829
- );
743
+ withdrawSwapper = swapper;
744
+ }
745
+
746
+ const { swapIxs, lookupTables } = await withdrawSwapper(
747
+ swapInputs,
748
+ initialInputs.klendAccounts,
749
+ initialInputs.swapQuote
750
+ );
751
+
752
+ if (initialInputs.collIsKtoken) {
753
+ if (initialInputs.strategy!.strategy.strategyLookupTable) {
754
+ const strategyLut = await getLookupTableAccount(
755
+ kaminoMarket.getConnection(),
756
+ initialInputs.strategy!.strategy.strategyLookupTable!
757
+ );
758
+ lookupTables.push(strategyLut!);
759
+ } else {
760
+ console.log('Strategy lookup table not found');
761
+ }
830
762
  }
831
763
 
832
- // 1. Calculate coll_amount and debt_amount to repay such that we maintain leverage and we withdraw to
833
- // the wallet `amountInDepositTokenToWithdrawToWallet` amount of collateral token
834
- // We need to withdraw withdrawAmountInDepositToken coll tokens
835
- // and repay repayAmountInBorrowToken debt tokens
836
- // TODO marius: do the same in useDeposit
837
- const { adjustDepositPosition: withdrawAmount, adjustBorrowPosition: initialRepayAmount } = isClosingPosition
838
- ? { adjustDepositPosition: deposited, adjustBorrowPosition: borrowed }
839
- : calcWithdrawAmounts({
840
- collTokenMint: collTokenMint,
841
- priceCollToDebt: new Decimal(priceCollToDebt),
842
- currentDepositPosition: deposited,
843
- currentBorrowPosition: borrowed,
844
- withdrawAmount: new Decimal(amount),
845
- selectedTokenMint: selectedTokenMint,
846
- });
847
-
848
- // Add slippage for the accrued interest rate amount
849
- const userObligation = obligation
850
- ? obligation
851
- : await kaminoMarket.getObligationByAddress(obligationType.toPda(kaminoMarket.getAddress(), user));
852
- const irSlippageBpsForDebt = userObligation!
853
- .estimateObligationInterestRate(kaminoMarket, debtReserve!, userObligation?.state.borrows[0]!, currentSlot)
854
- .toDecimalPlaces(debtReserve?.state.liquidity.mintDecimals.toNumber()!, Decimal.ROUND_CEIL);
855
- // add 0.1 to irSlippageBpsForDebt because we don't want to estimate slightly less than SC and end up not reapying enough
856
- const repayAmount = initialRepayAmount
857
- .mul(irSlippageBpsForDebt.add('0.1').div('10_000').add('1'))
858
- .toDecimalPlaces(debtReserve?.state.liquidity.mintDecimals.toNumber()!, Decimal.ROUND_CEIL);
859
-
860
- // 6. Get swap ixns
861
- // 5. Get swap estimations to understand how much we need to borrow from borrow reserve
862
- // prevent withdrawing more then deposited if we close position
863
- const depositTokenWithdrawAmount = !isClosingPosition
864
- ? withdrawAmount.mul(new Decimal(1).plus(flashLoanFee))
865
- : withdrawAmount;
866
-
867
- // We are swapping debt token
868
- // When withdrawing coll, it means we just need to swap enough to pay for the flash borrow
869
- const swapAmountIfWithdrawingColl = repayAmount
870
- .mul(new Decimal(1).plus(flashLoanFee))
871
- .mul(new Decimal(1 + slippagePct / 100))
872
- .div(priceCollToDebt);
873
-
874
- // When withdrawing debt, it means we need to swap just the collateral we are withdrwaing
875
- // enough to cover the debt we are repaying, leaving the remaining in the wallet
876
- const swapAmountIfWithdrawingDebt = withdrawAmount;
877
-
878
- const collTokenSwapIn = selectedTokenIsCollToken ? swapAmountIfWithdrawingColl : swapAmountIfWithdrawingDebt;
879
- const debtTokenExpectedSwapOut = collTokenSwapIn.mul(priceCollToDebt).div(new Decimal(1 + slippagePct / 100));
880
-
881
- const strategy = collIsKtoken ? await kamino?.getStrategyByKTokenMint(collTokenMint) : undefined;
882
-
883
- console.log('Expecting to swap', collTokenSwapIn.toString(), 'coll for', debtTokenExpectedSwapOut.toString(), 'debt');
764
+ const ixs = await buildWithdrawWithLeverageIxns(
765
+ kaminoMarket,
766
+ debtReserve!,
767
+ collReserve!,
768
+ owner,
769
+ obligation,
770
+ referrer,
771
+ currentSlot,
772
+ isClosingPosition,
773
+ depositTokenIsSol,
774
+ scopeFeed,
775
+ initialInputs.calcs,
776
+ budgetAndPriorityFeeIxs,
777
+ {
778
+ preActionIxs: [],
779
+ swapIxs,
780
+ lookupTables,
781
+ },
782
+ initialInputs.strategy,
783
+ initialInputs.collIsKtoken
784
+ );
785
+
786
+ // Send ixns and lookup tables
787
+ return {
788
+ ixs,
789
+ lookupTables,
790
+ swapInputs,
791
+ initialInputs: initialInputs,
792
+ };
793
+ }
794
+
795
+ export async function buildWithdrawWithLeverageIxns(
796
+ market: KaminoMarket,
797
+ debtReserve: KaminoReserve,
798
+ collReserve: KaminoReserve,
799
+ owner: PublicKey,
800
+ obligation: KaminoObligation,
801
+ referrer: PublicKey,
802
+ currentSlot: number,
803
+ isClosingPosition: boolean,
804
+ depositTokenIsSol: boolean,
805
+ scopeFeed: string | undefined,
806
+ calcs: WithdrawLeverageCalcsResult,
807
+ budgetAndPriorityFeeIxs: TransactionInstruction[] | undefined,
808
+ swapQuoteIxs: SwapQuoteIxs,
809
+ strategy: StrategyWithAddress | undefined,
810
+ collIsKtoken: boolean
811
+ ): Promise<TransactionInstruction[]> {
812
+ const collTokenMint = collReserve.getLiquidityMint();
813
+ const debtTokenMint = debtReserve.getLiquidityMint();
814
+ const debtTokenAta = getAssociatedTokenAddressSync(debtTokenMint, owner);
884
815
  // 1. Create atas & budget txns & user metadata
885
816
  let mintsToCreateAtas: Array<{ mint: PublicKey; tokenProgram: PublicKey }>;
886
817
  if (collIsKtoken) {
887
818
  const secondTokenAta = strategy!.strategy.tokenAMint.equals(debtTokenMint)
888
819
  ? strategy!.strategy.tokenBMint
889
820
  : strategy!.strategy.tokenAMint;
890
- const secondTokenTokenProgram = strategy?.strategy.tokenAMint.equals(debtTokenMint)
821
+ const secondTokenTokenProgram = strategy!.strategy.tokenAMint.equals(debtTokenMint)
891
822
  ? strategy!.strategy.tokenBTokenProgram.equals(PublicKey.default)
892
823
  ? TOKEN_PROGRAM_ID
893
824
  : strategy!.strategy.tokenBTokenProgram
@@ -897,14 +828,14 @@ export const getWithdrawWithLeverageIxns = async (props: {
897
828
  mintsToCreateAtas = [
898
829
  {
899
830
  mint: collTokenMint,
900
- tokenProgram: collReserve!.getLiquidityTokenProgram(),
831
+ tokenProgram: collReserve.getLiquidityTokenProgram(),
901
832
  },
902
833
  {
903
834
  mint: debtTokenMint,
904
- tokenProgram: debtReserve!.getLiquidityTokenProgram(),
835
+ tokenProgram: debtReserve.getLiquidityTokenProgram(),
905
836
  },
906
837
  {
907
- mint: collReserve!.getCTokenMint(),
838
+ mint: collReserve.getCTokenMint(),
908
839
  tokenProgram: TOKEN_PROGRAM_ID,
909
840
  },
910
841
  {
@@ -929,57 +860,59 @@ export const getWithdrawWithLeverageIxns = async (props: {
929
860
  ];
930
861
  }
931
862
 
932
- const {
933
- atas: [, debtTokenAta],
934
- createAtaIxs,
935
- } = await getAtasWithCreateIxnsIfMissing(connection, user, mintsToCreateAtas);
863
+ const atasAndCreateIxns = createAtasIdempotent(owner, mintsToCreateAtas);
936
864
 
937
865
  const closeWsolAtaIxns: TransactionInstruction[] = [];
938
866
  if (depositTokenIsSol || debtTokenMint.equals(WRAPPED_SOL_MINT)) {
939
- const wsolAta = getAssociatedTokenAddress(WRAPPED_SOL_MINT, user, false);
940
- closeWsolAtaIxns.push(createCloseAccountInstruction(wsolAta, user, user, [], TOKEN_PROGRAM_ID));
867
+ const wsolAta = getAssociatedTokenAddress(WRAPPED_SOL_MINT, owner, false);
868
+ closeWsolAtaIxns.push(createCloseAccountInstruction(wsolAta, owner, owner, [], TOKEN_PROGRAM_ID));
941
869
  }
942
870
 
943
- const budgetIxns = budgetAndPriorityFeeIxns || getComputeBudgetAndPriorityFeeIxns(3000000);
871
+ const budgetIxns = budgetAndPriorityFeeIxs || getComputeBudgetAndPriorityFeeIxns(3000000);
944
872
 
945
- // TODO: marius test this with shorting leverage and with leverage looping
873
+ // TODO: Might be worth removing as it's only needed for Ktokens
946
874
  // This is here so that we have enough wsol to repay in case the kAB swapped to sol after estimates is not enough
947
875
  const fillWsolAtaIxns: TransactionInstruction[] = [];
948
876
  if (debtTokenMint.equals(WRAPPED_SOL_MINT)) {
949
- const halfSolBalance = (await connection.getBalance(user)) / LAMPORTS_PER_SOL / 2;
877
+ const halfSolBalance = (await market.getConnection().getBalance(owner)) / LAMPORTS_PER_SOL / 2;
950
878
  const balanceToWrap = halfSolBalance < 0.1 ? halfSolBalance : 0.1;
951
879
  fillWsolAtaIxns.push(
952
- ...getDepositWsolIxns(user, debtTokenAta, toLamports(balanceToWrap, solTokenReserve!.stats.decimals).ceil())
880
+ ...getDepositWsolIxns(
881
+ owner,
882
+ getAssociatedTokenAddressSync(WRAPPED_SOL_MINT, owner),
883
+ toLamports(balanceToWrap, SOL_DECIMALS).ceil()
884
+ )
953
885
  );
954
886
  }
955
887
 
956
888
  // 2. Prepare the flash borrow and flash repay amounts and ixns
957
889
  // We borrow exactly how much we need to repay
958
890
  // and repay that + flash amount fee
959
-
960
891
  const { flashBorrowIxn, flashRepayIxn } = getFlashLoanInstructions({
961
- borrowIxnIndex: budgetIxns.length + createAtaIxs.length + fillWsolAtaIxns.length,
962
- walletPublicKey: user,
963
- lendingMarketAuthority: kaminoMarket.getLendingMarketAuthority(),
964
- lendingMarketAddress: kaminoMarket.getAddress(),
892
+ borrowIxnIndex: budgetIxns.length + atasAndCreateIxns.length + fillWsolAtaIxns.length,
893
+ walletPublicKey: owner,
894
+ lendingMarketAuthority: market.getLendingMarketAuthority(),
895
+ lendingMarketAddress: market.getAddress(),
965
896
  reserve: debtReserve!,
966
- amountLamports: toLamports(repayAmount, debtReserve!.stats.decimals),
897
+ amountLamports: toLamports(calcs.repayAmount, debtReserve!.stats.decimals),
967
898
  destinationAta: debtTokenAta,
968
- referrerAccount: kaminoMarket.programId,
969
- referrerTokenState: kaminoMarket.programId,
970
- programId: kaminoMarket.programId,
899
+ referrerAccount: market.programId,
900
+ referrerTokenState: market.programId,
901
+ programId: market.programId,
971
902
  });
972
903
 
973
904
  // 6. Repay borrowed tokens and Withdraw tokens from reserve that will be swapped to repay flash loan
974
905
  const repayAndWithdrawAction = await KaminoAction.buildRepayAndWithdrawTxns(
975
- kaminoMarket,
976
- isClosingPosition ? U64_MAX : toLamports(repayAmount, debtReserve!.stats.decimals).floor().toString(),
906
+ market,
907
+ isClosingPosition ? U64_MAX : toLamports(calcs.repayAmount, debtReserve!.stats.decimals).floor().toString(),
977
908
  debtTokenMint,
978
- isClosingPosition ? U64_MAX : toLamports(depositTokenWithdrawAmount, collReserve!.stats.decimals).ceil().toString(),
909
+ isClosingPosition
910
+ ? U64_MAX
911
+ : toLamports(calcs.depositTokenWithdrawAmount, collReserve!.stats.decimals).ceil().toString(),
979
912
  collTokenMint,
980
- user,
913
+ owner,
981
914
  currentSlot,
982
- userObligation ? userObligation : obligationType,
915
+ obligation,
983
916
  0,
984
917
  false,
985
918
  false,
@@ -989,76 +922,11 @@ export const getWithdrawWithLeverageIxns = async (props: {
989
922
  { includeScopeRefresh: true, scopeFeed: scopeFeed! }
990
923
  );
991
924
 
992
- const klendIxns = [
993
- ...budgetIxns,
994
- ...createAtaIxs,
995
- ...fillWsolAtaIxns,
996
- ...[flashBorrowIxn],
997
- ...repayAndWithdrawAction.setupIxs,
998
- ...[repayAndWithdrawAction.lendingIxs[0]],
999
- ...repayAndWithdrawAction.inBetweenIxs,
1000
- ...[repayAndWithdrawAction.lendingIxs[1]],
1001
- ...repayAndWithdrawAction.cleanupIxs,
1002
- ...[flashRepayIxn],
1003
- ...closeWsolAtaIxns,
1004
- ];
1005
-
1006
- const uniqueAccs = uniqueAccounts(klendIxns);
1007
- const totalKlendAccounts = uniqueAccs.length;
1008
-
1009
- // return early to avoid extra swapper calls
1010
- if (getTotalKlendAccountsOnly) {
1011
- return {
1012
- ixns: [],
1013
- lookupTablesAddresses: [],
1014
- swapInputs: {
1015
- inputAmountLamports: new Decimal('0'),
1016
- inputMint: PublicKey.default,
1017
- outputMint: PublicKey.default,
1018
- },
1019
- totalKlendAccounts: totalKlendAccounts,
1020
- };
1021
- }
1022
-
1023
- let withdrawSwapper: SwapIxnsProvider;
1024
-
1025
- if (collIsKtoken) {
1026
- if (kamino === undefined) {
1027
- throw Error('Ktoken use as collateral for leverage without Kamino instance');
1028
- }
1029
- withdrawSwapper = await getKtokenToTokenSwapper(kaminoMarket, kamino, user, swapper);
1030
- } else {
1031
- withdrawSwapper = swapper;
1032
- }
1033
-
1034
- const swapInputs: SwapInputs = {
1035
- inputAmountLamports: toLamports(collTokenSwapIn, collReserve!.stats.decimals).ceil(),
1036
- inputMint: collTokenMint,
1037
- outputMint: debtTokenMint,
1038
- };
1039
-
1040
- const [swapIxns, lookupTablesAddresses] = await withdrawSwapper(
1041
- swapInputs.inputAmountLamports.toNumber(),
1042
- swapInputs.inputMint,
1043
- swapInputs.outputMint,
1044
- slippagePct
1045
- );
1046
-
1047
- // TODO MARIUS: remove first instruction that is setBudget ixn
925
+ const swapInstructions = removeBudgetAndAtaIxns(swapQuoteIxs.swapIxs, []);
1048
926
 
1049
- if (collIsKtoken) {
1050
- if (strategy?.strategy.strategyLookupTable) {
1051
- lookupTablesAddresses.push(strategy?.strategy.strategyLookupTable!);
1052
- } else {
1053
- console.log('Strategy lookup table not found');
1054
- }
1055
- }
1056
-
1057
- const swapInstructions = removeBudgetAndAtaIxns(swapIxns, []);
1058
-
1059
- const ixns = [
927
+ return [
1060
928
  ...budgetIxns,
1061
- ...createAtaIxs,
929
+ ...atasAndCreateIxns.map((x) => x.createAtaIx),
1062
930
  ...fillWsolAtaIxns,
1063
931
  ...[flashBorrowIxn],
1064
932
  ...repayAndWithdrawAction.setupIxs,
@@ -1070,43 +938,48 @@ export const getWithdrawWithLeverageIxns = async (props: {
1070
938
  ...[flashRepayIxn],
1071
939
  ...closeWsolAtaIxns,
1072
940
  ];
1073
-
1074
- // Send ixns and lookup tables
1075
- return {
1076
- ixns,
1077
- lookupTablesAddresses,
1078
- swapInputs,
1079
- totalKlendAccounts: totalKlendAccounts,
1080
- };
1081
- };
1082
-
1083
- export const getAdjustLeverageSwapInputs = (props: {
1084
- deposited: Decimal;
1085
- borrowed: Decimal;
1086
- priceCollToDebt: Decimal;
1087
- priceDebtToColl: Decimal;
1088
- slippagePct: number;
1089
- targetLeverage: Decimal;
1090
- kaminoMarket: KaminoMarket;
1091
- debtTokenMint: PublicKey;
1092
- collTokenMint: PublicKey;
1093
- }): {
941
+ }
942
+
943
+ export async function getAdjustLeverageSwapInputs<QuoteResponse>({
944
+ owner,
945
+ kaminoMarket,
946
+ debtTokenMint,
947
+ collTokenMint,
948
+ obligation,
949
+ depositedLamports,
950
+ borrowedLamports,
951
+ referrer,
952
+ currentSlot,
953
+ targetLeverage,
954
+ priceCollToDebt,
955
+ priceDebtToColl,
956
+ slippagePct,
957
+ budgetAndPriorityFeeIxs,
958
+ kamino,
959
+ scopeFeed,
960
+ quoteBufferBps,
961
+ isKtoken,
962
+ quoter,
963
+ }: AdjustLeverageSwapInputsProps<QuoteResponse>): Promise<{
1094
964
  swapInputs: SwapInputs;
1095
- } => {
1096
- const {
1097
- deposited,
1098
- borrowed,
1099
- priceCollToDebt,
1100
- priceDebtToColl,
1101
- slippagePct,
1102
- targetLeverage,
1103
- kaminoMarket,
1104
- debtTokenMint,
1105
- collTokenMint,
1106
- } = props;
965
+ initialInputs: AdjustLeverageInitialInputs<QuoteResponse>;
966
+ }> {
1107
967
  const collReserve = kaminoMarket.getReserveByMint(collTokenMint);
1108
968
  const debtReserve = kaminoMarket.getReserveByMint(debtTokenMint);
1109
- const flashLoanFee = debtReserve?.getFlashLoanFee() || new Decimal(0);
969
+ const deposited = fromLamports(depositedLamports, collReserve!.stats.decimals);
970
+ const borrowed = fromLamports(borrowedLamports, debtReserve!.stats.decimals);
971
+ const collIsKtoken = await isKtoken(collTokenMint);
972
+ const strategy = collIsKtoken ? (await kamino!.getStrategyByKTokenMint(collTokenMint))! : undefined;
973
+
974
+ // Getting current flash loan fee
975
+ const currentLeverage = obligation.refreshedStats.leverage;
976
+ const isDepositViaLeverage = targetLeverage.gte(new Decimal(currentLeverage));
977
+ let flashLoanFee;
978
+ if (isDepositViaLeverage) {
979
+ flashLoanFee = collReserve!.getFlashLoanFee() || new Decimal(0);
980
+ } else {
981
+ flashLoanFee = debtReserve!.getFlashLoanFee() || new Decimal(0);
982
+ }
1110
983
 
1111
984
  const { adjustDepositPosition, adjustBorrowPosition } = calcAdjustAmounts({
1112
985
  currentDepositPosition: deposited,
@@ -1115,253 +988,376 @@ export const getAdjustLeverageSwapInputs = (props: {
1115
988
  priceCollToDebt: priceCollToDebt,
1116
989
  flashLoanFee: new Decimal(flashLoanFee),
1117
990
  });
991
+
1118
992
  const isDeposit = adjustDepositPosition.gte(0) && adjustBorrowPosition.gte(0);
993
+ if (isDepositViaLeverage !== isDeposit) {
994
+ throw new Error('Invalid target leverage');
995
+ }
1119
996
 
1120
997
  if (isDeposit) {
1121
- const borrowAmount = adjustDepositPosition
1122
- .mul(new Decimal(1).plus(flashLoanFee))
1123
- .mul(new Decimal(1 + slippagePct / 100))
1124
- .div(priceDebtToColl);
998
+ const calcs = await adjustDepositLeverageCalcs(
999
+ kaminoMarket,
1000
+ owner,
1001
+ debtReserve!,
1002
+ adjustDepositPosition,
1003
+ adjustBorrowPosition,
1004
+ priceDebtToColl,
1005
+ flashLoanFee,
1006
+ slippagePct,
1007
+ collIsKtoken
1008
+ );
1009
+
1010
+ // Build the repay & withdraw collateral tx to get the number of accounts
1011
+ const klendIxs = await buildIncreaseLeverageIxns(
1012
+ owner,
1013
+ kaminoMarket,
1014
+ collTokenMint,
1015
+ debtTokenMint,
1016
+ obligation,
1017
+ referrer,
1018
+ currentSlot,
1019
+ calcs,
1020
+ strategy,
1021
+ scopeFeed,
1022
+ collIsKtoken,
1023
+ {
1024
+ preActionIxs: [],
1025
+ swapIxs: [],
1026
+ lookupTables: [],
1027
+ },
1028
+ budgetAndPriorityFeeIxs
1029
+ );
1030
+
1031
+ const uniqueKlendAccounts = uniqueAccounts(klendIxs);
1032
+
1033
+ const swapInputAmount = toLamports(
1034
+ !collIsKtoken ? calcs.borrowAmount : calcs.amountToFlashBorrowDebt,
1035
+ debtReserve!.state.liquidity.mintDecimals.toNumber()
1036
+ ).ceil();
1037
+
1038
+ const swapInputsForQuote: SwapInputs = {
1039
+ inputAmountLamports: swapInputAmount.mul(new Decimal(1).add(quoteBufferBps.div(FullBPS))),
1040
+ inputMint: debtTokenMint,
1041
+ outputMint: collTokenMint,
1042
+ amountDebtAtaBalance: new Decimal(0), // Only needed for ktokens swaps
1043
+ };
1044
+
1045
+ const swapQuote = await quoter(swapInputsForQuote, uniqueKlendAccounts);
1046
+
1047
+ const {
1048
+ adjustDepositPosition: adjustDepositPositionQuotePrice,
1049
+ adjustBorrowPosition: adjustBorrowPositionQuotePrice,
1050
+ } = calcAdjustAmounts({
1051
+ currentDepositPosition: deposited,
1052
+ currentBorrowPosition: borrowed,
1053
+ targetLeverage: targetLeverage,
1054
+ priceCollToDebt: new Decimal(1).div(swapQuote.priceAInB),
1055
+ flashLoanFee: new Decimal(flashLoanFee),
1056
+ });
1057
+
1058
+ const calcsQuotePrice = await adjustDepositLeverageCalcs(
1059
+ kaminoMarket,
1060
+ owner,
1061
+ debtReserve!,
1062
+ adjustDepositPositionQuotePrice,
1063
+ adjustBorrowPositionQuotePrice,
1064
+ swapQuote.priceAInB,
1065
+ flashLoanFee,
1066
+ slippagePct,
1067
+ collIsKtoken
1068
+ );
1069
+
1070
+ const swapInputAmountQuotePrice = toLamports(
1071
+ !collIsKtoken ? calcsQuotePrice.borrowAmount : calcsQuotePrice.amountToFlashBorrowDebt,
1072
+ debtReserve!.state.liquidity.mintDecimals.toNumber()
1073
+ ).ceil();
1074
+
1075
+ let expectedDebtTokenAtaBalance = new Decimal(0);
1076
+ if (collIsKtoken) {
1077
+ expectedDebtTokenAtaBalance = await getExpectedTokenBalanceAfterBorrow(
1078
+ kaminoMarket.getConnection(),
1079
+ debtTokenMint,
1080
+ owner,
1081
+ toLamports(
1082
+ !collIsKtoken ? calcsQuotePrice.borrowAmount : calcsQuotePrice.amountToFlashBorrowDebt,
1083
+ debtReserve!.stats.decimals
1084
+ ).floor(),
1085
+ debtReserve!.state.liquidity.mintDecimals.toNumber()
1086
+ );
1087
+ }
1125
1088
 
1126
1089
  return {
1127
1090
  swapInputs: {
1128
- inputAmountLamports: toLamports(borrowAmount, debtReserve!.state.liquidity.mintDecimals.toNumber()).ceil(),
1091
+ inputAmountLamports: swapInputAmountQuotePrice,
1129
1092
  inputMint: debtTokenMint,
1130
1093
  outputMint: collTokenMint,
1094
+ amountDebtAtaBalance: expectedDebtTokenAtaBalance,
1095
+ },
1096
+ initialInputs: {
1097
+ calcs: calcsQuotePrice,
1098
+ swapQuote,
1099
+ currentSlot,
1100
+ collIsKtoken,
1101
+ strategy,
1102
+ obligation: obligation,
1103
+ klendAccounts: uniqueKlendAccounts,
1104
+ isDeposit: isDeposit,
1131
1105
  },
1132
1106
  };
1133
1107
  } else {
1134
- const withdrawAmountWithSlippageAndFlashLoanFee = Decimal.abs(adjustDepositPosition)
1135
- .mul(new Decimal(1).plus(flashLoanFee))
1136
- .mul(1 + slippagePct / 100);
1108
+ const calcs = adjustWithdrawLeverageCalcs(adjustDepositPosition, adjustBorrowPosition, flashLoanFee, slippagePct);
1109
+
1110
+ const klendIxs = await buildDecreaseLeverageIxns(
1111
+ owner,
1112
+ kaminoMarket,
1113
+ collTokenMint,
1114
+ debtTokenMint,
1115
+ obligation,
1116
+ referrer,
1117
+ currentSlot,
1118
+ calcs,
1119
+ strategy,
1120
+ scopeFeed,
1121
+ collIsKtoken,
1122
+ {
1123
+ preActionIxs: [],
1124
+ swapIxs: [],
1125
+ lookupTables: [],
1126
+ },
1127
+ budgetAndPriorityFeeIxs
1128
+ );
1129
+
1130
+ const uniqueKlendAccounts = uniqueAccounts(klendIxs);
1131
+
1132
+ const swapInputAmount = toLamports(
1133
+ calcs.withdrawAmountWithSlippageAndFlashLoanFee,
1134
+ collReserve!.state.liquidity.mintDecimals.toNumber()
1135
+ ).ceil();
1136
+
1137
+ const swapInputsForQuote: SwapInputs = {
1138
+ inputAmountLamports: swapInputAmount.mul(new Decimal(1).add(quoteBufferBps.div(FullBPS))),
1139
+ inputMint: collTokenMint,
1140
+ outputMint: debtTokenMint,
1141
+ amountDebtAtaBalance: new Decimal(0), // Only needed for ktokens deposits
1142
+ };
1143
+
1144
+ const swapQuote = await quoter(swapInputsForQuote, uniqueKlendAccounts);
1145
+
1146
+ const {
1147
+ adjustDepositPosition: adjustDepositPositionQuotePrice,
1148
+ adjustBorrowPosition: adjustBorrowPositionQuotePrice,
1149
+ } = calcAdjustAmounts({
1150
+ currentDepositPosition: deposited,
1151
+ currentBorrowPosition: borrowed,
1152
+ targetLeverage: targetLeverage,
1153
+ priceCollToDebt: swapQuote.priceAInB,
1154
+ flashLoanFee: new Decimal(flashLoanFee),
1155
+ });
1156
+
1157
+ const calcsQuotePrice = adjustWithdrawLeverageCalcs(
1158
+ adjustDepositPositionQuotePrice,
1159
+ adjustBorrowPositionQuotePrice,
1160
+ flashLoanFee,
1161
+ slippagePct
1162
+ );
1163
+
1164
+ const swapInputAmountQuotePrice = toLamports(
1165
+ calcsQuotePrice.withdrawAmountWithSlippageAndFlashLoanFee,
1166
+ collReserve!.state.liquidity.mintDecimals.toNumber()
1167
+ ).ceil();
1137
1168
 
1138
1169
  return {
1139
1170
  swapInputs: {
1140
- inputAmountLamports: toLamports(
1141
- withdrawAmountWithSlippageAndFlashLoanFee,
1142
- collReserve!.state.liquidity.mintDecimals.toNumber()
1143
- ).ceil(),
1171
+ inputAmountLamports: swapInputAmountQuotePrice,
1144
1172
  inputMint: collTokenMint,
1145
1173
  outputMint: debtTokenMint,
1174
+ amountDebtAtaBalance: new Decimal(0), // Only needed for ktokens deposits
1175
+ },
1176
+ initialInputs: {
1177
+ calcs: calcsQuotePrice,
1178
+ swapQuote,
1179
+ currentSlot,
1180
+ collIsKtoken,
1181
+ strategy,
1182
+ obligation,
1183
+ klendAccounts: uniqueKlendAccounts,
1184
+ isDeposit,
1146
1185
  },
1147
1186
  };
1148
1187
  }
1149
- };
1150
-
1151
- export const getAdjustLeverageIxns = async (props: {
1152
- connection: Connection;
1153
- budgetAndPriorityFeeIxns: TransactionInstruction[];
1154
- user: PublicKey;
1155
- kaminoMarket: KaminoMarket;
1156
- priceDebtToColl: Decimal;
1157
- priceCollToDebt: Decimal;
1158
- targetLeverage: Decimal;
1159
- slippagePct: number;
1160
- depositedLamports: Decimal;
1161
- borrowedLamports: Decimal;
1162
- collTokenMint: PublicKey;
1163
- debtTokenMint: PublicKey;
1164
- swapper: SwapIxnsProvider;
1165
- referrer: PublicKey;
1166
- isKtoken: IsKtokenProvider;
1167
- priceAinB: PriceAinBProvider;
1168
- kamino: Kamino | undefined;
1169
- obligationTypeTagOverride: ObligationTypeTag;
1170
- obligation: KaminoObligation | null;
1171
- currentSlot: number;
1172
- getTotalKlendAccountsOnly: boolean;
1173
- scopeFeed: string | undefined;
1174
- }) => {
1175
- const {
1176
- connection,
1177
- budgetAndPriorityFeeIxns,
1178
- user,
1188
+ }
1189
+
1190
+ export async function getAdjustLeverageIxns<QuoteResponse>({
1191
+ owner,
1192
+ kaminoMarket,
1193
+ debtTokenMint,
1194
+ collTokenMint,
1195
+ obligation,
1196
+ depositedLamports,
1197
+ borrowedLamports,
1198
+ referrer,
1199
+ currentSlot,
1200
+ targetLeverage,
1201
+ priceCollToDebt,
1202
+ priceDebtToColl,
1203
+ slippagePct,
1204
+ budgetAndPriorityFeeIxs,
1205
+ kamino,
1206
+ scopeFeed,
1207
+ quoteBufferBps,
1208
+ priceAinB,
1209
+ isKtoken,
1210
+ quoter,
1211
+ swapper,
1212
+ }: AdjustLeverageProps<QuoteResponse>): Promise<AdjustLeverageIxsResponse<QuoteResponse>> {
1213
+ const { swapInputs, initialInputs } = await getAdjustLeverageSwapInputs({
1214
+ owner,
1179
1215
  kaminoMarket,
1180
- priceDebtToColl,
1181
- priceCollToDebt,
1182
- targetLeverage,
1183
- slippagePct,
1216
+ debtTokenMint,
1217
+ collTokenMint,
1218
+ obligation,
1184
1219
  depositedLamports,
1185
1220
  borrowedLamports,
1186
- collTokenMint,
1187
- debtTokenMint,
1188
- swapper,
1189
1221
  referrer,
1190
- isKtoken,
1191
- priceAinB,
1192
- kamino,
1193
- obligationTypeTagOverride,
1194
- obligation,
1195
1222
  currentSlot,
1196
- getTotalKlendAccountsOnly,
1223
+ targetLeverage,
1224
+ priceCollToDebt,
1225
+ priceDebtToColl,
1226
+ slippagePct,
1227
+ budgetAndPriorityFeeIxs,
1228
+ kamino,
1197
1229
  scopeFeed,
1198
- } = props;
1199
-
1200
- const collReserve = kaminoMarket.getReserveByMint(collTokenMint);
1201
- const debtReserve = kaminoMarket.getReserveByMint(debtTokenMint);
1202
-
1203
- const deposited = fromLamports(depositedLamports, collReserve!.stats.decimals);
1204
- const borrowed = fromLamports(borrowedLamports, debtReserve!.stats.decimals);
1205
- const userObligation = obligation
1206
- ? obligation
1207
- : (await kaminoMarket.getUserObligationsByTag(obligationTypeTagOverride, user)).filter(
1208
- (obligation: KaminoObligation) =>
1209
- obligation.getBorrowByMint(debtReserve!.getLiquidityMint()) !== undefined &&
1210
- obligation.getDepositByMint(collReserve!.getLiquidityMint()) !== undefined
1211
- )[0];
1212
- const currentLeverage = userObligation!.refreshedStats.leverage;
1213
- const isDepositViaLeverage = targetLeverage.gte(new Decimal(currentLeverage));
1214
-
1215
- let flashLoanFee;
1216
- if (isDepositViaLeverage) {
1217
- flashLoanFee = collReserve!.getFlashLoanFee() || new Decimal(0);
1218
- } else {
1219
- flashLoanFee = debtReserve!.getFlashLoanFee() || new Decimal(0);
1220
- }
1221
-
1222
- const { adjustDepositPosition, adjustBorrowPosition } = calcAdjustAmounts({
1223
- currentDepositPosition: deposited,
1224
- currentBorrowPosition: borrowed,
1225
- targetLeverage: targetLeverage,
1226
- priceCollToDebt: priceCollToDebt,
1227
- flashLoanFee: new Decimal(flashLoanFee),
1230
+ quoteBufferBps,
1231
+ priceAinB,
1232
+ isKtoken,
1233
+ quoter,
1228
1234
  });
1229
1235
 
1230
- let ixns: TransactionInstruction[] = [];
1231
- let lookupTablesAddresses: PublicKey[] = [];
1232
- let swapInputs: SwapInputs;
1233
- let totalKlendAccounts: number = 0;
1236
+ // leverage increased so we need to deposit and borrow more
1237
+ if (initialInputs.isDeposit) {
1238
+ let depositSwapper: SwapQuoteIxsProvider<QuoteResponse>;
1239
+
1240
+ if (initialInputs.collIsKtoken) {
1241
+ if (kamino === undefined) {
1242
+ throw Error('Ktoken use as collateral for leverage without Kamino instance');
1243
+ }
1244
+ depositSwapper = await getTokenToKtokenSwapper(
1245
+ kaminoMarket,
1246
+ kamino,
1247
+ owner,
1248
+ slippagePct,
1249
+ swapper,
1250
+ priceAinB,
1251
+ false
1252
+ );
1253
+ } else {
1254
+ depositSwapper = swapper;
1255
+ }
1234
1256
 
1235
- const isDeposit = adjustDepositPosition.gte(0) && adjustBorrowPosition.gte(0);
1236
- if (isDepositViaLeverage !== isDeposit) {
1237
- throw new Error('Invalid target leverage');
1238
- }
1257
+ const { swapIxs, lookupTables } = await depositSwapper(
1258
+ swapInputs,
1259
+ initialInputs.klendAccounts,
1260
+ initialInputs.swapQuote
1261
+ );
1239
1262
 
1240
- // leverage increased so we need to deposit and borrow more
1241
- if (isDeposit) {
1242
- console.log('Increasing leaverage');
1243
1263
  // TODO: marius why are we not using both adjustDepositPosition & adjustBorrowPosition
1244
- const res = await getIncreaseLeverageIxns({
1245
- connection,
1246
- budgetAndPriorityFeeIxns,
1247
- user,
1264
+ const ixs = await buildIncreaseLeverageIxns(
1265
+ owner,
1248
1266
  kaminoMarket,
1249
- depositAmount: adjustDepositPosition,
1250
1267
  collTokenMint,
1251
1268
  debtTokenMint,
1252
- slippagePct,
1253
- priceDebtToColl,
1254
- priceCollToDebt,
1255
- swapper,
1269
+ obligation,
1256
1270
  referrer,
1257
- isKtoken,
1258
- priceAinB,
1259
- kamino,
1260
- obligationTypeTagOverride,
1261
- obligation: userObligation,
1262
1271
  currentSlot,
1263
- getTotalKlendAccountsOnly,
1272
+ initialInputs.calcs,
1273
+ initialInputs.strategy,
1264
1274
  scopeFeed,
1265
- });
1266
- ixns = res.ixns;
1267
- lookupTablesAddresses = res.lookupTablesAddresses;
1268
- swapInputs = res.swapInputs;
1269
- totalKlendAccounts = res.totalKlendAccounts;
1275
+ initialInputs.collIsKtoken,
1276
+ {
1277
+ preActionIxs: [],
1278
+ swapIxs,
1279
+ lookupTables,
1280
+ },
1281
+ budgetAndPriorityFeeIxs
1282
+ );
1283
+ return {
1284
+ ixs,
1285
+ lookupTables,
1286
+ swapInputs,
1287
+ initialInputs,
1288
+ };
1270
1289
  } else {
1271
1290
  console.log('Decreasing leverage');
1272
- const res = await getDecreaseLeverageIxns({
1273
- connection,
1274
- budgetAndPriorityFeeIxns,
1275
- user,
1291
+
1292
+ let withdrawSwapper: SwapQuoteIxsProvider<QuoteResponse>;
1293
+
1294
+ if (initialInputs.collIsKtoken) {
1295
+ if (kamino === undefined) {
1296
+ throw Error('Ktoken use as collateral for leverage without Kamino instance');
1297
+ }
1298
+ withdrawSwapper = await getKtokenToTokenSwapper(kaminoMarket, kamino, owner, swapper);
1299
+ } else {
1300
+ withdrawSwapper = swapper;
1301
+ }
1302
+
1303
+ // 5. Get swap ixns
1304
+ const { swapIxs, lookupTables } = await withdrawSwapper(
1305
+ swapInputs,
1306
+ initialInputs.klendAccounts,
1307
+ initialInputs.swapQuote
1308
+ );
1309
+
1310
+ const ixs = await buildDecreaseLeverageIxns(
1311
+ owner,
1276
1312
  kaminoMarket,
1277
- withdrawAmount: Decimal.abs(adjustDepositPosition),
1278
- repayAmount: Decimal.abs(adjustBorrowPosition),
1279
1313
  collTokenMint,
1280
1314
  debtTokenMint,
1281
- slippagePct,
1282
- swapper,
1315
+ obligation,
1283
1316
  referrer,
1284
- isKtoken,
1285
- kamino,
1286
- obligationTypeTagOverride,
1287
- obligation: userObligation,
1288
1317
  currentSlot,
1289
- getTotalKlendAccountsOnly,
1318
+ initialInputs.calcs,
1319
+ initialInputs.strategy,
1290
1320
  scopeFeed,
1291
- });
1292
- ixns = res.ixns;
1293
- lookupTablesAddresses = res.lookupTablesAddresses;
1294
- swapInputs = res.swapInputs;
1295
- totalKlendAccounts = res.totalKlendAccounts;
1296
- }
1321
+ initialInputs.collIsKtoken,
1322
+ {
1323
+ preActionIxs: [],
1324
+ swapIxs,
1325
+ lookupTables,
1326
+ },
1327
+ budgetAndPriorityFeeIxs
1328
+ );
1297
1329
 
1298
- return {
1299
- ixns,
1300
- lookupTablesAddresses,
1301
- swapInputs,
1302
- totalKlendAccounts,
1303
- };
1304
- };
1330
+ return {
1331
+ ixs,
1332
+ lookupTables,
1333
+ swapInputs,
1334
+ initialInputs,
1335
+ };
1336
+ }
1337
+ }
1305
1338
 
1306
1339
  /**
1307
1340
  * Deposit and borrow tokens if leverage increased
1308
1341
  */
1309
- export const getIncreaseLeverageIxns = async (props: {
1310
- connection: Connection;
1311
- budgetAndPriorityFeeIxns: TransactionInstruction[];
1312
- user: PublicKey;
1313
- kaminoMarket: KaminoMarket;
1314
- depositAmount: Decimal;
1315
- collTokenMint: PublicKey;
1316
- debtTokenMint: PublicKey;
1317
- slippagePct: number;
1318
- priceDebtToColl: Decimal;
1319
- priceCollToDebt: Decimal;
1320
- swapper: SwapIxnsProvider;
1321
- referrer: PublicKey;
1322
- isKtoken: IsKtokenProvider;
1323
- priceAinB: PriceAinBProvider;
1324
- kamino: Kamino | undefined;
1325
- obligationTypeTagOverride: ObligationTypeTag;
1326
- obligation: KaminoObligation | null;
1327
- currentSlot: number;
1328
- getTotalKlendAccountsOnly: boolean;
1329
- scopeFeed: string | undefined;
1330
- }) => {
1331
- const {
1332
- connection,
1333
- budgetAndPriorityFeeIxns,
1334
- user,
1335
- kaminoMarket,
1336
- depositAmount,
1337
- collTokenMint,
1338
- debtTokenMint,
1339
- slippagePct,
1340
- priceDebtToColl,
1341
- priceCollToDebt,
1342
- swapper,
1343
- referrer,
1344
- isKtoken,
1345
- priceAinB,
1346
- kamino,
1347
- obligationTypeTagOverride = 1,
1348
- obligation,
1349
- currentSlot,
1350
- getTotalKlendAccountsOnly,
1351
- scopeFeed,
1352
- } = props;
1342
+ async function buildIncreaseLeverageIxns(
1343
+ owner: PublicKey,
1344
+ kaminoMarket: KaminoMarket,
1345
+ collTokenMint: PublicKey,
1346
+ debtTokenMint: PublicKey,
1347
+ obligation: KaminoObligation,
1348
+ referrer: PublicKey,
1349
+ currentSlot: number,
1350
+ calcs: AdjustLeverageCalcsResult,
1351
+ strategy: StrategyWithAddress | undefined,
1352
+ scopeFeed: string | undefined,
1353
+ collIsKtoken: boolean,
1354
+ swapQuoteIxs: SwapQuoteIxs,
1355
+ budgetAndPriorityFeeIxns: TransactionInstruction[] | undefined
1356
+ ): Promise<TransactionInstruction[]> {
1353
1357
  const collReserve = kaminoMarket.getReserveByMint(collTokenMint);
1354
1358
  const debtReserve = kaminoMarket.getReserveByMint(debtTokenMint);
1355
- const collIsKtoken = await isKtoken(collTokenMint);
1356
-
1357
- const flashLoanFee = collReserve!.getFlashLoanFee() || new Decimal(0);
1358
-
1359
- if (!priceDebtToColl || !priceCollToDebt) {
1360
- throw new Error('Price is not loaded. Please, reload the page and try again');
1361
- }
1362
-
1363
- // TODO: why are we recalculating here again
1364
- const strategy = collIsKtoken ? await kamino?.getStrategyByKTokenMint(collTokenMint) : undefined;
1359
+ const debtTokenAta = getAssociatedTokenAddressSync(debtTokenMint, owner);
1360
+ const collTokenAta = getAssociatedTokenAddressSync(collTokenMint, owner);
1365
1361
 
1366
1362
  // 1. Create atas & budget txns
1367
1363
  const budgetIxns = budgetAndPriorityFeeIxns || getComputeBudgetAndPriorityFeeIxns(3000000);
@@ -1412,28 +1408,17 @@ export const getIncreaseLeverageIxns = async (props: {
1412
1408
  ];
1413
1409
  }
1414
1410
 
1415
- const {
1416
- atas: [collTokenAta, debtTokenAta],
1417
- createAtaIxs,
1418
- } = await getAtasWithCreateIxnsIfMissing(connection, user, mintsToCreateAtas);
1411
+ const atasAndCreateIxns = createAtasIdempotent(owner, mintsToCreateAtas);
1419
1412
 
1420
1413
  // 2. Create borrow flash loan instruction
1421
-
1422
- // used if coll is Ktoken and we borrow debt token instead
1423
- const amountToFashBorrowDebt = depositAmount
1424
- .div(priceDebtToColl)
1425
- .mul(new Decimal(1 + slippagePct / 100))
1426
- .toDecimalPlaces(debtReserve!.stats.decimals, Decimal.ROUND_UP);
1427
- // .toDecimalPlaces(debtReserve?.state.liquidity.mintDecimals.toNumber());
1428
-
1429
1414
  const { flashBorrowIxn, flashRepayIxn } = getFlashLoanInstructions({
1430
- borrowIxnIndex: budgetIxns.length + createAtaIxs.length, // TODO: how about user metadata ixns
1431
- walletPublicKey: user,
1415
+ borrowIxnIndex: budgetIxns.length + atasAndCreateIxns.length, // TODO: how about user metadata ixns
1416
+ walletPublicKey: owner,
1432
1417
  lendingMarketAuthority: kaminoMarket.getLendingMarketAuthority(),
1433
1418
  lendingMarketAddress: kaminoMarket.getAddress(),
1434
1419
  reserve: !collIsKtoken ? collReserve! : debtReserve!,
1435
1420
  amountLamports: toLamports(
1436
- !collIsKtoken ? depositAmount : amountToFashBorrowDebt,
1421
+ !collIsKtoken ? calcs.adjustDepositPosition : calcs.amountToFlashBorrowDebt,
1437
1422
  !collIsKtoken ? collReserve!.stats.decimals : debtReserve!.stats.decimals
1438
1423
  ),
1439
1424
  destinationAta: !collIsKtoken ? collTokenAta : debtTokenAta,
@@ -1442,24 +1427,12 @@ export const getIncreaseLeverageIxns = async (props: {
1442
1427
  programId: kaminoMarket.programId,
1443
1428
  });
1444
1429
 
1445
- // 3. Deposit initial tokens + borrowed tokens into reserve
1446
- let obligationType: ObligationType;
1447
- if (obligationTypeTagOverride === ObligationTypeTag.Multiply) {
1448
- // multiply
1449
- obligationType = new MultiplyObligation(collTokenMint, debtTokenMint, kaminoMarket.programId);
1450
- } else if (obligationTypeTagOverride === ObligationTypeTag.Leverage) {
1451
- // leverage
1452
- obligationType = new LeverageObligation(collTokenMint, debtTokenMint, kaminoMarket.programId);
1453
- } else {
1454
- throw Error('Obligation type tag not supported for leverage, please use 1 - multiply or 3 - leverage');
1455
- }
1456
-
1457
1430
  const depositAction = await KaminoAction.buildDepositTxns(
1458
1431
  kaminoMarket,
1459
- toLamports(depositAmount, collReserve!.stats.decimals).floor().toString(),
1432
+ toLamports(calcs.adjustDepositPosition, collReserve!.stats.decimals).floor().toString(),
1460
1433
  collTokenMint,
1461
- user,
1462
- obligation ? obligation : obligationType,
1434
+ owner,
1435
+ obligation,
1463
1436
  0,
1464
1437
  false,
1465
1438
  false,
@@ -1469,21 +1442,13 @@ export const getIncreaseLeverageIxns = async (props: {
1469
1442
  { includeScopeRefresh: true, scopeFeed: scopeFeed! }
1470
1443
  );
1471
1444
 
1472
- // 4. Get swap estimations to understand how much we need to borrow from borrow reserve
1473
- const borrowAmount = depositAmount
1474
- .mul(new Decimal(1).plus(flashLoanFee))
1475
- .mul(new Decimal(1 + slippagePct / 100))
1476
- .div(priceDebtToColl);
1477
-
1478
- const _collTokenExpectedSwapOut = depositAmount.mul(new Decimal(1).plus(flashLoanFee));
1479
-
1480
- // 5. Borrow tokens in borrow token reserve that will be swapped to repay flash loan
1445
+ // 4. Borrow tokens in borrow token reserve that will be swapped to repay flash loan
1481
1446
  const borrowAction = await KaminoAction.buildBorrowTxns(
1482
1447
  kaminoMarket,
1483
- toLamports(borrowAmount, debtReserve!.stats.decimals).ceil().toString(),
1448
+ toLamports(calcs.borrowAmount, debtReserve!.stats.decimals).ceil().toString(),
1484
1449
  debtTokenMint,
1485
- user,
1486
- obligation ? obligation : obligationType,
1450
+ owner,
1451
+ obligation,
1487
1452
  0,
1488
1453
  false,
1489
1454
  false,
@@ -1493,84 +1458,12 @@ export const getIncreaseLeverageIxns = async (props: {
1493
1458
  { includeScopeRefresh: true, scopeFeed: scopeFeed! }
1494
1459
  );
1495
1460
 
1496
- const klendIxns = [
1497
- ...budgetIxns,
1498
- ...createAtaIxs,
1499
- ...[flashBorrowIxn],
1500
- ...depositAction.setupIxs,
1501
- ...depositAction.lendingIxs,
1502
- ...depositAction.cleanupIxs,
1503
- ...borrowAction.setupIxs,
1504
- ...borrowAction.lendingIxs,
1505
- ...borrowAction.cleanupIxs,
1506
- ...[flashRepayIxn],
1507
- ];
1508
-
1509
- const uniqueAccounts = new PublicKeySet<PublicKey>([]);
1510
- klendIxns.forEach((ixn) => {
1511
- ixn.keys.forEach((key) => {
1512
- uniqueAccounts.add(key.pubkey);
1513
- });
1514
- });
1515
- const totalKlendAccounts = uniqueAccounts.toArray().length;
1516
-
1517
- // return early to avoid extra swapper calls
1518
- if (getTotalKlendAccountsOnly) {
1519
- return {
1520
- ixns: [],
1521
- lookupTablesAddresses: [],
1522
- swapInputs: {
1523
- inputAmountLamports: new Decimal('0'),
1524
- inputMint: PublicKey.default,
1525
- outputMint: PublicKey.default,
1526
- },
1527
- totalKlendAccounts: totalKlendAccounts,
1528
- };
1529
- }
1530
-
1531
- let depositSwapper: SwapIxnsProvider;
1532
- let expectedDebtTokenAtaBalance = new Decimal(0);
1533
-
1534
- if (collIsKtoken) {
1535
- if (kamino === undefined) {
1536
- throw Error('Ktoken use as collateral for leverage without Kamino instance');
1537
- }
1538
- depositSwapper = await getTokenToKtokenSwapper(connection, kaminoMarket, kamino, user, swapper, priceAinB, false);
1539
-
1540
- expectedDebtTokenAtaBalance = await getExpectedTokenBalanceAfterBorrow(
1541
- connection,
1542
- debtTokenMint,
1543
- user,
1544
- toLamports(!collIsKtoken ? borrowAmount : amountToFashBorrowDebt, debtReserve!.stats.decimals).floor(),
1545
- debtReserve!.state.liquidity.mintDecimals.toNumber()
1546
- );
1547
- } else {
1548
- depositSwapper = swapper;
1549
- }
1550
-
1551
- const swapInputs: SwapInputs = {
1552
- inputAmountLamports: toLamports(
1553
- !collIsKtoken ? borrowAmount : amountToFashBorrowDebt,
1554
- debtReserve!.stats.decimals
1555
- ).ceil(),
1556
- inputMint: debtTokenMint,
1557
- outputMint: collTokenMint,
1558
- };
1559
-
1560
- const [swapIxns, lookupTablesAddresses] = await depositSwapper(
1561
- swapInputs.inputAmountLamports.toNumber(),
1562
- swapInputs.inputMint,
1563
- swapInputs.outputMint,
1564
- slippagePct,
1565
- expectedDebtTokenAtaBalance
1566
- );
1567
-
1568
- const swapInstructions = removeBudgetAndAtaIxns(swapIxns, []);
1461
+ const swapInstructions = removeBudgetAndAtaIxns(swapQuoteIxs.swapIxs, []);
1569
1462
 
1570
- const ixns = !collIsKtoken
1463
+ const ixs = !collIsKtoken
1571
1464
  ? [
1572
1465
  ...budgetIxns,
1573
- ...createAtaIxs,
1466
+ ...atasAndCreateIxns.map((x) => x.createAtaIx),
1574
1467
  ...[flashBorrowIxn],
1575
1468
  ...depositAction.setupIxs,
1576
1469
  ...depositAction.lendingIxs,
@@ -1583,7 +1476,7 @@ export const getIncreaseLeverageIxns = async (props: {
1583
1476
  ]
1584
1477
  : [
1585
1478
  ...budgetIxns,
1586
- ...createAtaIxs,
1479
+ ...atasAndCreateIxns.map((x) => x.createAtaIx),
1587
1480
  ...[flashBorrowIxn],
1588
1481
  ...swapInstructions,
1589
1482
  ...depositAction.setupIxs,
@@ -1595,81 +1488,30 @@ export const getIncreaseLeverageIxns = async (props: {
1595
1488
  ...[flashRepayIxn],
1596
1489
  ];
1597
1490
 
1598
- ixns.forEach((ixn, i) => {
1599
- console.log(`ixn ${i + 1}: ${ixn.programId.toString()}`);
1600
- });
1601
-
1602
- // Create and send transaction
1603
- if (collIsKtoken) {
1604
- if (strategy?.strategy.strategyLookupTable) {
1605
- lookupTablesAddresses.push(strategy?.strategy.strategyLookupTable!);
1606
- } else {
1607
- console.log('Strategy lookup table not found');
1608
- }
1609
- }
1610
- return {
1611
- ixns,
1612
- lookupTablesAddresses,
1613
- swapInputs,
1614
- totalKlendAccounts,
1615
- };
1616
- };
1491
+ return ixs;
1492
+ }
1617
1493
 
1618
1494
  /**
1619
1495
  * Withdraw and repay tokens if leverage decreased
1620
1496
  */
1621
- export const getDecreaseLeverageIxns = async (props: {
1622
- connection: Connection;
1623
- budgetAndPriorityFeeIxns: TransactionInstruction[];
1624
- user: PublicKey;
1625
- kaminoMarket: KaminoMarket;
1626
- withdrawAmount: Decimal;
1627
- repayAmount: Decimal;
1628
- collTokenMint: PublicKey;
1629
- debtTokenMint: PublicKey;
1630
- slippagePct: number;
1631
- swapper: SwapIxnsProvider;
1632
- referrer: PublicKey;
1633
- isKtoken: IsKtokenProvider;
1634
- kamino: Kamino | undefined;
1635
- obligationTypeTagOverride: ObligationTypeTag;
1636
- obligation: KaminoObligation | null;
1637
- currentSlot: number;
1638
- getTotalKlendAccountsOnly: boolean;
1639
- scopeFeed: string | undefined;
1640
- }) => {
1641
- const {
1642
- connection,
1643
- budgetAndPriorityFeeIxns,
1644
- user,
1645
- kaminoMarket,
1646
- withdrawAmount,
1647
- repayAmount,
1648
- collTokenMint,
1649
- debtTokenMint,
1650
- slippagePct,
1651
- swapper,
1652
- referrer,
1653
- isKtoken,
1654
- kamino,
1655
- obligationTypeTagOverride = 1,
1656
- obligation,
1657
- currentSlot,
1658
- getTotalKlendAccountsOnly,
1659
- scopeFeed,
1660
- } = props;
1661
-
1662
- console.log(
1663
- 'getDecreaseLeverageIxns',
1664
- toJson({ withdrawAmount, repayAmount, collTokenMint, debtTokenMint, slippagePct })
1665
- );
1497
+ async function buildDecreaseLeverageIxns(
1498
+ owner: PublicKey,
1499
+ kaminoMarket: KaminoMarket,
1500
+ collTokenMint: PublicKey,
1501
+ debtTokenMint: PublicKey,
1502
+ obligation: KaminoObligation,
1503
+ referrer: PublicKey,
1504
+ currentSlot: number,
1505
+ calcs: AdjustLeverageCalcsResult,
1506
+ strategy: StrategyWithAddress | undefined,
1507
+ scopeFeed: string | undefined,
1508
+ collIsKtoken: boolean,
1509
+ swapQuoteIxs: SwapQuoteIxs,
1510
+ budgetAndPriorityFeeIxns: TransactionInstruction[] | undefined
1511
+ ): Promise<TransactionInstruction[]> {
1666
1512
  const collReserve = kaminoMarket.getReserveByMint(collTokenMint);
1667
1513
  const debtReserve = kaminoMarket.getReserveByMint(debtTokenMint);
1668
- const collIsKtoken = await isKtoken(collTokenMint);
1669
-
1670
- const flashLoanFee = debtReserve?.getFlashLoanFee() || new Decimal(0);
1671
-
1672
- const strategy = collIsKtoken ? await kamino?.getStrategyByKTokenMint(collTokenMint) : undefined;
1514
+ const debtTokenAta = getAssociatedTokenAddressSync(debtTokenMint, owner);
1673
1515
 
1674
1516
  // 1. Create atas & budget txns
1675
1517
  const budgetIxns = budgetAndPriorityFeeIxns || getComputeBudgetAndPriorityFeeIxns(3000000);
@@ -1719,36 +1561,32 @@ export const getDecreaseLeverageIxns = async (props: {
1719
1561
  },
1720
1562
  ];
1721
1563
  }
1722
- const {
1723
- atas: [, debtTokenAta],
1724
- createAtaIxs,
1725
- } = await getAtasWithCreateIxnsIfMissing(connection, user, mintsToCreateAtas);
1564
+ const atasAndCreateIxns = createAtasIdempotent(owner, mintsToCreateAtas);
1726
1565
 
1727
1566
  // TODO: Mihai/Marius check if we can improve this logic and not convert any SOL
1728
1567
  // This is here so that we have enough wsol to repay in case the kAB swapped to sol after estimates is not enough
1729
1568
  const closeWsolAtaIxns: TransactionInstruction[] = [];
1730
- if (debtTokenMint.equals(WRAPPED_SOL_MINT)) {
1731
- const wsolAta = await getAssociatedTokenAddress(WRAPPED_SOL_MINT, user, false);
1732
- closeWsolAtaIxns.push(createCloseAccountInstruction(wsolAta, user, user, [], TOKEN_PROGRAM_ID));
1733
- }
1734
-
1735
1569
  const fillWsolAtaIxns: TransactionInstruction[] = [];
1736
1570
  if (debtTokenMint.equals(WRAPPED_SOL_MINT)) {
1737
- const halfSolBalance = (await connection.getBalance(user)) / LAMPORTS_PER_SOL / 2;
1571
+ const wsolAta = getAssociatedTokenAddress(WRAPPED_SOL_MINT, owner, false);
1572
+
1573
+ closeWsolAtaIxns.push(createCloseAccountInstruction(wsolAta, owner, owner, [], TOKEN_PROGRAM_ID));
1574
+
1575
+ const halfSolBalance = (await kaminoMarket.getConnection().getBalance(owner)) / LAMPORTS_PER_SOL / 2;
1738
1576
  const balanceToWrap = halfSolBalance < 0.1 ? halfSolBalance : 0.1;
1739
1577
  fillWsolAtaIxns.push(
1740
- ...getDepositWsolIxns(user, debtTokenAta, toLamports(balanceToWrap, debtReserve!.stats.decimals).ceil())
1578
+ ...getDepositWsolIxns(owner, wsolAta, toLamports(balanceToWrap, debtReserve!.stats.decimals).ceil())
1741
1579
  );
1742
1580
  }
1743
1581
 
1744
1582
  // 3. Flash borrow & repay amount to repay (debt)
1745
1583
  const { flashBorrowIxn, flashRepayIxn } = getFlashLoanInstructions({
1746
- borrowIxnIndex: budgetIxns.length + createAtaIxs.length + fillWsolAtaIxns.length,
1747
- walletPublicKey: user,
1584
+ borrowIxnIndex: budgetIxns.length + atasAndCreateIxns.length + fillWsolAtaIxns.length,
1585
+ walletPublicKey: owner,
1748
1586
  lendingMarketAuthority: kaminoMarket.getLendingMarketAuthority(),
1749
1587
  lendingMarketAddress: kaminoMarket.getAddress(),
1750
1588
  reserve: debtReserve!,
1751
- amountLamports: toLamports(repayAmount, debtReserve!.stats.decimals),
1589
+ amountLamports: toLamports(Decimal.abs(calcs.adjustBorrowPosition), debtReserve!.stats.decimals),
1752
1590
  destinationAta: debtTokenAta,
1753
1591
  referrerAccount: kaminoMarket.programId,
1754
1592
  referrerTokenState: kaminoMarket.programId,
@@ -1756,25 +1594,13 @@ export const getDecreaseLeverageIxns = async (props: {
1756
1594
  });
1757
1595
 
1758
1596
  // 4. Actually do the repay of the flash borrowed amounts
1759
- let obligationType: ObligationType;
1760
- if (obligationTypeTagOverride === ObligationTypeTag.Multiply) {
1761
- // multiply
1762
- obligationType = new MultiplyObligation(collTokenMint, debtTokenMint, kaminoMarket.programId);
1763
- } else if (obligationTypeTagOverride === ObligationTypeTag.Leverage) {
1764
- // leverage
1765
- obligationType = new LeverageObligation(collTokenMint, debtTokenMint, kaminoMarket.programId);
1766
- } else {
1767
- throw Error('Obligation type tag not supported for leverage, please use 1 - multiply or 3 - leverage');
1768
- }
1769
-
1770
1597
  const scopeRefresh = scopeFeed ? { includeScopeRefresh: true, scopeFeed: scopeFeed } : undefined;
1771
-
1772
1598
  const repayAction = await KaminoAction.buildRepayTxns(
1773
1599
  kaminoMarket,
1774
- toLamports(repayAmount, debtReserve!.stats.decimals).floor().toString(),
1600
+ toLamports(Decimal.abs(calcs.adjustBorrowPosition), debtReserve!.stats.decimals).floor().toString(),
1775
1601
  debtTokenMint,
1776
- user,
1777
- obligation ? obligation : obligationType,
1602
+ owner,
1603
+ obligation,
1778
1604
  currentSlot,
1779
1605
  undefined,
1780
1606
  0,
@@ -1786,16 +1612,12 @@ export const getDecreaseLeverageIxns = async (props: {
1786
1612
  );
1787
1613
 
1788
1614
  // 6. Withdraw collateral (a little bit more to be able to pay for the slippage on swap)
1789
- const withdrawAmountWithSlippageAndFlashLoanFee = withdrawAmount
1790
- .mul(new Decimal(1).plus(flashLoanFee))
1791
- .mul(1 + slippagePct / 100);
1792
-
1793
1615
  const withdrawAction = await KaminoAction.buildWithdrawTxns(
1794
1616
  kaminoMarket,
1795
- toLamports(withdrawAmountWithSlippageAndFlashLoanFee, collReserve!.stats.decimals).ceil().toString(),
1617
+ toLamports(calcs.withdrawAmountWithSlippageAndFlashLoanFee, collReserve!.stats.decimals).ceil().toString(),
1796
1618
  collTokenMint,
1797
- user,
1798
- obligation ? obligation : obligationType,
1619
+ owner,
1620
+ obligation,
1799
1621
  0,
1800
1622
  false,
1801
1623
  false,
@@ -1805,68 +1627,11 @@ export const getDecreaseLeverageIxns = async (props: {
1805
1627
  { includeScopeRefresh: true, scopeFeed: scopeFeed! }
1806
1628
  );
1807
1629
 
1808
- const klendIxns = [
1809
- ...budgetIxns,
1810
- ...createAtaIxs,
1811
- ...fillWsolAtaIxns,
1812
- ...[flashBorrowIxn],
1813
- ...repayAction.setupIxs,
1814
- ...repayAction.lendingIxs,
1815
- ...repayAction.cleanupIxs,
1816
- ...withdrawAction.setupIxs,
1817
- ...withdrawAction.lendingIxs,
1818
- ...withdrawAction.cleanupIxs,
1819
- ...[flashRepayIxn],
1820
- ...closeWsolAtaIxns,
1821
- ];
1822
-
1823
- const uniqueAccs = uniqueAccounts(klendIxns);
1824
- const totalKlendAccounts = uniqueAccs.length;
1825
-
1826
- // return early to avoid extra swapper calls
1827
- if (getTotalKlendAccountsOnly) {
1828
- return {
1829
- ixns: [],
1830
- lookupTablesAddresses: [],
1831
- swapInputs: {
1832
- inputAmountLamports: new Decimal('0'),
1833
- inputMint: PublicKey.default,
1834
- outputMint: PublicKey.default,
1835
- },
1836
- totalKlendAccounts: totalKlendAccounts,
1837
- };
1838
- }
1839
-
1840
- let withdrawSwapper: SwapIxnsProvider;
1841
-
1842
- if (collIsKtoken) {
1843
- if (kamino === undefined) {
1844
- throw Error('Ktoken use as collateral for leverage without Kamino instance');
1845
- }
1846
- withdrawSwapper = await getKtokenToTokenSwapper(kaminoMarket, kamino, user, swapper);
1847
- } else {
1848
- withdrawSwapper = swapper;
1849
- }
1850
-
1851
- const swapInputs: SwapInputs = {
1852
- inputAmountLamports: toLamports(withdrawAmountWithSlippageAndFlashLoanFee, collReserve!.stats.decimals).ceil(),
1853
- inputMint: collTokenMint,
1854
- outputMint: debtTokenMint,
1855
- };
1856
-
1857
- // 5. Get swap ixns
1858
- const [swapIxns, lookupTablesAddresses] = await withdrawSwapper(
1859
- swapInputs.inputAmountLamports.toNumber(),
1860
- swapInputs.inputMint,
1861
- swapInputs.outputMint,
1862
- slippagePct
1863
- );
1864
-
1865
- const swapInstructions = removeBudgetAndAtaIxns(swapIxns, []);
1630
+ const swapInstructions = removeBudgetAndAtaIxns(swapQuoteIxs.swapIxs, []);
1866
1631
 
1867
1632
  const ixns = [
1868
1633
  ...budgetIxns,
1869
- ...createAtaIxs,
1634
+ ...atasAndCreateIxns.map((x) => x.createAtaIx),
1870
1635
  ...fillWsolAtaIxns,
1871
1636
  ...[flashBorrowIxn],
1872
1637
  ...repayAction.setupIxs,
@@ -1880,22 +1645,5 @@ export const getDecreaseLeverageIxns = async (props: {
1880
1645
  ...closeWsolAtaIxns,
1881
1646
  ];
1882
1647
 
1883
- ixns.forEach((ixn, i) => {
1884
- console.log(`ixn ${i + 1}: ${ixn.programId.toString()}`);
1885
- });
1886
-
1887
- if (collIsKtoken) {
1888
- if (strategy?.strategy.strategyLookupTable) {
1889
- lookupTablesAddresses.push(strategy?.strategy.strategyLookupTable!);
1890
- } else {
1891
- console.log('Strategy lookup table not found');
1892
- }
1893
- }
1894
- // Create and send transaction
1895
- return {
1896
- ixns,
1897
- lookupTablesAddresses,
1898
- swapInputs,
1899
- totalKlendAccounts,
1900
- };
1901
- };
1648
+ return ixns;
1649
+ }