@kamino-finance/klend-sdk 7.2.6 → 7.3.1

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 (223) hide show
  1. package/README.md +1 -0
  2. package/dist/@codegen/klend/accounts/LendingMarket.d.ts +33 -0
  3. package/dist/@codegen/klend/accounts/LendingMarket.d.ts.map +1 -1
  4. package/dist/@codegen/klend/accounts/LendingMarket.js +24 -2
  5. package/dist/@codegen/klend/accounts/LendingMarket.js.map +1 -1
  6. package/dist/@codegen/klend/errors/custom.d.ts +26 -2
  7. package/dist/@codegen/klend/errors/custom.d.ts.map +1 -1
  8. package/dist/@codegen/klend/errors/custom.js +45 -3
  9. package/dist/@codegen/klend/errors/custom.js.map +1 -1
  10. package/dist/@codegen/klend/instructions/index.d.ts +2 -0
  11. package/dist/@codegen/klend/instructions/index.d.ts.map +1 -1
  12. package/dist/@codegen/klend/instructions/index.js +3 -1
  13. package/dist/@codegen/klend/instructions/index.js.map +1 -1
  14. package/dist/@codegen/klend/instructions/initReserve.d.ts +1 -1
  15. package/dist/@codegen/klend/instructions/initReserve.d.ts.map +1 -1
  16. package/dist/@codegen/klend/instructions/initReserve.js +1 -5
  17. package/dist/@codegen/klend/instructions/initReserve.js.map +1 -1
  18. package/dist/@codegen/klend/instructions/liquidateObligationAndRedeemReserveCollateralV2.js +1 -1
  19. package/dist/@codegen/klend/instructions/seedDepositOnInitReserve.d.ts +13 -0
  20. package/dist/@codegen/klend/instructions/seedDepositOnInitReserve.d.ts.map +1 -0
  21. package/dist/@codegen/klend/instructions/seedDepositOnInitReserve.js +24 -0
  22. package/dist/@codegen/klend/instructions/seedDepositOnInitReserve.js.map +1 -0
  23. package/dist/@codegen/klend/instructions/withdrawObligationCollateral.js +1 -1
  24. package/dist/@codegen/klend/instructions/withdrawObligationCollateralV2.js +1 -1
  25. package/dist/@codegen/klend/types/ReserveConfig.d.ts +58 -24
  26. package/dist/@codegen/klend/types/ReserveConfig.d.ts.map +1 -1
  27. package/dist/@codegen/klend/types/ReserveConfig.js +40 -18
  28. package/dist/@codegen/klend/types/ReserveConfig.js.map +1 -1
  29. package/dist/@codegen/klend/types/ReserveFees.d.ts +8 -8
  30. package/dist/@codegen/klend/types/ReserveFees.d.ts.map +1 -1
  31. package/dist/@codegen/klend/types/ReserveFees.js +8 -8
  32. package/dist/@codegen/klend/types/ReserveFees.js.map +1 -1
  33. package/dist/@codegen/klend/types/UpdateConfigMode.d.ts +46 -7
  34. package/dist/@codegen/klend/types/UpdateConfigMode.d.ts.map +1 -1
  35. package/dist/@codegen/klend/types/UpdateConfigMode.js +85 -12
  36. package/dist/@codegen/klend/types/UpdateConfigMode.js.map +1 -1
  37. package/dist/@codegen/klend/types/UpdateLendingMarketMode.d.ts +26 -0
  38. package/dist/@codegen/klend/types/UpdateLendingMarketMode.d.ts.map +1 -1
  39. package/dist/@codegen/klend/types/UpdateLendingMarketMode.js +49 -1
  40. package/dist/@codegen/klend/types/UpdateLendingMarketMode.js.map +1 -1
  41. package/dist/@codegen/klend/types/index.d.ts +4 -4
  42. package/dist/@codegen/klend/types/index.d.ts.map +1 -1
  43. package/dist/@codegen/klend/types/index.js.map +1 -1
  44. package/dist/@codegen/kvault/accounts/GlobalConfig.d.ts +32 -0
  45. package/dist/@codegen/kvault/accounts/GlobalConfig.d.ts.map +1 -0
  46. package/dist/@codegen/kvault/accounts/GlobalConfig.js +125 -0
  47. package/dist/@codegen/kvault/accounts/GlobalConfig.js.map +1 -0
  48. package/dist/@codegen/kvault/accounts/Reserve.js +1 -1
  49. package/dist/@codegen/kvault/accounts/ReserveWhitelistEntry.d.ts +52 -0
  50. package/dist/@codegen/kvault/accounts/ReserveWhitelistEntry.d.ts.map +1 -0
  51. package/dist/@codegen/kvault/accounts/ReserveWhitelistEntry.js +127 -0
  52. package/dist/@codegen/kvault/accounts/ReserveWhitelistEntry.js.map +1 -0
  53. package/dist/@codegen/kvault/accounts/VaultState.d.ts +18 -0
  54. package/dist/@codegen/kvault/accounts/VaultState.d.ts.map +1 -1
  55. package/dist/@codegen/kvault/accounts/VaultState.js +39 -1
  56. package/dist/@codegen/kvault/accounts/VaultState.js.map +1 -1
  57. package/dist/@codegen/kvault/accounts/index.d.ts +4 -0
  58. package/dist/@codegen/kvault/accounts/index.d.ts.map +1 -1
  59. package/dist/@codegen/kvault/accounts/index.js +5 -1
  60. package/dist/@codegen/kvault/accounts/index.js.map +1 -1
  61. package/dist/@codegen/kvault/errors/custom.d.ts +61 -5
  62. package/dist/@codegen/kvault/errors/custom.d.ts.map +1 -1
  63. package/dist/@codegen/kvault/errors/custom.js +108 -9
  64. package/dist/@codegen/kvault/errors/custom.js.map +1 -1
  65. package/dist/@codegen/kvault/instructions/addUpdateWhitelistedReserve.d.ts +16 -0
  66. package/dist/@codegen/kvault/instructions/addUpdateWhitelistedReserve.d.ts.map +1 -0
  67. package/dist/@codegen/kvault/instructions/addUpdateWhitelistedReserve.js +66 -0
  68. package/dist/@codegen/kvault/instructions/addUpdateWhitelistedReserve.js.map +1 -0
  69. package/dist/@codegen/kvault/instructions/buy.d.ts +24 -0
  70. package/dist/@codegen/kvault/instructions/buy.d.ts.map +1 -0
  71. package/dist/@codegen/kvault/instructions/buy.js +67 -0
  72. package/dist/@codegen/kvault/instructions/buy.js.map +1 -0
  73. package/dist/@codegen/kvault/instructions/index.d.ts +12 -0
  74. package/dist/@codegen/kvault/instructions/index.d.ts.map +1 -1
  75. package/dist/@codegen/kvault/instructions/index.js +13 -1
  76. package/dist/@codegen/kvault/instructions/index.js.map +1 -1
  77. package/dist/@codegen/kvault/instructions/initGlobalConfig.d.ts +11 -0
  78. package/dist/@codegen/kvault/instructions/initGlobalConfig.d.ts.map +1 -0
  79. package/dist/@codegen/kvault/instructions/initGlobalConfig.js +20 -0
  80. package/dist/@codegen/kvault/instructions/initGlobalConfig.js.map +1 -0
  81. package/dist/@codegen/kvault/instructions/invest.d.ts +2 -1
  82. package/dist/@codegen/kvault/instructions/invest.d.ts.map +1 -1
  83. package/dist/@codegen/kvault/instructions/invest.js +5 -0
  84. package/dist/@codegen/kvault/instructions/invest.js.map +1 -1
  85. package/dist/@codegen/kvault/instructions/sell.d.ts +40 -0
  86. package/dist/@codegen/kvault/instructions/sell.d.ts.map +1 -0
  87. package/dist/@codegen/kvault/instructions/sell.js +98 -0
  88. package/dist/@codegen/kvault/instructions/sell.js.map +1 -0
  89. package/dist/@codegen/kvault/instructions/updateGlobalConfig.d.ts +13 -0
  90. package/dist/@codegen/kvault/instructions/updateGlobalConfig.d.ts.map +1 -0
  91. package/dist/@codegen/kvault/instructions/updateGlobalConfig.js +63 -0
  92. package/dist/@codegen/kvault/instructions/updateGlobalConfig.js.map +1 -0
  93. package/dist/@codegen/kvault/instructions/updateGlobalConfigAdmin.d.ts +8 -0
  94. package/dist/@codegen/kvault/instructions/updateGlobalConfigAdmin.d.ts.map +1 -0
  95. package/dist/@codegen/kvault/instructions/updateGlobalConfigAdmin.js +21 -0
  96. package/dist/@codegen/kvault/instructions/updateGlobalConfigAdmin.js.map +1 -0
  97. package/dist/@codegen/kvault/instructions/updateReserveAllocation.d.ts +2 -1
  98. package/dist/@codegen/kvault/instructions/updateReserveAllocation.d.ts.map +1 -1
  99. package/dist/@codegen/kvault/instructions/updateReserveAllocation.js +5 -0
  100. package/dist/@codegen/kvault/instructions/updateReserveAllocation.js.map +1 -1
  101. package/dist/@codegen/kvault/instructions/updateVaultConfig.d.ts +2 -1
  102. package/dist/@codegen/kvault/instructions/updateVaultConfig.d.ts.map +1 -1
  103. package/dist/@codegen/kvault/instructions/updateVaultConfig.js +2 -5
  104. package/dist/@codegen/kvault/instructions/updateVaultConfig.js.map +1 -1
  105. package/dist/@codegen/kvault/instructions/withdraw.d.ts +1 -0
  106. package/dist/@codegen/kvault/instructions/withdraw.d.ts.map +1 -1
  107. package/dist/@codegen/kvault/instructions/withdraw.js +1 -0
  108. package/dist/@codegen/kvault/instructions/withdraw.js.map +1 -1
  109. package/dist/@codegen/kvault/instructions/withdrawFromAvailable.d.ts +1 -0
  110. package/dist/@codegen/kvault/instructions/withdrawFromAvailable.d.ts.map +1 -1
  111. package/dist/@codegen/kvault/instructions/withdrawFromAvailable.js +1 -0
  112. package/dist/@codegen/kvault/instructions/withdrawFromAvailable.js.map +1 -1
  113. package/dist/@codegen/kvault/types/ReserveConfig.d.ts +27 -34
  114. package/dist/@codegen/kvault/types/ReserveConfig.d.ts.map +1 -1
  115. package/dist/@codegen/kvault/types/ReserveConfig.js +20 -17
  116. package/dist/@codegen/kvault/types/ReserveConfig.js.map +1 -1
  117. package/dist/@codegen/kvault/types/ReserveFees.d.ts +8 -8
  118. package/dist/@codegen/kvault/types/ReserveFees.d.ts.map +1 -1
  119. package/dist/@codegen/kvault/types/ReserveFees.js +8 -8
  120. package/dist/@codegen/kvault/types/ReserveFees.js.map +1 -1
  121. package/dist/@codegen/kvault/types/UpdateGlobalConfigMode.d.ts +68 -0
  122. package/dist/@codegen/kvault/types/UpdateGlobalConfigMode.d.ts.map +1 -0
  123. package/dist/@codegen/kvault/types/UpdateGlobalConfigMode.js +162 -0
  124. package/dist/@codegen/kvault/types/UpdateGlobalConfigMode.js.map +1 -0
  125. package/dist/@codegen/kvault/types/UpdateReserveWhitelistMode.d.ts +46 -0
  126. package/dist/@codegen/kvault/types/UpdateReserveWhitelistMode.d.ts.map +1 -0
  127. package/dist/@codegen/kvault/types/UpdateReserveWhitelistMode.js +124 -0
  128. package/dist/@codegen/kvault/types/UpdateReserveWhitelistMode.js.map +1 -0
  129. package/dist/@codegen/kvault/types/VaultConfigField.d.ts +65 -0
  130. package/dist/@codegen/kvault/types/VaultConfigField.d.ts.map +1 -1
  131. package/dist/@codegen/kvault/types/VaultConfigField.js +121 -1
  132. package/dist/@codegen/kvault/types/VaultConfigField.js.map +1 -1
  133. package/dist/@codegen/kvault/types/index.d.ts +10 -2
  134. package/dist/@codegen/kvault/types/index.d.ts.map +1 -1
  135. package/dist/@codegen/kvault/types/index.js +5 -1
  136. package/dist/@codegen/kvault/types/index.js.map +1 -1
  137. package/dist/classes/manager.d.ts +19 -1
  138. package/dist/classes/manager.d.ts.map +1 -1
  139. package/dist/classes/manager.js +28 -0
  140. package/dist/classes/manager.js.map +1 -1
  141. package/dist/classes/reserve.d.ts.map +1 -1
  142. package/dist/classes/reserve.js +7 -4
  143. package/dist/classes/reserve.js.map +1 -1
  144. package/dist/classes/vault.d.ts +33 -5
  145. package/dist/classes/vault.d.ts.map +1 -1
  146. package/dist/classes/vault.js +379 -48
  147. package/dist/classes/vault.js.map +1 -1
  148. package/dist/idl/klend.json +129 -59
  149. package/dist/lib.d.ts +1 -0
  150. package/dist/lib.d.ts.map +1 -1
  151. package/dist/lib.js +4 -2
  152. package/dist/lib.js.map +1 -1
  153. package/dist/manager/client_kamino_manager.js +206 -15
  154. package/dist/manager/client_kamino_manager.js.map +1 -1
  155. package/dist/utils/index.d.ts +1 -0
  156. package/dist/utils/index.d.ts.map +1 -1
  157. package/dist/utils/index.js +1 -0
  158. package/dist/utils/index.js.map +1 -1
  159. package/dist/utils/managerTypes.d.ts +1 -1
  160. package/dist/utils/managerTypes.d.ts.map +1 -1
  161. package/dist/utils/managerTypes.js +5 -3
  162. package/dist/utils/managerTypes.js.map +1 -1
  163. package/dist/utils/parse.d.ts +10 -0
  164. package/dist/utils/parse.d.ts.map +1 -0
  165. package/dist/utils/parse.js +24 -0
  166. package/dist/utils/parse.js.map +1 -0
  167. package/dist/utils/seeds.d.ts +5 -5
  168. package/dist/utils/seeds.d.ts.map +1 -1
  169. package/dist/utils/seeds.js +13 -13
  170. package/dist/utils/seeds.js.map +1 -1
  171. package/dist/utils/vault.d.ts.map +1 -1
  172. package/dist/utils/vault.js +6 -0
  173. package/dist/utils/vault.js.map +1 -1
  174. package/package.json +1 -1
  175. package/src/@codegen/klend/accounts/LendingMarket.ts +46 -2
  176. package/src/@codegen/klend/errors/custom.ts +47 -2
  177. package/src/@codegen/klend/instructions/index.ts +2 -0
  178. package/src/@codegen/klend/instructions/initReserve.ts +2 -6
  179. package/src/@codegen/klend/instructions/liquidateObligationAndRedeemReserveCollateralV2.ts +1 -1
  180. package/src/@codegen/klend/instructions/seedDepositOnInitReserve.ts +50 -0
  181. package/src/@codegen/klend/instructions/withdrawObligationCollateral.ts +1 -1
  182. package/src/@codegen/klend/instructions/withdrawObligationCollateralV2.ts +1 -1
  183. package/src/@codegen/klend/types/ReserveConfig.ts +72 -30
  184. package/src/@codegen/klend/types/ReserveFees.ts +12 -12
  185. package/src/@codegen/klend/types/UpdateConfigMode.ts +103 -13
  186. package/src/@codegen/klend/types/UpdateLendingMarketMode.ts +60 -0
  187. package/src/@codegen/klend/types/index.ts +12 -2
  188. package/src/@codegen/kvault/accounts/GlobalConfig.ts +136 -0
  189. package/src/@codegen/kvault/accounts/Reserve.ts +1 -1
  190. package/src/@codegen/kvault/accounts/ReserveWhitelistEntry.ts +157 -0
  191. package/src/@codegen/kvault/accounts/VaultState.ts +57 -1
  192. package/src/@codegen/kvault/accounts/index.ts +7 -0
  193. package/src/@codegen/kvault/errors/custom.ts +109 -8
  194. package/src/@codegen/kvault/instructions/addUpdateWhitelistedReserve.ts +64 -0
  195. package/src/@codegen/kvault/instructions/buy.ts +74 -0
  196. package/src/@codegen/kvault/instructions/index.ts +18 -0
  197. package/src/@codegen/kvault/instructions/initGlobalConfig.ts +44 -0
  198. package/src/@codegen/kvault/instructions/invest.ts +4 -0
  199. package/src/@codegen/kvault/instructions/sell.ts +122 -0
  200. package/src/@codegen/kvault/instructions/updateGlobalConfig.ts +58 -0
  201. package/src/@codegen/kvault/instructions/updateGlobalConfigAdmin.ts +42 -0
  202. package/src/@codegen/kvault/instructions/updateReserveAllocation.ts +4 -0
  203. package/src/@codegen/kvault/instructions/updateVaultConfig.ts +4 -6
  204. package/src/@codegen/kvault/instructions/withdraw.ts +2 -0
  205. package/src/@codegen/kvault/instructions/withdrawFromAvailable.ts +2 -0
  206. package/src/@codegen/kvault/types/ReserveConfig.ts +34 -37
  207. package/src/@codegen/kvault/types/ReserveFees.ts +12 -12
  208. package/src/@codegen/kvault/types/UpdateGlobalConfigMode.ts +160 -0
  209. package/src/@codegen/kvault/types/UpdateReserveWhitelistMode.ts +117 -0
  210. package/src/@codegen/kvault/types/VaultConfigField.ts +150 -0
  211. package/src/@codegen/kvault/types/index.ts +31 -0
  212. package/src/classes/manager.ts +50 -1
  213. package/src/classes/reserve.ts +7 -5
  214. package/src/classes/vault.ts +555 -48
  215. package/src/idl/klend.json +130 -60
  216. package/src/idl/kvault.json +582 -23
  217. package/src/lib.ts +2 -1
  218. package/src/manager/client_kamino_manager.ts +313 -18
  219. package/src/utils/index.ts +2 -1
  220. package/src/utils/managerTypes.ts +6 -4
  221. package/src/utils/parse.ts +18 -0
  222. package/src/utils/seeds.ts +13 -17
  223. package/src/utils/vault.ts +6 -0
@@ -23,6 +23,9 @@ import {
23
23
  AccountInfoWithPubkey,
24
24
  AccountInfoBase,
25
25
  AccountInfoWithJsonData,
26
+ Option,
27
+ some,
28
+ none,
26
29
  } from '@solana/kit';
27
30
  import {
28
31
  AllOracleAccounts,
@@ -40,18 +43,28 @@ import {
40
43
  WRAPPED_SOL_MINT,
41
44
  } from '../lib';
42
45
  import {
46
+ addUpdateWhitelistedReserve,
47
+ AddUpdateWhitelistedReserveAccounts,
48
+ AddUpdateWhitelistedReserveArgs,
49
+ buy,
50
+ BuyAccounts,
51
+ BuyArgs,
43
52
  deposit,
44
53
  DepositAccounts,
45
54
  DepositArgs,
46
55
  giveUpPendingFees,
47
56
  GiveUpPendingFeesAccounts,
48
57
  GiveUpPendingFeesArgs,
58
+ initKVaultGlobalConfig,
49
59
  initVault,
50
60
  InitVaultAccounts,
51
61
  invest,
52
62
  InvestAccounts,
53
63
  removeAllocation,
54
64
  RemoveAllocationAccounts,
65
+ sell,
66
+ SellAccounts,
67
+ SellArgs,
55
68
  updateAdmin,
56
69
  UpdateAdminAccounts,
57
70
  updateReserveAllocation,
@@ -69,7 +82,7 @@ import {
69
82
  withdrawPendingFees,
70
83
  WithdrawPendingFeesAccounts,
71
84
  } from '../@codegen/kvault/instructions';
72
- import { VaultConfigField, VaultConfigFieldKind } from '../@codegen/kvault/types';
85
+ import { UpdateReserveWhitelistModeKind, VaultConfigField, VaultConfigFieldKind } from '../@codegen/kvault/types';
73
86
  import { VaultState } from '../@codegen/kvault/accounts';
74
87
  import Decimal from 'decimal.js';
75
88
  import { bpsToPct, decodeVaultName, numberToLamportsDecimal, parseTokenSymbol, pubkeyHashMapToJson } from './utils';
@@ -85,6 +98,8 @@ import {
85
98
  getTokenAccountAmount,
86
99
  getTokenAccountMint,
87
100
  lendingMarketAuthPda,
101
+ parseBooleanFlag,
102
+ programDataPda,
88
103
  SECONDS_PER_YEAR,
89
104
  U64_MAX,
90
105
  VAULT_INITIAL_DEPOSIT,
@@ -140,6 +155,8 @@ const BASE_VAULT_AUTHORITY_SEED = 'authority';
140
155
  const SHARES_SEED = 'shares';
141
156
  const EVENT_AUTHORITY_SEED = '__event_authority';
142
157
  export const METADATA_SEED = 'metadata';
158
+ const GLOBAL_CONFIG_STATE_SEED = 'global_config';
159
+ const WHITELISTED_RESERVES_SEED = 'whitelisted_reserves';
143
160
 
144
161
  export const METADATA_PROGRAM_ID: Address = address('metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s');
145
162
 
@@ -215,6 +232,29 @@ export class KaminoVaultClient {
215
232
  console.log('Tokens per share: ', tokensPerShare);
216
233
  }
217
234
 
235
+ /**
236
+ * This method initializes the kvault global config (one off, needs to be signed by program owner)
237
+ * @param admin - the admin of the kvault program
238
+ * @returns - an instruction to initialize the kvault global config
239
+ */
240
+ async initKvaultGlobalConfigIx(admin: TransactionSigner) {
241
+ const globalConfigAddress = await getKvaultGlobalConfigPda(this.getProgramID());
242
+
243
+ const programData = await programDataPda(this.getProgramID());
244
+ const ix = initKVaultGlobalConfig(
245
+ {
246
+ payer: admin,
247
+ globalConfig: globalConfigAddress,
248
+ programData: programData,
249
+ systemProgram: SYSTEM_PROGRAM_ADDRESS,
250
+ rent: SYSVAR_RENT_ADDRESS,
251
+ },
252
+ undefined,
253
+ this.getProgramID()
254
+ );
255
+ return ix;
256
+ }
257
+
218
258
  /**
219
259
  * This method will create a vault with a given config. The config can be changed later on, but it is recommended to set it up correctly from the start
220
260
  * @param vaultConfig - the config object used to create a vault
@@ -316,7 +356,7 @@ export class KaminoVaultClient {
316
356
  []
317
357
  );
318
358
 
319
- const setLUTIx = this.updateUninitialisedVaultConfigIx(
359
+ const setLUTIx = await this.updateUninitialisedVaultConfigIx(
320
360
  vaultConfig.admin,
321
361
  vaultState.address,
322
362
  new VaultConfigField.LookupTable(),
@@ -326,7 +366,7 @@ export class KaminoVaultClient {
326
366
  const ixs = [createVaultIx, initVaultIx, setLUTIx];
327
367
 
328
368
  if (vaultConfig.getPerformanceFeeBps() > 0) {
329
- const setPerformanceFeeIx = this.updateUninitialisedVaultConfigIx(
369
+ const setPerformanceFeeIx = await this.updateUninitialisedVaultConfigIx(
330
370
  vaultConfig.admin,
331
371
  vaultState.address,
332
372
  new VaultConfigField.PerformanceFeeBps(),
@@ -335,7 +375,7 @@ export class KaminoVaultClient {
335
375
  ixs.push(setPerformanceFeeIx);
336
376
  }
337
377
  if (vaultConfig.getManagementFeeBps() > 0) {
338
- const setManagementFeeIx = this.updateUninitialisedVaultConfigIx(
378
+ const setManagementFeeIx = await this.updateUninitialisedVaultConfigIx(
339
379
  vaultConfig.admin,
340
380
  vaultState.address,
341
381
  new VaultConfigField.ManagementFeeBps(),
@@ -344,7 +384,7 @@ export class KaminoVaultClient {
344
384
  ixs.push(setManagementFeeIx);
345
385
  }
346
386
  if (vaultConfig.name && vaultConfig.name.length > 0) {
347
- const setNameIx = this.updateUninitialisedVaultConfigIx(
387
+ const setNameIx = await this.updateUninitialisedVaultConfigIx(
348
388
  vaultConfig.admin,
349
389
  vaultState.address,
350
390
  new VaultConfigField.Name(),
@@ -352,7 +392,7 @@ export class KaminoVaultClient {
352
392
  );
353
393
  ixs.push(setNameIx);
354
394
  }
355
- const setFarmIx = this.updateUninitialisedVaultConfigIx(
395
+ const setFarmIx = await this.updateUninitialisedVaultConfigIx(
356
396
  vaultConfig.admin,
357
397
  vaultState.address,
358
398
  new VaultConfigField.Farm(),
@@ -482,6 +522,12 @@ export class KaminoVaultClient {
482
522
  this._kaminoVaultProgramId
483
523
  );
484
524
 
525
+ const reserveWhitelistEntryOption = await getReserveWhitelistEntryIfExists(
526
+ reserveAllocationConfig.getReserveAddress(),
527
+ this.getConnection(),
528
+ this._kaminoVaultProgramId
529
+ );
530
+
485
531
  const vaultAdmin = parseVaultAdmin(vaultState, vaultAdminAuthority);
486
532
  const updateReserveAllocationAccounts: UpdateReserveAllocationAccounts = {
487
533
  signer: vaultAdmin,
@@ -490,6 +536,7 @@ export class KaminoVaultClient {
490
536
  reserveCollateralMint: reserveState.collateral.mintPubkey,
491
537
  reserve: reserveAllocationConfig.getReserveAddress(),
492
538
  ctokenVault: cTokenVault,
539
+ reserveWhitelistEntry: reserveWhitelistEntryOption,
493
540
  systemProgram: SYSTEM_PROGRAM_ADDRESS,
494
541
  rent: SYSVAR_RENT_ADDRESS,
495
542
  reserveCollateralTokenProgram: TOKEN_PROGRAM_ADDRESS,
@@ -756,7 +803,8 @@ export class KaminoVaultClient {
756
803
  * @param vault the vault to update
757
804
  * @param mode the field to update (based on VaultConfigFieldKind enum)
758
805
  * @param value the value to update the field with
759
- * @param [vaultAdminAuthority] the signer of the transaction. Optional. If not provided the admin of the vault will be used. It should be used when changing the admin of the vault if we want to build or batch multiple ixs in the same tx
806
+ * @param [adminAuthority] the signer of the transaction. Optional. If not provided the admin of the vault will be used. It should be used when changing the admin of the vault if we want to build or batch multiple ixs in the same tx.
807
+ * The global admin should be passed in when wanting to change the AllowAllocationsInWhitelistedReservesOnly or AllowInvestInWhitelistedReservesOnly fields to false
760
808
  * @param [lutIxsSigner] the signer of the transaction to be used for the lookup table instructions. Optional. If not provided the admin of the vault will be used. It should be used when changing the admin of the vault if we want to build or batch multiple ixs in the same tx
761
809
  * @param [skipLutUpdate] if true, the lookup table instructions will not be included in the returned instructions
762
810
  * @returns a struct that contains the instruction to update the field and an optional list of instructions to update the lookup table
@@ -765,41 +813,26 @@ export class KaminoVaultClient {
765
813
  vault: KaminoVault,
766
814
  mode: VaultConfigFieldKind,
767
815
  value: string,
768
- vaultAdminAuthority?: TransactionSigner,
816
+ adminAuthority?: TransactionSigner,
769
817
  lutIxsSigner?: TransactionSigner,
770
818
  skipLutUpdate: boolean = false
771
819
  ): Promise<UpdateVaultConfigIxs> {
772
820
  const vaultState: VaultState = await vault.getState();
773
- const admin = parseVaultAdmin(vaultState, vaultAdminAuthority);
821
+ const admin = parseVaultAdmin(vaultState, adminAuthority);
774
822
 
823
+ const globalConfig = await getKvaultGlobalConfigPda(this._kaminoVaultProgramId);
775
824
  const updateVaultConfigAccs: UpdateVaultConfigAccounts = {
776
- vaultAdminAuthority: admin,
825
+ signer: admin,
826
+ globalConfig: globalConfig,
777
827
  vaultState: vault.address,
778
828
  klendProgram: this._kaminoLendProgramId,
779
829
  };
780
- if (vaultAdminAuthority) {
781
- updateVaultConfigAccs.vaultAdminAuthority = vaultAdminAuthority;
782
- }
783
830
 
784
831
  const updateVaultConfigArgs: UpdateVaultConfigArgs = {
785
832
  entry: mode,
786
- data: Buffer.from([0]),
833
+ data: this.getValueForModeAsBuffer(mode, value),
787
834
  };
788
835
 
789
- if (isNaN(+value) || value === DEFAULT_PUBLIC_KEY) {
790
- if (mode.kind === new VaultConfigField.Name().kind) {
791
- const data = Array.from(this.encodeVaultName(value));
792
- updateVaultConfigArgs.data = Buffer.from(data);
793
- } else {
794
- const data = address(value);
795
- updateVaultConfigArgs.data = Buffer.from(addressEncoder.encode(data));
796
- }
797
- } else {
798
- const buffer = Buffer.alloc(8);
799
- buffer.writeBigUInt64LE(BigInt(value.toString()));
800
- updateVaultConfigArgs.data = buffer;
801
- }
802
-
803
836
  const vaultReserves = this.getVaultReserves(vaultState);
804
837
  const vaultReservesState = await this.loadVaultReserves(vaultState);
805
838
 
@@ -863,6 +896,38 @@ export class KaminoVaultClient {
863
896
  return updateVaultConfigIxs;
864
897
  }
865
898
 
899
+ /**
900
+ * Add or update a reserve whitelist entry. This controls whether the reserve is whitelisted for adding/updating
901
+ * allocations or for invest, depending on the mode parameter.
902
+ *
903
+ * @param reserve - Address of the reserve to whitelist
904
+ * @param mode - The whitelist mode: either 'Invest' or 'AddAllocation' with a value (1 = allow, 0 = deny)
905
+ * @param globalAdmin - The global admin that signs the transaction
906
+ * @returns - An instruction to add/update the whitelisted reserve
907
+ */
908
+ async addUpdateWhitelistedReserveIx(
909
+ reserve: Address,
910
+ mode: UpdateReserveWhitelistModeKind,
911
+ globalAdmin: TransactionSigner
912
+ ): Promise<Instruction> {
913
+ const globalConfig = await getKvaultGlobalConfigPda(this._kaminoVaultProgramId);
914
+ const reserveWhitelistEntry = await getReserveWhitelistEntryPda(reserve, this._kaminoVaultProgramId);
915
+
916
+ const accounts: AddUpdateWhitelistedReserveAccounts = {
917
+ globalAdmin,
918
+ globalConfig,
919
+ reserve,
920
+ reserveWhitelistEntry,
921
+ systemProgram: SYSTEM_PROGRAM_ADDRESS,
922
+ };
923
+
924
+ const args: AddUpdateWhitelistedReserveArgs = {
925
+ update: mode,
926
+ };
927
+
928
+ return addUpdateWhitelistedReserve(args, accounts, undefined, this._kaminoVaultProgramId);
929
+ }
930
+
866
931
  /** Sets the farm where the shares can be staked. This is store in vault state and a vault can only have one farm, so the new farm will ovveride the old farm
867
932
  * @param vault - vault to set the farm for
868
933
  * @param farm - the farm where the vault shares can be staked
@@ -895,44 +960,35 @@ export class KaminoVaultClient {
895
960
  }
896
961
 
897
962
  /**
898
- * This method updates the vault config for a vault that
899
- * @param admin - address of vault to be updated
963
+ * This method updates the vault config during vault initialization, within the same transaction
964
+ * where the vault is created. Use this when the vault state is not yet committed to the chain
965
+ * and cannot be fetched via RPC. For updates to existing vaults, use updateVaultConfigIxs instead.
966
+ *
967
+ * @param admin - the admin that signs the transaction
900
968
  * @param vault - address of vault to be updated
901
969
  * @param mode - the field to be updated
902
970
  * @param value - the new value for the field to be updated (number or pubkey)
903
971
  * @returns - an instruction to update the vault config
904
972
  */
905
- private updateUninitialisedVaultConfigIx(
973
+ private async updateUninitialisedVaultConfigIx(
906
974
  admin: TransactionSigner,
907
975
  vault: Address,
908
976
  mode: VaultConfigFieldKind,
909
977
  value: string
910
- ): Instruction {
978
+ ): Promise<Instruction> {
979
+ const globalConfig = await getKvaultGlobalConfigPda(this._kaminoVaultProgramId);
911
980
  const updateVaultConfigAccs: UpdateVaultConfigAccounts = {
912
- vaultAdminAuthority: admin,
981
+ signer: admin,
982
+ globalConfig: globalConfig,
913
983
  vaultState: vault,
914
984
  klendProgram: this._kaminoLendProgramId,
915
985
  };
916
986
 
917
987
  const updateVaultConfigArgs: UpdateVaultConfigArgs = {
918
988
  entry: mode,
919
- data: Buffer.from([0]),
989
+ data: this.getValueForModeAsBuffer(mode, value),
920
990
  };
921
991
 
922
- if (isNaN(+value)) {
923
- if (mode.kind === new VaultConfigField.Name().kind) {
924
- const data = Array.from(this.encodeVaultName(value));
925
- updateVaultConfigArgs.data = Buffer.from(data);
926
- } else {
927
- const data = address(value);
928
- updateVaultConfigArgs.data = Buffer.from(addressEncoder.encode(data));
929
- }
930
- } else {
931
- const buffer = Buffer.alloc(8);
932
- buffer.writeBigUInt64LE(BigInt(value.toString()));
933
- updateVaultConfigArgs.data = buffer;
934
- }
935
-
936
992
  const updateVaultConfigIx = updateVaultConfig(
937
993
  updateVaultConfigArgs,
938
994
  updateVaultConfigAccs,
@@ -1220,6 +1276,92 @@ export class KaminoVaultClient {
1220
1276
  return depositIxs;
1221
1277
  }
1222
1278
 
1279
+ // todo (silviu): after all tx indexing works for buy/sell ixs remove this function and use the buyIx in the deposit function above
1280
+ async buySharesIxs(
1281
+ user: TransactionSigner,
1282
+ vault: KaminoVault,
1283
+ tokenAmount: Decimal,
1284
+ vaultReservesMap?: Map<Address, KaminoReserve>,
1285
+ farmState?: FarmState
1286
+ ): Promise<DepositIxs> {
1287
+ const vaultState = await vault.getState();
1288
+
1289
+ const tokenProgramID = vaultState.tokenProgram;
1290
+ const userTokenAta = await getAssociatedTokenAddress(vaultState.tokenMint, user.address, tokenProgramID);
1291
+ const createAtasIxs: Instruction[] = [];
1292
+ const closeAtasIxs: Instruction[] = [];
1293
+ if (vaultState.tokenMint === WRAPPED_SOL_MINT) {
1294
+ const [{ ata: wsolAta, createAtaIx: createWsolAtaIxn }] = await createAtasIdempotent(user, [
1295
+ {
1296
+ mint: WRAPPED_SOL_MINT,
1297
+ tokenProgram: TOKEN_PROGRAM_ADDRESS,
1298
+ },
1299
+ ]);
1300
+ createAtasIxs.push(createWsolAtaIxn);
1301
+ const transferWsolIxs = getTransferWsolIxs(
1302
+ user,
1303
+ wsolAta,
1304
+ lamports(
1305
+ BigInt(numberToLamportsDecimal(tokenAmount, vaultState.tokenMintDecimals.toNumber()).ceil().toString())
1306
+ )
1307
+ );
1308
+ createAtasIxs.push(...transferWsolIxs);
1309
+ }
1310
+
1311
+ const [{ ata: userSharesAta, createAtaIx: createSharesAtaIxs }] = await createAtasIdempotent(user, [
1312
+ {
1313
+ mint: vaultState.sharesMint,
1314
+ tokenProgram: TOKEN_PROGRAM_ADDRESS,
1315
+ },
1316
+ ]);
1317
+ createAtasIxs.push(createSharesAtaIxs);
1318
+
1319
+ const eventAuthority = await getEventAuthorityPda(this._kaminoVaultProgramId);
1320
+ const buyAccounts: BuyAccounts = {
1321
+ user: user,
1322
+ vaultState: vault.address,
1323
+ tokenVault: vaultState.tokenVault,
1324
+ tokenMint: vaultState.tokenMint,
1325
+ baseVaultAuthority: vaultState.baseVaultAuthority,
1326
+ sharesMint: vaultState.sharesMint,
1327
+ userTokenAta: userTokenAta,
1328
+ userSharesAta: userSharesAta,
1329
+ tokenProgram: tokenProgramID,
1330
+ klendProgram: this._kaminoLendProgramId,
1331
+ sharesTokenProgram: TOKEN_PROGRAM_ADDRESS,
1332
+ eventAuthority: eventAuthority,
1333
+ program: this._kaminoVaultProgramId,
1334
+ };
1335
+
1336
+ const buyArgs: BuyArgs = {
1337
+ maxAmount: new BN(
1338
+ numberToLamportsDecimal(tokenAmount, vaultState.tokenMintDecimals.toNumber()).floor().toString()
1339
+ ),
1340
+ };
1341
+
1342
+ let buyIx = buy(buyArgs, buyAccounts, undefined, this._kaminoVaultProgramId);
1343
+
1344
+ const vaultReserves = this.getVaultReserves(vaultState);
1345
+
1346
+ const vaultReservesState = vaultReservesMap ? vaultReservesMap : await this.loadVaultReserves(vaultState);
1347
+ buyIx = this.appendRemainingAccountsForVaultReserves(buyIx, vaultReserves, vaultReservesState);
1348
+
1349
+ const depositIxs: DepositIxs = {
1350
+ depositIxs: [...createAtasIxs, buyIx, ...closeAtasIxs],
1351
+ stakeInFarmIfNeededIxs: [],
1352
+ };
1353
+
1354
+ // if there is no farm, we can return the deposit instructions, otherwise include the stake ix in the response
1355
+ if (!(await vault.hasFarm())) {
1356
+ return depositIxs;
1357
+ }
1358
+
1359
+ // if there is a farm, stake the shares
1360
+ const stakeSharesIxs = await this.stakeSharesIxs(user, vault, undefined, farmState);
1361
+ depositIxs.stakeInFarmIfNeededIxs = stakeSharesIxs;
1362
+ return depositIxs;
1363
+ }
1364
+
1223
1365
  /**
1224
1366
  * This function creates instructions to stake the shares in the vault farm if the vault has a farm
1225
1367
  * @param user - user to stake
@@ -1389,6 +1531,135 @@ export class KaminoVaultClient {
1389
1531
  return withdrawIxs;
1390
1532
  }
1391
1533
 
1534
+ async sellSharesIxs(
1535
+ user: TransactionSigner,
1536
+ vault: KaminoVault,
1537
+ shareAmountToWithdraw: Decimal,
1538
+ slot: Slot,
1539
+ vaultReservesMap?: Map<Address, KaminoReserve>,
1540
+ farmState?: FarmState
1541
+ ): Promise<WithdrawIxs> {
1542
+ const vaultState = await vault.getState();
1543
+ const hasFarm = await vault.hasFarm();
1544
+
1545
+ const withdrawIxs: WithdrawIxs = {
1546
+ unstakeFromFarmIfNeededIxs: [],
1547
+ withdrawIxs: [],
1548
+ postWithdrawIxs: [],
1549
+ };
1550
+
1551
+ // compute the total shares the user has (in ATA + in farm) and check if they want to withdraw everything or just a part
1552
+ let userSharesAtaBalance = new Decimal(0);
1553
+ const userSharesAta = await getAssociatedTokenAddress(vaultState.sharesMint, user.address);
1554
+ const userSharesAtaState = await fetchMaybeToken(this.getConnection(), userSharesAta);
1555
+ if (userSharesAtaState.exists) {
1556
+ const userSharesAtaBalanceInLamports = getTokenBalanceFromAccountInfoLamports(userSharesAtaState);
1557
+ userSharesAtaBalance = userSharesAtaBalanceInLamports.div(
1558
+ new Decimal(10).pow(vaultState.sharesMintDecimals.toString())
1559
+ );
1560
+ }
1561
+
1562
+ let userSharesInFarm = new Decimal(0);
1563
+ if (hasFarm) {
1564
+ userSharesInFarm = await getUserSharesInTokensStakedInFarm(
1565
+ this.getConnection(),
1566
+ user.address,
1567
+ vaultState.vaultFarm,
1568
+ vaultState.sharesMintDecimals.toNumber()
1569
+ );
1570
+ }
1571
+
1572
+ let sharesToWithdraw = shareAmountToWithdraw;
1573
+ const totalUserShares = userSharesAtaBalance.add(userSharesInFarm);
1574
+ let withdrawAllShares = false;
1575
+ if (sharesToWithdraw.gt(totalUserShares)) {
1576
+ sharesToWithdraw = new Decimal(U64_MAX.toString()).div(
1577
+ new Decimal(10).pow(vaultState.sharesMintDecimals.toString())
1578
+ );
1579
+ withdrawAllShares = true;
1580
+ }
1581
+
1582
+ // if not enough shares in ATA unstake from farm
1583
+ const sharesInAtaAreEnoughForWithdraw = sharesToWithdraw.lte(userSharesAtaBalance);
1584
+ if (hasFarm && !sharesInAtaAreEnoughForWithdraw && userSharesInFarm.gt(0)) {
1585
+ // if we need to unstake we need to make sure share ata is created
1586
+ const [{ createAtaIx }] = await createAtasIdempotent(user, [
1587
+ {
1588
+ mint: vaultState.sharesMint,
1589
+ tokenProgram: TOKEN_PROGRAM_ADDRESS,
1590
+ },
1591
+ ]);
1592
+ withdrawIxs.unstakeFromFarmIfNeededIxs.push(createAtaIx);
1593
+ let shareLamportsToWithdraw = new Decimal(U64_MAX.toString());
1594
+ if (!withdrawAllShares) {
1595
+ const sharesToWithdrawFromFarm = sharesToWithdraw.sub(userSharesAtaBalance);
1596
+ shareLamportsToWithdraw = collToLamportsDecimal(
1597
+ sharesToWithdrawFromFarm,
1598
+ vaultState.sharesMintDecimals.toNumber()
1599
+ );
1600
+ }
1601
+ const unstakeAndWithdrawFromFarmIxs = await getFarmUnstakeAndWithdrawIxs(
1602
+ this.getConnection(),
1603
+ user,
1604
+ shareLamportsToWithdraw,
1605
+ vaultState.vaultFarm,
1606
+ farmState
1607
+ );
1608
+ withdrawIxs.unstakeFromFarmIfNeededIxs.push(unstakeAndWithdrawFromFarmIxs.unstakeIx);
1609
+ withdrawIxs.unstakeFromFarmIfNeededIxs.push(unstakeAndWithdrawFromFarmIxs.withdrawIx);
1610
+ }
1611
+
1612
+ // if the vault has allocations withdraw otherwise wtihdraw from available ix
1613
+ const vaultAllocation = vaultState.vaultAllocationStrategy.find(
1614
+ (allocation) => allocation.reserve !== DEFAULT_PUBLIC_KEY
1615
+ );
1616
+
1617
+ if (vaultAllocation) {
1618
+ const withdrawFromVaultIxs = await this.sellSharesWithReserveIxs(
1619
+ user,
1620
+ vault,
1621
+ sharesToWithdraw,
1622
+ totalUserShares,
1623
+ slot,
1624
+ vaultReservesMap
1625
+ );
1626
+ withdrawIxs.withdrawIxs = withdrawFromVaultIxs;
1627
+ } else {
1628
+ const withdrawFromVaultIxs = await this.withdrawFromAvailableIxs(user, vault, sharesToWithdraw);
1629
+ withdrawIxs.withdrawIxs = withdrawFromVaultIxs;
1630
+ }
1631
+
1632
+ // if the vault is for SOL return the ix to unwrap the SOL
1633
+ if (vaultState.tokenMint === WRAPPED_SOL_MINT) {
1634
+ const userWsolAta = await getAssociatedTokenAddress(WRAPPED_SOL_MINT, user.address);
1635
+ const unwrapIx = getCloseAccountInstruction(
1636
+ {
1637
+ account: userWsolAta,
1638
+ owner: user,
1639
+ destination: user.address,
1640
+ },
1641
+ { programAddress: TOKEN_PROGRAM_ADDRESS }
1642
+ );
1643
+ withdrawIxs.postWithdrawIxs.push(unwrapIx);
1644
+ }
1645
+
1646
+ // if we burn all of user's shares close its shares ATA
1647
+ const burnAllUserShares = sharesToWithdraw.gt(totalUserShares);
1648
+ if (burnAllUserShares) {
1649
+ const closeAtaIx = getCloseAccountInstruction(
1650
+ {
1651
+ account: userSharesAta,
1652
+ owner: user,
1653
+ destination: user.address,
1654
+ },
1655
+ { programAddress: TOKEN_PROGRAM_ADDRESS }
1656
+ );
1657
+ withdrawIxs.postWithdrawIxs.push(closeAtaIx);
1658
+ }
1659
+
1660
+ return withdrawIxs;
1661
+ }
1662
+
1392
1663
  private async withdrawFromAvailableIxs(
1393
1664
  user: TransactionSigner,
1394
1665
  vault: KaminoVault,
@@ -1417,6 +1688,115 @@ export class KaminoVaultClient {
1417
1688
  return [createAtaIx, withdrawFromAvailableIxn];
1418
1689
  }
1419
1690
 
1691
+ private async sellSharesWithReserveIxs(
1692
+ user: TransactionSigner,
1693
+ vault: KaminoVault,
1694
+ shareAmount: Decimal,
1695
+ allUserShares: Decimal,
1696
+ slot: Slot,
1697
+ vaultReservesMap?: Map<Address, KaminoReserve>
1698
+ ): Promise<Instruction[]> {
1699
+ const vaultState = await vault.getState();
1700
+
1701
+ const vaultReservesState = vaultReservesMap ? vaultReservesMap : await this.loadVaultReserves(vaultState);
1702
+ const userSharesAta = await getAssociatedTokenAddress(vaultState.sharesMint, user.address);
1703
+ const [{ ata: userTokenAta, createAtaIx }] = await createAtasIdempotent(user, [
1704
+ {
1705
+ mint: vaultState.tokenMint,
1706
+ tokenProgram: vaultState.tokenProgram,
1707
+ },
1708
+ ]);
1709
+
1710
+ const withdrawAllShares = shareAmount.gte(allUserShares);
1711
+ const actualSharesToWithdraw = shareAmount.lte(allUserShares) ? shareAmount : allUserShares;
1712
+ const shareLamportsToWithdraw = collToLamportsDecimal(
1713
+ actualSharesToWithdraw,
1714
+ vaultState.sharesMintDecimals.toNumber()
1715
+ );
1716
+ const tokensPerShare = await this.getTokensPerShareSingleVault(vault, slot);
1717
+ const sharesPerToken = new Decimal(1).div(tokensPerShare);
1718
+ const tokensToWithdraw = shareLamportsToWithdraw.mul(tokensPerShare);
1719
+ let tokenLeftToWithdraw = tokensToWithdraw;
1720
+ const availableTokens = new Decimal(vaultState.tokenAvailable.toString());
1721
+ tokenLeftToWithdraw = tokenLeftToWithdraw.sub(availableTokens);
1722
+
1723
+ type ReserveWithTokensToWithdraw = { reserve: Address; shares: Decimal };
1724
+
1725
+ const reserveWithSharesAmountToWithdraw: ReserveWithTokensToWithdraw[] = [];
1726
+ let isFirstWithdraw = true;
1727
+
1728
+ if (tokenLeftToWithdraw.lte(0)) {
1729
+ // Availabe enough to withdraw all - using the first existent reserve
1730
+ const firstReserve = vaultState.vaultAllocationStrategy.find((reserve) => reserve.reserve !== DEFAULT_PUBLIC_KEY);
1731
+ if (withdrawAllShares) {
1732
+ reserveWithSharesAmountToWithdraw.push({
1733
+ reserve: firstReserve!.reserve,
1734
+ shares: new Decimal(U64_MAX.toString()),
1735
+ });
1736
+ } else {
1737
+ reserveWithSharesAmountToWithdraw.push({
1738
+ reserve: firstReserve!.reserve,
1739
+ shares: shareLamportsToWithdraw,
1740
+ });
1741
+ }
1742
+ } else {
1743
+ // Get decreasing order sorted available liquidity to withdraw from each reserve allocated to
1744
+ const reserveAllocationAvailableLiquidityToWithdraw = await this.getReserveAllocationAvailableLiquidityToWithdraw(
1745
+ vault,
1746
+ slot,
1747
+ vaultReservesState
1748
+ );
1749
+ // sort
1750
+ const reserveAllocationAvailableLiquidityToWithdrawSorted = [
1751
+ ...reserveAllocationAvailableLiquidityToWithdraw.entries(),
1752
+ ].sort((a, b) => b[1].sub(a[1]).toNumber());
1753
+
1754
+ reserveAllocationAvailableLiquidityToWithdrawSorted.forEach(([key, availableLiquidityToWithdraw], _) => {
1755
+ if (tokenLeftToWithdraw.gt(0)) {
1756
+ let tokensToWithdrawFromReserve = Decimal.min(tokenLeftToWithdraw, availableLiquidityToWithdraw);
1757
+ if (isFirstWithdraw) {
1758
+ tokensToWithdrawFromReserve = tokensToWithdrawFromReserve.add(availableTokens);
1759
+ isFirstWithdraw = false;
1760
+ }
1761
+ if (withdrawAllShares) {
1762
+ reserveWithSharesAmountToWithdraw.push({ reserve: key, shares: new Decimal(U64_MAX.toString()) });
1763
+ } else {
1764
+ // round up to the nearest integer the shares to withdraw
1765
+ const sharesToWithdrawFromReserve = tokensToWithdrawFromReserve.mul(sharesPerToken).floor();
1766
+ reserveWithSharesAmountToWithdraw.push({ reserve: key, shares: sharesToWithdrawFromReserve });
1767
+ }
1768
+
1769
+ tokenLeftToWithdraw = tokenLeftToWithdraw.sub(tokensToWithdrawFromReserve);
1770
+ }
1771
+ });
1772
+ }
1773
+
1774
+ const withdrawIxs: Instruction[] = [];
1775
+ withdrawIxs.push(createAtaIx);
1776
+ for (let reserveIndex = 0; reserveIndex < reserveWithSharesAmountToWithdraw.length; reserveIndex++) {
1777
+ const reserveWithTokens = reserveWithSharesAmountToWithdraw[reserveIndex];
1778
+ const reserveState = vaultReservesState.get(reserveWithTokens.reserve);
1779
+ if (reserveState === undefined) {
1780
+ throw new Error(`Reserve ${reserveWithTokens.reserve} not found in vault reserves map`);
1781
+ }
1782
+ const marketAddress = reserveState.state.lendingMarket;
1783
+
1784
+ const withdrawFromReserveIx = await this.sellIx(
1785
+ user,
1786
+ vault,
1787
+ vaultState,
1788
+ marketAddress,
1789
+ { address: reserveWithTokens.reserve, state: reserveState.state },
1790
+ userSharesAta,
1791
+ userTokenAta,
1792
+ reserveWithTokens.shares,
1793
+ vaultReservesState
1794
+ );
1795
+ withdrawIxs.push(withdrawFromReserveIx);
1796
+ }
1797
+
1798
+ return withdrawIxs;
1799
+ }
1420
1800
  private async withdrawWithReserveIxs(
1421
1801
  user: TransactionSigner,
1422
1802
  vault: KaminoVault,
@@ -1666,6 +2046,12 @@ export class KaminoVaultClient {
1666
2046
  ixs.push(createAtaIx);
1667
2047
  }
1668
2048
 
2049
+ const reserveWhitelistEntryOption = await getReserveWhitelistEntryIfExists(
2050
+ reserve.address,
2051
+ this.getConnection(),
2052
+ this._kaminoVaultProgramId
2053
+ );
2054
+
1669
2055
  const investAccounts: InvestAccounts = {
1670
2056
  payer,
1671
2057
  vaultState: vault.address,
@@ -1678,6 +2064,7 @@ export class KaminoVaultClient {
1678
2064
  lendingMarketAuthority: lendingMarketAuth,
1679
2065
  reserveLiquiditySupply: reserve.state.liquidity.supplyVault,
1680
2066
  reserveCollateralMint: reserve.state.collateral.mintPubkey,
2067
+ reserveWhitelistEntry: reserveWhitelistEntryOption,
1681
2068
  klendProgram: this._kaminoLendProgramId,
1682
2069
  instructionSysvarAccount: SYSVAR_INSTRUCTIONS_ADDRESS,
1683
2070
  tokenProgram: tokenProgram,
@@ -1707,6 +2094,89 @@ export class KaminoVaultClient {
1707
2094
  return decodeVaultName(token);
1708
2095
  }
1709
2096
 
2097
+ /** Helper to serialize value as Buffer for updateVaultConfig instruction */
2098
+ private getValueForModeAsBuffer(mode: VaultConfigFieldKind, value: string): Buffer {
2099
+ const isWhitelistOnlyFlag =
2100
+ mode.kind === new VaultConfigField.AllowInvestInWhitelistedReservesOnly().kind ||
2101
+ mode.kind === new VaultConfigField.AllowAllocationsInWhitelistedReservesOnly().kind;
2102
+
2103
+ if (isWhitelistOnlyFlag) {
2104
+ const flag = parseBooleanFlag(value);
2105
+ return Buffer.from([flag]);
2106
+ } else if (isNaN(+value)) {
2107
+ if (mode.kind === new VaultConfigField.Name().kind) {
2108
+ const data = Array.from(this.encodeVaultName(value));
2109
+ return Buffer.from(data);
2110
+ } else {
2111
+ const data = address(value);
2112
+ return Buffer.from(addressEncoder.encode(data));
2113
+ }
2114
+ } else {
2115
+ const buffer = Buffer.alloc(8);
2116
+ buffer.writeBigUInt64LE(BigInt(value.toString()));
2117
+ return buffer;
2118
+ }
2119
+ }
2120
+
2121
+ private async sellIx(
2122
+ user: TransactionSigner,
2123
+ vault: KaminoVault,
2124
+ vaultState: VaultState,
2125
+ marketAddress: Address,
2126
+ reserve: ReserveWithAddress,
2127
+ userSharesAta: Address,
2128
+ userTokenAta: Address,
2129
+ shareAmountLamports: Decimal,
2130
+ vaultReservesState: Map<Address, KaminoReserve>
2131
+ ): Promise<Instruction> {
2132
+ const [lendingMarketAuth] = await lendingMarketAuthPda(marketAddress, this._kaminoLendProgramId);
2133
+
2134
+ const globalConfig = await getKvaultGlobalConfigPda(this._kaminoVaultProgramId);
2135
+ const eventAuthority = await getEventAuthorityPda(this._kaminoVaultProgramId);
2136
+ const sellAccounts: SellAccounts = {
2137
+ withdrawFromAvailable: {
2138
+ user,
2139
+ vaultState: vault.address,
2140
+ globalConfig: globalConfig,
2141
+ tokenVault: vaultState.tokenVault,
2142
+ baseVaultAuthority: vaultState.baseVaultAuthority,
2143
+ userTokenAta: userTokenAta,
2144
+ tokenMint: vaultState.tokenMint,
2145
+ userSharesAta: userSharesAta,
2146
+ sharesMint: vaultState.sharesMint,
2147
+ tokenProgram: vaultState.tokenProgram,
2148
+ sharesTokenProgram: TOKEN_PROGRAM_ADDRESS,
2149
+ klendProgram: this._kaminoLendProgramId,
2150
+ eventAuthority: eventAuthority,
2151
+ program: this._kaminoVaultProgramId,
2152
+ },
2153
+ withdrawFromReserveAccounts: {
2154
+ vaultState: vault.address,
2155
+ reserve: reserve.address,
2156
+ ctokenVault: await getCTokenVaultPda(vault.address, reserve.address, this._kaminoVaultProgramId),
2157
+ lendingMarket: marketAddress,
2158
+ lendingMarketAuthority: lendingMarketAuth,
2159
+ reserveLiquiditySupply: reserve.state.liquidity.supplyVault,
2160
+ reserveCollateralMint: reserve.state.collateral.mintPubkey,
2161
+ reserveCollateralTokenProgram: TOKEN_PROGRAM_ADDRESS,
2162
+ instructionSysvarAccount: SYSVAR_INSTRUCTIONS_ADDRESS,
2163
+ },
2164
+ eventAuthority: eventAuthority,
2165
+ program: this._kaminoVaultProgramId,
2166
+ };
2167
+
2168
+ const sellArgs: SellArgs = {
2169
+ sharesAmount: new BN(shareAmountLamports.floor().toString()),
2170
+ };
2171
+
2172
+ let sellIxn = sell(sellArgs, sellAccounts, undefined, this._kaminoVaultProgramId);
2173
+
2174
+ const vaultReserves = this.getVaultReserves(vaultState);
2175
+ sellIxn = this.appendRemainingAccountsForVaultReserves(sellIxn, vaultReserves, vaultReservesState);
2176
+
2177
+ return sellIxn;
2178
+ }
2179
+
1710
2180
  private async withdrawIx(
1711
2181
  user: TransactionSigner,
1712
2182
  vault: KaminoVault,
@@ -1720,11 +2190,13 @@ export class KaminoVaultClient {
1720
2190
  ): Promise<Instruction> {
1721
2191
  const [lendingMarketAuth] = await lendingMarketAuthPda(marketAddress, this._kaminoLendProgramId);
1722
2192
 
2193
+ const globalConfig = await getKvaultGlobalConfigPda(this._kaminoVaultProgramId);
1723
2194
  const eventAuthority = await getEventAuthorityPda(this._kaminoVaultProgramId);
1724
2195
  const withdrawAccounts: WithdrawAccounts = {
1725
2196
  withdrawFromAvailable: {
1726
2197
  user,
1727
2198
  vaultState: vault.address,
2199
+ globalConfig: globalConfig,
1728
2200
  tokenVault: vaultState.tokenVault,
1729
2201
  baseVaultAuthority: vaultState.baseVaultAuthority,
1730
2202
  userTokenAta: userTokenAta,
@@ -1772,10 +2244,12 @@ export class KaminoVaultClient {
1772
2244
  userTokenAta: Address,
1773
2245
  shareAmountLamports: Decimal
1774
2246
  ): Promise<Instruction> {
2247
+ const globalConfig = await getKvaultGlobalConfigPda(this._kaminoVaultProgramId);
1775
2248
  const eventAuthority = await getEventAuthorityPda(this._kaminoVaultProgramId);
1776
2249
  const withdrawFromAvailableAccounts: WithdrawFromAvailableAccounts = {
1777
2250
  user,
1778
2251
  vaultState: vault.address,
2252
+ globalConfig: globalConfig,
1779
2253
  tokenVault: vaultState.tokenVault,
1780
2254
  baseVaultAuthority: vaultState.baseVaultAuthority,
1781
2255
  userTokenAta,
@@ -4162,6 +4636,39 @@ export async function getEventAuthorityPda(kaminoVaultProgramId: Address): Promi
4162
4636
  )[0];
4163
4637
  }
4164
4638
 
4639
+ export async function getKvaultGlobalConfigPda(kaminoVaultProgramId: Address): Promise<Address> {
4640
+ return (
4641
+ await getProgramDerivedAddress({
4642
+ seeds: [Buffer.from(GLOBAL_CONFIG_STATE_SEED)],
4643
+ programAddress: kaminoVaultProgramId,
4644
+ })
4645
+ )[0];
4646
+ }
4647
+
4648
+ export async function getReserveWhitelistEntryPda(
4649
+ reserveAddress: Address,
4650
+ kaminoVaultProgramId: Address
4651
+ ): Promise<Address> {
4652
+ return (
4653
+ await getProgramDerivedAddress({
4654
+ seeds: [Buffer.from(WHITELISTED_RESERVES_SEED), addressEncoder.encode(reserveAddress)],
4655
+ programAddress: kaminoVaultProgramId,
4656
+ })
4657
+ )[0];
4658
+ }
4659
+
4660
+ async function getReserveWhitelistEntryIfExists(
4661
+ reserveAddress: Address,
4662
+ rpc: Rpc<SolanaRpcApi>,
4663
+ kaminoVaultProgramId: Address
4664
+ ): Promise<Option<Address>> {
4665
+ const reserveWhitelistEntry = await getReserveWhitelistEntryPda(reserveAddress, kaminoVaultProgramId);
4666
+ const reserveWhitelistEntryAccount = await fetchEncodedAccount(rpc, reserveWhitelistEntry, {
4667
+ commitment: 'processed',
4668
+ });
4669
+ return reserveWhitelistEntryAccount.exists ? some(reserveWhitelistEntry) : none<Address>();
4670
+ }
4671
+
4165
4672
  function parseVaultAdmin(vault: VaultState, signer?: TransactionSigner) {
4166
4673
  return signer ?? noopSigner(vault.vaultAdminAuthority);
4167
4674
  }