@kamino-finance/klend-sdk 7.3.3 → 7.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -697,6 +697,12 @@ class KaminoVaultClient {
697
697
  * @returns - an instance of DepositIxs which contains the instructions to deposit in vault and the instructions to stake the shares in the farm if the vault has a farm
698
698
  */
699
699
  async depositIxs(user, vault, tokenAmount, vaultReservesMap, farmState) {
700
+ return this.buildShareEntryIxs('deposit', user, vault, tokenAmount, vaultReservesMap, farmState);
701
+ }
702
+ async buySharesIxs(user, vault, tokenAmount, vaultReservesMap, farmState) {
703
+ return this.buildShareEntryIxs('buy', user, vault, tokenAmount, vaultReservesMap, farmState);
704
+ }
705
+ async buildShareEntryIxs(mode, user, vault, tokenAmount, vaultReservesMap, farmState) {
700
706
  const vaultState = await vault.getState();
701
707
  const tokenProgramID = vaultState.tokenProgram;
702
708
  const userTokenAta = await (0, lib_1.getAssociatedTokenAddress)(vaultState.tokenMint, user.address, tokenProgramID);
@@ -721,40 +727,63 @@ class KaminoVaultClient {
721
727
  ]);
722
728
  createAtasIxs.push(createSharesAtaIxs);
723
729
  const eventAuthority = await getEventAuthorityPda(this._kaminoVaultProgramId);
724
- const buyAccounts = {
725
- user: user,
726
- vaultState: vault.address,
727
- tokenVault: vaultState.tokenVault,
728
- tokenMint: vaultState.tokenMint,
729
- baseVaultAuthority: vaultState.baseVaultAuthority,
730
- sharesMint: vaultState.sharesMint,
731
- userTokenAta: userTokenAta,
732
- userSharesAta: userSharesAta,
733
- tokenProgram: tokenProgramID,
734
- klendProgram: this._kaminoLendProgramId,
735
- sharesTokenProgram: token_1.TOKEN_PROGRAM_ADDRESS,
736
- eventAuthority: eventAuthority,
737
- program: this._kaminoVaultProgramId,
738
- };
739
- const buyArgs = {
740
- maxAmount: new bn_js_1.default((0, utils_1.numberToLamportsDecimal)(tokenAmount, vaultState.tokenMintDecimals.toNumber()).floor().toString()),
741
- };
742
- let buyIx = (0, instructions_1.buy)(buyArgs, buyAccounts, undefined, this._kaminoVaultProgramId);
730
+ const tokenAmountLamports = (0, utils_1.numberToLamportsDecimal)(tokenAmount, vaultState.tokenMintDecimals.toNumber()).floor();
731
+ let entryIx;
732
+ if (mode === 'deposit') {
733
+ const depositAccounts = {
734
+ user,
735
+ vaultState: vault.address,
736
+ tokenVault: vaultState.tokenVault,
737
+ tokenMint: vaultState.tokenMint,
738
+ baseVaultAuthority: vaultState.baseVaultAuthority,
739
+ sharesMint: vaultState.sharesMint,
740
+ userTokenAta,
741
+ userSharesAta,
742
+ tokenProgram: tokenProgramID,
743
+ klendProgram: this._kaminoLendProgramId,
744
+ sharesTokenProgram: token_1.TOKEN_PROGRAM_ADDRESS,
745
+ eventAuthority,
746
+ program: this._kaminoVaultProgramId,
747
+ };
748
+ const depositArgs = {
749
+ maxAmount: new bn_js_1.default(tokenAmountLamports.toString()),
750
+ };
751
+ entryIx = (0, instructions_1.deposit)(depositArgs, depositAccounts, undefined, this._kaminoVaultProgramId);
752
+ }
753
+ else {
754
+ const buyAccounts = {
755
+ user,
756
+ vaultState: vault.address,
757
+ tokenVault: vaultState.tokenVault,
758
+ tokenMint: vaultState.tokenMint,
759
+ baseVaultAuthority: vaultState.baseVaultAuthority,
760
+ sharesMint: vaultState.sharesMint,
761
+ userTokenAta,
762
+ userSharesAta,
763
+ tokenProgram: tokenProgramID,
764
+ klendProgram: this._kaminoLendProgramId,
765
+ sharesTokenProgram: token_1.TOKEN_PROGRAM_ADDRESS,
766
+ eventAuthority,
767
+ program: this._kaminoVaultProgramId,
768
+ };
769
+ const buyArgs = {
770
+ maxAmount: new bn_js_1.default(tokenAmountLamports.toString()),
771
+ };
772
+ entryIx = (0, instructions_1.buy)(buyArgs, buyAccounts, undefined, this._kaminoVaultProgramId);
773
+ }
743
774
  const vaultReserves = this.getVaultReserves(vaultState);
744
775
  const vaultReservesState = vaultReservesMap ? vaultReservesMap : await this.loadVaultReserves(vaultState);
745
- buyIx = this.appendRemainingAccountsForVaultReserves(buyIx, vaultReserves, vaultReservesState);
746
- const depositIxs = {
747
- depositIxs: [...createAtasIxs, buyIx, ...closeAtasIxs],
776
+ entryIx = this.appendRemainingAccountsForVaultReserves(entryIx, vaultReserves, vaultReservesState);
777
+ const result = {
778
+ depositIxs: [...createAtasIxs, entryIx, ...closeAtasIxs],
748
779
  stakeInFarmIfNeededIxs: [],
749
780
  };
750
- // if there is no farm, we can return the deposit instructions, otherwise include the stake ix in the response
751
781
  if (!(await vault.hasFarm())) {
752
- return depositIxs;
782
+ return result;
753
783
  }
754
- // if there is a farm, stake the shares
755
784
  const stakeSharesIxs = await this.stakeSharesIxs(user, vault, undefined, farmState);
756
- depositIxs.stakeInFarmIfNeededIxs = stakeSharesIxs;
757
- return depositIxs;
785
+ result.stakeInFarmIfNeededIxs = stakeSharesIxs;
786
+ return result;
758
787
  }
759
788
  /**
760
789
  * This function creates instructions to stake the shares in the vault farm if the vault has a farm
@@ -788,6 +817,22 @@ class KaminoVaultClient {
788
817
  * @returns an array of instructions to create missing ATAs if needed and the withdraw instructions
789
818
  */
790
819
  async withdrawIxs(user, vault, shareAmountToWithdraw, slot, vaultReservesMap, farmState) {
820
+ return this.buildShareExitIxs('withdraw', user, vault, shareAmountToWithdraw, slot, vaultReservesMap, farmState);
821
+ }
822
+ /**
823
+ * This function will return the missing ATA creation instructions, as well as one or multiple withdraw instructions, based on how many reserves it's needed to withdraw from. This might have to be split in multiple transactions
824
+ * @param user - user to sell shares for vault tokens
825
+ * @param vault - vault to sell shares from
826
+ * @param shareAmount - share amount to sell (in tokens, not lamports), in order to withdraw everything, any value > user share amount
827
+ * @param slot - current slot, used to estimate the interest earned in the different reserves with allocation from the vault
828
+ * @param [vaultReservesMap] - optional parameter; a hashmap from each reserve pubkey to the reserve state. If provided the function will be significantly faster as it will not have to fetch the reserves
829
+ * @param [farmState] - the state of the vault farm, if the vault has a farm. Optional. If not provided, it will be fetched
830
+ * @returns an array of instructions to create missing ATAs if needed and the withdraw instructions
831
+ */
832
+ async sellSharesIxs(user, vault, shareAmountToWithdraw, slot, vaultReservesMap, farmState) {
833
+ return this.buildShareExitIxs('sell', user, vault, shareAmountToWithdraw, slot, vaultReservesMap, farmState);
834
+ }
835
+ async buildShareExitIxs(mode, user, vault, shareAmountToWithdraw, slot, vaultReservesMap, farmState) {
791
836
  const vaultState = await vault.getState();
792
837
  const hasFarm = await vault.hasFarm();
793
838
  const withdrawIxs = {
@@ -834,10 +879,21 @@ class KaminoVaultClient {
834
879
  withdrawIxs.unstakeFromFarmIfNeededIxs.push(unstakeAndWithdrawFromFarmIxs.unstakeIx);
835
880
  withdrawIxs.unstakeFromFarmIfNeededIxs.push(unstakeAndWithdrawFromFarmIxs.withdrawIx);
836
881
  }
837
- // if the vault has allocations withdraw otherwise wtihdraw from available ix
838
- const vaultAllocation = vaultState.vaultAllocationStrategy.find((allocation) => allocation.reserve !== lib_1.DEFAULT_PUBLIC_KEY);
839
- if (vaultAllocation) {
840
- const withdrawFromVaultIxs = await this.withdrawWithReserveIxs(user, vault, sharesToWithdraw, totalUserShares, slot, vaultReservesMap);
882
+ const hasAllocatedReserves = vaultState.vaultAllocationStrategy.some((allocation) => allocation.reserve !== lib_1.DEFAULT_PUBLIC_KEY);
883
+ if (hasAllocatedReserves) {
884
+ const reserveExitBuilder = mode === 'withdraw'
885
+ ? (params) => this.withdrawIx(params.user, params.vault, params.vaultState, params.marketAddress, params.reserve, params.userSharesAta, params.userTokenAta, params.shareAmountLamports, params.vaultReservesState)
886
+ : (params) => this.sellIx(params.user, params.vault, params.vaultState, params.marketAddress, params.reserve, params.userSharesAta, params.userTokenAta, params.shareAmountLamports, params.vaultReservesState);
887
+ const withdrawFromVaultIxs = await this.buildReserveExitIxs({
888
+ user,
889
+ vault,
890
+ vaultState,
891
+ shareAmount: sharesToWithdraw,
892
+ allUserShares: totalUserShares,
893
+ slot,
894
+ vaultReservesMap,
895
+ builder: reserveExitBuilder,
896
+ });
841
897
  withdrawIxs.withdrawIxs = withdrawFromVaultIxs;
842
898
  }
843
899
  else {
@@ -879,8 +935,7 @@ class KaminoVaultClient {
879
935
  const withdrawFromAvailableIxn = await this.withdrawFromAvailableIx(user, vault, vaultState, userSharesAta, userTokenAta, shareLamportsToWithdraw);
880
936
  return [createAtaIx, withdrawFromAvailableIxn];
881
937
  }
882
- async withdrawWithReserveIxs(user, vault, shareAmount, allUserShares, slot, vaultReservesMap) {
883
- const vaultState = await vault.getState();
938
+ async buildReserveExitIxs({ user, vault, vaultState, shareAmount, allUserShares, slot, vaultReservesMap, builder, }) {
884
939
  const vaultReservesState = vaultReservesMap ? vaultReservesMap : await this.loadVaultReserves(vaultState);
885
940
  const userSharesAta = await (0, lib_1.getAssociatedTokenAddress)(vaultState.sharesMint, user.address);
886
941
  const [{ ata: userTokenAta, createAtaIx }] = await (0, utils_2.createAtasIdempotent)(user, [
@@ -901,8 +956,10 @@ class KaminoVaultClient {
901
956
  const reserveWithSharesAmountToWithdraw = [];
902
957
  let isFirstWithdraw = true;
903
958
  if (tokenLeftToWithdraw.lte(0)) {
904
- // Availabe enough to withdraw all - using the first existent reserve
905
959
  const firstReserve = vaultState.vaultAllocationStrategy.find((reserve) => reserve.reserve !== lib_1.DEFAULT_PUBLIC_KEY);
960
+ if (!firstReserve) {
961
+ throw new Error('No reserve available to satisfy withdraw request');
962
+ }
906
963
  if (withdrawAllShares) {
907
964
  reserveWithSharesAmountToWithdraw.push({
908
965
  reserve: firstReserve.reserve,
@@ -917,13 +974,11 @@ class KaminoVaultClient {
917
974
  }
918
975
  }
919
976
  else {
920
- // Get decreasing order sorted available liquidity to withdraw from each reserve allocated to
921
977
  const reserveAllocationAvailableLiquidityToWithdraw = await this.getReserveAllocationAvailableLiquidityToWithdraw(vault, slot, vaultReservesState);
922
- // sort
923
978
  const reserveAllocationAvailableLiquidityToWithdrawSorted = [
924
979
  ...reserveAllocationAvailableLiquidityToWithdraw.entries(),
925
980
  ].sort((a, b) => b[1].sub(a[1]).toNumber());
926
- reserveAllocationAvailableLiquidityToWithdrawSorted.forEach(([key, availableLiquidityToWithdraw], _) => {
981
+ reserveAllocationAvailableLiquidityToWithdrawSorted.forEach(([key, availableLiquidityToWithdraw]) => {
927
982
  if (tokenLeftToWithdraw.gt(0)) {
928
983
  let tokensToWithdrawFromReserve = decimal_js_1.default.min(tokenLeftToWithdraw, availableLiquidityToWithdraw);
929
984
  if (isFirstWithdraw) {
@@ -934,7 +989,6 @@ class KaminoVaultClient {
934
989
  reserveWithSharesAmountToWithdraw.push({ reserve: key, shares: new decimal_js_1.default(utils_2.U64_MAX.toString()) });
935
990
  }
936
991
  else {
937
- // round up to the nearest integer the shares to withdraw
938
992
  const sharesToWithdrawFromReserve = tokensToWithdrawFromReserve.mul(sharesPerToken).floor();
939
993
  reserveWithSharesAmountToWithdraw.push({ reserve: key, shares: sharesToWithdrawFromReserve });
940
994
  }
@@ -944,15 +998,24 @@ class KaminoVaultClient {
944
998
  }
945
999
  const withdrawIxs = [];
946
1000
  withdrawIxs.push(createAtaIx);
947
- for (let reserveIndex = 0; reserveIndex < reserveWithSharesAmountToWithdraw.length; reserveIndex++) {
948
- const reserveWithTokens = reserveWithSharesAmountToWithdraw[reserveIndex];
1001
+ for (const reserveWithTokens of reserveWithSharesAmountToWithdraw) {
949
1002
  const reserveState = vaultReservesState.get(reserveWithTokens.reserve);
950
1003
  if (reserveState === undefined) {
951
1004
  throw new Error(`Reserve ${reserveWithTokens.reserve} not found in vault reserves map`);
952
1005
  }
953
1006
  const marketAddress = reserveState.state.lendingMarket;
954
- const sellIx = await this.sellIx(user, vault, vaultState, marketAddress, { address: reserveWithTokens.reserve, state: reserveState.state }, userSharesAta, userTokenAta, reserveWithTokens.shares, vaultReservesState);
955
- withdrawIxs.push(sellIx);
1007
+ const exitIx = await builder({
1008
+ user,
1009
+ vault,
1010
+ vaultState,
1011
+ marketAddress,
1012
+ reserve: { address: reserveWithTokens.reserve, state: reserveState.state },
1013
+ userSharesAta,
1014
+ userTokenAta,
1015
+ shareAmountLamports: reserveWithTokens.shares,
1016
+ vaultReservesState,
1017
+ });
1018
+ withdrawIxs.push(exitIx);
956
1019
  }
957
1020
  return withdrawIxs;
958
1021
  }
@@ -1164,6 +1227,49 @@ class KaminoVaultClient {
1164
1227
  sellIxn = this.appendRemainingAccountsForVaultReserves(sellIxn, vaultReserves, vaultReservesState);
1165
1228
  return sellIxn;
1166
1229
  }
1230
+ async withdrawIx(user, vault, vaultState, marketAddress, reserve, userSharesAta, userTokenAta, shareAmountLamports, vaultReservesState) {
1231
+ const [lendingMarketAuth] = await (0, utils_2.lendingMarketAuthPda)(marketAddress, this._kaminoLendProgramId);
1232
+ const globalConfig = await getKvaultGlobalConfigPda(this._kaminoVaultProgramId);
1233
+ const eventAuthority = await getEventAuthorityPda(this._kaminoVaultProgramId);
1234
+ const withdrawAccounts = {
1235
+ withdrawFromAvailable: {
1236
+ user,
1237
+ vaultState: vault.address,
1238
+ globalConfig: globalConfig,
1239
+ tokenVault: vaultState.tokenVault,
1240
+ baseVaultAuthority: vaultState.baseVaultAuthority,
1241
+ userTokenAta: userTokenAta,
1242
+ tokenMint: vaultState.tokenMint,
1243
+ userSharesAta: userSharesAta,
1244
+ sharesMint: vaultState.sharesMint,
1245
+ tokenProgram: vaultState.tokenProgram,
1246
+ sharesTokenProgram: token_1.TOKEN_PROGRAM_ADDRESS,
1247
+ klendProgram: this._kaminoLendProgramId,
1248
+ eventAuthority: eventAuthority,
1249
+ program: this._kaminoVaultProgramId,
1250
+ },
1251
+ withdrawFromReserveAccounts: {
1252
+ vaultState: vault.address,
1253
+ reserve: reserve.address,
1254
+ ctokenVault: await getCTokenVaultPda(vault.address, reserve.address, this._kaminoVaultProgramId),
1255
+ lendingMarket: marketAddress,
1256
+ lendingMarketAuthority: lendingMarketAuth,
1257
+ reserveLiquiditySupply: reserve.state.liquidity.supplyVault,
1258
+ reserveCollateralMint: reserve.state.collateral.mintPubkey,
1259
+ reserveCollateralTokenProgram: token_1.TOKEN_PROGRAM_ADDRESS,
1260
+ instructionSysvarAccount: sysvars_1.SYSVAR_INSTRUCTIONS_ADDRESS,
1261
+ },
1262
+ eventAuthority: eventAuthority,
1263
+ program: this._kaminoVaultProgramId,
1264
+ };
1265
+ const withdrawArgs = {
1266
+ sharesAmount: new bn_js_1.default(shareAmountLamports.floor().toString()),
1267
+ };
1268
+ let withdrawIxn = (0, instructions_1.withdraw)(withdrawArgs, withdrawAccounts, undefined, this._kaminoVaultProgramId);
1269
+ const vaultReserves = this.getVaultReserves(vaultState);
1270
+ withdrawIxn = this.appendRemainingAccountsForVaultReserves(withdrawIxn, vaultReserves, vaultReservesState);
1271
+ return withdrawIxn;
1272
+ }
1167
1273
  async withdrawFromAvailableIx(user, vault, vaultState, userSharesAta, userTokenAta, shareAmountLamports) {
1168
1274
  const globalConfig = await getKvaultGlobalConfigPda(this._kaminoVaultProgramId);
1169
1275
  const eventAuthority = await getEventAuthorityPda(this._kaminoVaultProgramId);