@kamino-finance/klend-sdk 7.1.10 → 7.2.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.
@@ -3,10 +3,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.ReserveAllocationConfig = exports.KaminoVaultConfig = exports.KaminoVault = exports.KaminoVaultClient = exports.INITIAL_DEPOSIT_LAMPORTS = exports.METADATA_PROGRAM_ID = exports.METADATA_SEED = exports.kaminoVaultStagingId = exports.kaminoVaultId = void 0;
6
+ exports.VaultHoldings = exports.ReserveAllocationConfig = exports.KaminoVaultConfig = exports.KaminoVault = exports.KaminoVaultClient = exports.INITIAL_DEPOSIT_LAMPORTS = exports.METADATA_PROGRAM_ID = exports.METADATA_SEED = exports.kaminoVaultStagingId = exports.kaminoVaultId = void 0;
7
7
  exports.getCTokenVaultPda = getCTokenVaultPda;
8
8
  exports.getEventAuthorityPda = getEventAuthorityPda;
9
- exports.printHoldings = printHoldings;
10
9
  const bn_js_1 = __importDefault(require("bn.js"));
11
10
  const kit_1 = require("@solana/kit");
12
11
  const lib_1 = require("../lib");
@@ -67,6 +66,9 @@ class KaminoVaultClient {
67
66
  getProgramID() {
68
67
  return this._kaminoVaultProgramId;
69
68
  }
69
+ getRpc() {
70
+ return this._rpc;
71
+ }
70
72
  hasFarm() {
71
73
  return;
72
74
  }
@@ -82,7 +84,7 @@ class KaminoVaultClient {
82
84
  console.log(`Vault ${vaultPubkey.toString()} not found`);
83
85
  return;
84
86
  }
85
- const kaminoVault = new KaminoVault(vaultPubkey, vault, this._kaminoVaultProgramId);
87
+ const kaminoVault = KaminoVault.loadWithClientAndState(this, vaultPubkey, vault);
86
88
  const vaultName = this.decodeVaultName(vault.name);
87
89
  const slot = await this.getConnection().getSlot({ commitment: 'confirmed' }).send();
88
90
  const tokensPerShare = await this.getTokensPerShareSingleVault(kaminoVault, slot);
@@ -90,7 +92,7 @@ class KaminoVaultClient {
90
92
  const sharesIssued = new decimal_js_1.default(vault.sharesIssued.toString()).div(new decimal_js_1.default(vault.sharesMintDecimals.toString()));
91
93
  console.log('Name: ', vaultName);
92
94
  console.log('Shares issued: ', sharesIssued);
93
- printHoldings(holdings);
95
+ holdings.print();
94
96
  console.log('Tokens per share: ', tokensPerShare);
95
97
  }
96
98
  /**
@@ -224,7 +226,7 @@ class KaminoVaultClient {
224
226
  * @returns - a struct with an instruction to update the reserve allocation and an optional list of instructions to update the lookup table for the allocation changes
225
227
  */
226
228
  async updateReserveAllocationIxs(vault, reserveAllocationConfig, vaultAdminAuthority) {
227
- const vaultState = await vault.getState(this.getConnection());
229
+ const vaultState = await vault.getState();
228
230
  const reserveState = reserveAllocationConfig.getReserveState();
229
231
  const cTokenVault = await getCTokenVaultPda(vault.address, reserveAllocationConfig.getReserveAddress(), this._kaminoVaultProgramId);
230
232
  const vaultAdmin = parseVaultAdmin(vaultState, vaultAdminAuthority);
@@ -267,7 +269,7 @@ class KaminoVaultClient {
267
269
  * @returns - a list of instructions to update the unallocated weight and cap
268
270
  */
269
271
  async updateVaultUnallocatedWeightAndCapIxs(vault, vaultAdminAuthority, unallocatedWeight, unallocatedCap) {
270
- const vaultState = await vault.getState(this.getConnection());
272
+ const vaultState = await vault.getState();
271
273
  const unallocatedWeightToUse = unallocatedWeight ? unallocatedWeight : vaultState.unallocatedWeight;
272
274
  const unallocatedCapToUse = unallocatedCap ? unallocatedCap : vaultState.unallocatedTokensCap;
273
275
  const ixs = [];
@@ -289,7 +291,7 @@ class KaminoVaultClient {
289
291
  * @returns - a struct with an instruction to update the reserve allocation and an optional list of instructions to update the lookup table for the allocation changes
290
292
  */
291
293
  async withdrawEverythingAndBlockInvestReserve(vault, reserve, vaultAdminAuthority) {
292
- const vaultState = await vault.getState(this.getConnection());
294
+ const vaultState = await vault.getState();
293
295
  const reserveIsPartOfAllocation = vaultState.vaultAllocationStrategy.some((allocation) => allocation.reserve === reserve);
294
296
  const withdrawAndBlockReserveIxs = {
295
297
  updateReserveAllocationIxs: [],
@@ -323,7 +325,7 @@ class KaminoVaultClient {
323
325
  * @returns - a struct with an instruction to update the reserve allocations (set weight and ctoken allocation to 0) and an a list of instructions to disinvest the funds in the reserves
324
326
  */
325
327
  async withdrawEverythingFromAllReservesAndBlockInvest(vault, vaultReservesMap, payer) {
326
- const vaultState = await vault.getState(this.getConnection());
328
+ const vaultState = await vault.getState();
327
329
  const reserves = this.getVaultReserves(vaultState);
328
330
  const withdrawAndBlockReserveIxs = {
329
331
  updateReserveAllocationIxs: [],
@@ -355,7 +357,7 @@ class KaminoVaultClient {
355
357
  * @returns - a struct with an instruction to update the reserve allocations to 0 weight and a list of instructions to disinvest the funds in the reserves
356
358
  */
357
359
  async disinvestAllReservesIxs(vault, vaultReservesMap, payer) {
358
- const vaultState = await vault.getState(this.getConnection());
360
+ const vaultState = await vault.getState();
359
361
  const reserves = this.getVaultReserves(vaultState);
360
362
  const disinvestAllReservesIxs = {
361
363
  updateReserveAllocationIxs: [],
@@ -391,7 +393,7 @@ class KaminoVaultClient {
391
393
  * @returns - an instruction to remove the reserve from the vault allocation strategy or undefined if the reserve is not part of the allocation strategy
392
394
  */
393
395
  async removeReserveFromAllocationIx(vault, reserve, vaultAdminAuthority) {
394
- const vaultState = await vault.getState(this.getConnection());
396
+ const vaultState = await vault.getState();
395
397
  const vaultAdmin = parseVaultAdmin(vaultState, vaultAdminAuthority);
396
398
  const reserveIsPartOfAllocation = vaultState.vaultAllocationStrategy.some((allocation) => allocation.reserve === reserve);
397
399
  if (!reserveIsPartOfAllocation) {
@@ -413,7 +415,7 @@ class KaminoVaultClient {
413
415
  * @returns a struct that contains the instruction to update the field and an optional list of instructions to update the lookup table
414
416
  */
415
417
  async updateVaultConfigIxs(vault, mode, value, vaultAdminAuthority) {
416
- const vaultState = await vault.getState(this.getConnection());
418
+ const vaultState = await vault.getState();
417
419
  const admin = parseVaultAdmin(vaultState, vaultAdminAuthority);
418
420
  const updateVaultConfigAccs = {
419
421
  vaultAdminAuthority: admin,
@@ -479,7 +481,7 @@ class KaminoVaultClient {
479
481
  *
480
482
  */
481
483
  async setVaultFarmIxs(vault, farm, errorOnOverride = true, vaultAdminAuthority) {
482
- const vaultHasFarm = await vault.hasFarm(this.getConnection());
484
+ const vaultHasFarm = await vault.hasFarm();
483
485
  if (vaultHasFarm && errorOnOverride) {
484
486
  throw new Error('Vault already has a farm, if you want to override it set errorOnOverride to false');
485
487
  }
@@ -528,7 +530,7 @@ class KaminoVaultClient {
528
530
  * @returns - an instruction to accept the ownership of the vault and a list of instructions to update the lookup table
529
531
  */
530
532
  async acceptVaultOwnershipIxs(vault, pendingAdmin) {
531
- const vaultState = await vault.getState(this.getConnection());
533
+ const vaultState = await vault.getState();
532
534
  const signer = parseVaultPendingAdmin(vaultState, pendingAdmin);
533
535
  const acceptOwneshipAccounts = {
534
536
  pendingAdmin: signer,
@@ -559,7 +561,7 @@ class KaminoVaultClient {
559
561
  * @returns - an instruction to give up the specified pending fees
560
562
  */
561
563
  async giveUpPendingFeesIx(vault, maxAmountToGiveUp, vaultAdminAuthority) {
562
- const vaultState = await vault.getState(this.getConnection());
564
+ const vaultState = await vault.getState();
563
565
  const vaultAdmin = parseVaultAdmin(vaultState, vaultAdminAuthority);
564
566
  const giveUpPendingFeesAccounts = {
565
567
  vaultAdminAuthority: vaultAdmin,
@@ -582,7 +584,7 @@ class KaminoVaultClient {
582
584
  * @returns - list of instructions to withdraw all pending fees, including the ATA creation instructions if needed
583
585
  */
584
586
  async withdrawPendingFeesIxs(vault, slot, vaultReservesMap, vaultAdminAuthority) {
585
- const vaultState = await vault.getState(this.getConnection());
587
+ const vaultState = await vault.getState();
586
588
  const vaultAdmin = parseVaultAdmin(vaultState, vaultAdminAuthority);
587
589
  const vaultReservesState = vaultReservesMap ? vaultReservesMap : await this.loadVaultReserves(vaultState);
588
590
  const [{ ata: adminTokenAta, createAtaIx }] = await (0, utils_2.createAtasIdempotent)(vaultAdmin, [
@@ -640,7 +642,7 @@ class KaminoVaultClient {
640
642
  * @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
641
643
  */
642
644
  async depositIxs(user, vault, tokenAmount, vaultReservesMap, farmState) {
643
- const vaultState = await vault.getState(this.getConnection());
645
+ const vaultState = await vault.getState();
644
646
  const tokenProgramID = vaultState.tokenProgram;
645
647
  const userTokenAta = await (0, lib_1.getAssociatedTokenAddress)(vaultState.tokenMint, user.address, tokenProgramID);
646
648
  const createAtasIxs = [];
@@ -691,7 +693,7 @@ class KaminoVaultClient {
691
693
  stakeInFarmIfNeededIxs: [],
692
694
  };
693
695
  // if there is no farm, we can return the deposit instructions, otherwise include the stake ix in the response
694
- if (!(await vault.hasFarm(this.getConnection()))) {
696
+ if (!(await vault.hasFarm())) {
695
697
  return depositIxs;
696
698
  }
697
699
  // if there is a farm, stake the shares
@@ -708,13 +710,13 @@ class KaminoVaultClient {
708
710
  * @returns - a list of instructions for the user to stake shares into the vault's farm, including the creation of prerequisite accounts if needed
709
711
  */
710
712
  async stakeSharesIxs(user, vault, sharesAmount, farmState) {
711
- const vaultState = await vault.getState(this.getConnection());
713
+ const vaultState = await vault.getState();
712
714
  let sharesToStakeLamports = new decimal_js_1.default(utils_2.U64_MAX);
713
715
  if (sharesAmount) {
714
716
  sharesToStakeLamports = (0, utils_1.numberToLamportsDecimal)(sharesAmount, vaultState.sharesMintDecimals.toNumber());
715
717
  }
716
718
  // if tokens to be staked are 0 or vault has no farm there is no stake needed
717
- if (sharesToStakeLamports.lte(0) || !(await vault.hasFarm(this.getConnection()))) {
719
+ if (sharesToStakeLamports.lte(0) || !(await vault.hasFarm())) {
718
720
  return [];
719
721
  }
720
722
  // returns the ix to create the farm state account if needed and the ix to stake the shares
@@ -731,9 +733,8 @@ class KaminoVaultClient {
731
733
  * @returns an array of instructions to create missing ATAs if needed and the withdraw instructions
732
734
  */
733
735
  async withdrawIxs(user, vault, shareAmountToWithdraw, slot, vaultReservesMap, farmState) {
734
- const vaultState = await vault.getState(this.getConnection());
735
- const kaminoVault = new KaminoVault(vault.address, vaultState, vault.programId);
736
- const hasFarm = await vault.hasFarm(this.getConnection());
736
+ const vaultState = await vault.getState();
737
+ const hasFarm = await vault.hasFarm();
737
738
  const withdrawIxs = {
738
739
  unstakeFromFarmIfNeededIxs: [],
739
740
  withdrawIxs: [],
@@ -760,7 +761,7 @@ class KaminoVaultClient {
760
761
  }
761
762
  // if not enough shares in ATA unstake from farm
762
763
  const sharesInAtaAreEnoughForWithdraw = sharesToWithdraw.lte(userSharesAtaBalance);
763
- if (hasFarm && !sharesInAtaAreEnoughForWithdraw) {
764
+ if (hasFarm && !sharesInAtaAreEnoughForWithdraw && userSharesInFarm.gt(0)) {
764
765
  // if we need to unstake we need to make sure share ata is created
765
766
  const [{ createAtaIx }] = await (0, utils_2.createAtasIdempotent)(user, [
766
767
  {
@@ -781,11 +782,11 @@ class KaminoVaultClient {
781
782
  // if the vault has allocations withdraw otherwise wtihdraw from available ix
782
783
  const vaultAllocation = vaultState.vaultAllocationStrategy.find((allocation) => allocation.reserve !== lib_1.DEFAULT_PUBLIC_KEY);
783
784
  if (vaultAllocation) {
784
- const withdrawFromVaultIxs = await this.withdrawWithReserveIxs(user, kaminoVault, sharesToWithdraw, totalUserShares, slot, vaultReservesMap);
785
+ const withdrawFromVaultIxs = await this.withdrawWithReserveIxs(user, vault, sharesToWithdraw, totalUserShares, slot, vaultReservesMap);
785
786
  withdrawIxs.withdrawIxs = withdrawFromVaultIxs;
786
787
  }
787
788
  else {
788
- const withdrawFromVaultIxs = await this.withdrawFromAvailableIxs(user, kaminoVault, sharesToWithdraw);
789
+ const withdrawFromVaultIxs = await this.withdrawFromAvailableIxs(user, vault, sharesToWithdraw);
789
790
  withdrawIxs.withdrawIxs = withdrawFromVaultIxs;
790
791
  }
791
792
  // if the vault is for SOL return the ix to unwrap the SOL
@@ -811,8 +812,7 @@ class KaminoVaultClient {
811
812
  return withdrawIxs;
812
813
  }
813
814
  async withdrawFromAvailableIxs(user, vault, shareAmount) {
814
- const vaultState = await vault.getState(this.getConnection());
815
- const kaminoVault = new KaminoVault(vault.address, vaultState, vault.programId);
815
+ const vaultState = await vault.getState();
816
816
  const userSharesAta = await (0, lib_1.getAssociatedTokenAddress)(vaultState.sharesMint, user.address);
817
817
  const [{ ata: userTokenAta, createAtaIx }] = await (0, utils_2.createAtasIdempotent)(user, [
818
818
  {
@@ -821,11 +821,11 @@ class KaminoVaultClient {
821
821
  },
822
822
  ]);
823
823
  const shareLamportsToWithdraw = (0, kliquidity_sdk_1.collToLamportsDecimal)(shareAmount, vaultState.sharesMintDecimals.toNumber());
824
- const withdrawFromAvailableIxn = await this.withdrawFromAvailableIx(user, kaminoVault, vaultState, userSharesAta, userTokenAta, shareLamportsToWithdraw);
824
+ const withdrawFromAvailableIxn = await this.withdrawFromAvailableIx(user, vault, vaultState, userSharesAta, userTokenAta, shareLamportsToWithdraw);
825
825
  return [createAtaIx, withdrawFromAvailableIxn];
826
826
  }
827
827
  async withdrawWithReserveIxs(user, vault, shareAmount, allUserShares, slot, vaultReservesMap) {
828
- const vaultState = await vault.getState(this.getConnection());
828
+ const vaultState = await vault.getState();
829
829
  const vaultReservesState = vaultReservesMap ? vaultReservesMap : await this.loadVaultReserves(vaultState);
830
830
  const userSharesAta = await (0, lib_1.getAssociatedTokenAddress)(vaultState.sharesMint, user.address);
831
831
  const [{ ata: userTokenAta, createAtaIx }] = await (0, utils_2.createAtasIdempotent)(user, [
@@ -880,7 +880,7 @@ class KaminoVaultClient {
880
880
  }
881
881
  else {
882
882
  // round up to the nearest integer the shares to withdraw
883
- const sharesToWithdrawFromReserve = tokensToWithdrawFromReserve.mul(sharesPerToken).ceil();
883
+ const sharesToWithdrawFromReserve = tokensToWithdrawFromReserve.mul(sharesPerToken).floor();
884
884
  reserveWithSharesAmountToWithdraw.push({ reserve: key, shares: sharesToWithdrawFromReserve });
885
885
  }
886
886
  tokenLeftToWithdraw = tokenLeftToWithdraw.sub(tokensToWithdrawFromReserve);
@@ -896,13 +896,7 @@ class KaminoVaultClient {
896
896
  throw new Error(`Reserve ${reserveWithTokens.reserve} not found in vault reserves map`);
897
897
  }
898
898
  const marketAddress = reserveState.state.lendingMarket;
899
- const isLastWithdraw = reserveIndex === reserveWithSharesAmountToWithdraw.length - 1;
900
- // if it is not last withdraw it means that we can pass all shares as we are withdrawing everything from that reserve
901
- let sharesToWithdraw = shareAmount;
902
- if (isLastWithdraw) {
903
- sharesToWithdraw = reserveWithTokens.shares;
904
- }
905
- const withdrawFromReserveIx = await this.withdrawIx(user, vault, vaultState, marketAddress, { address: reserveWithTokens.reserve, state: reserveState.state }, userSharesAta, userTokenAta, sharesToWithdraw, vaultReservesState);
899
+ const withdrawFromReserveIx = await this.withdrawIx(user, vault, vaultState, marketAddress, { address: reserveWithTokens.reserve, state: reserveState.state }, userSharesAta, userTokenAta, reserveWithTokens.shares, vaultReservesState);
906
900
  withdrawIxs.push(withdrawFromReserveIx);
907
901
  }
908
902
  return withdrawIxs;
@@ -915,7 +909,7 @@ class KaminoVaultClient {
915
909
  * @returns - an array of invest instructions for each invest action required for the vault reserves
916
910
  */
917
911
  async investAllReservesIxs(payer, vault, skipComputationChecks = false) {
918
- const vaultState = await vault.reloadState(this.getConnection());
912
+ const vaultState = await vault.reloadState();
919
913
  const minInvestAmount = vaultState.minInvestAmount;
920
914
  const allReserves = this.getVaultReserves(vaultState);
921
915
  if (allReserves.length === 0) {
@@ -999,7 +993,7 @@ class KaminoVaultClient {
999
993
  * @returns - an array of invest instructions for each invest action required for the vault reserves
1000
994
  */
1001
995
  async investSingleReserveIxs(payer, vault, reserve, vaultReservesMap, createAtaIfNeeded = true) {
1002
- const vaultState = await vault.getState(this.getConnection());
996
+ const vaultState = await vault.getState();
1003
997
  const cTokenVault = await getCTokenVaultPda(vault.address, reserve.address, this._kaminoVaultProgramId);
1004
998
  const [lendingMarketAuth] = await (0, utils_2.lendingMarketAuthPda)(reserve.state.lendingMarket, this._kaminoLendProgramId);
1005
999
  const ixs = [];
@@ -1144,7 +1138,7 @@ class KaminoVaultClient {
1144
1138
  * @returns a struct that contains a list of ix to create the LUT and assign it to the vault if needed + a list of ixs to insert all the accounts in the LUT
1145
1139
  */
1146
1140
  async syncVaultLookupTableIxs(authority, vault, vaultReservesMap) {
1147
- const vaultState = await vault.getState(this.getConnection());
1141
+ const vaultState = await vault.getState();
1148
1142
  const allAccountsToBeInserted = [
1149
1143
  vault.address,
1150
1144
  vaultState.vaultAdminAuthority,
@@ -1260,7 +1254,7 @@ class KaminoVaultClient {
1260
1254
  * @returns - user share balance in tokens (not lamports)
1261
1255
  */
1262
1256
  async getUserSharesBalanceSingleVault(user, vault) {
1263
- const vaultState = await vault.getState(this.getConnection());
1257
+ const vaultState = await vault.getState();
1264
1258
  const userShares = {
1265
1259
  unstakedShares: new decimal_js_1.default(0),
1266
1260
  stakedShares: new decimal_js_1.default(0),
@@ -1280,7 +1274,7 @@ class KaminoVaultClient {
1280
1274
  }
1281
1275
  return acc;
1282
1276
  }, new decimal_js_1.default(0));
1283
- if (await vault.hasFarm(this.getConnection())) {
1277
+ if (await vault.hasFarm()) {
1284
1278
  const userSharesInFarm = await (0, farm_utils_1.getUserSharesInTokensStakedInFarm)(this.getConnection(), user, vaultState.vaultFarm, vaultState.sharesMintDecimals.toNumber());
1285
1279
  userShares.stakedShares = userSharesInFarm;
1286
1280
  }
@@ -1317,7 +1311,7 @@ class KaminoVaultClient {
1317
1311
  return mint === state.sharesMint;
1318
1312
  });
1319
1313
  userSharesTokenAccountsPerVault.set(vault.address, userSharesTokenAccounts);
1320
- if (await vault.hasFarm(this.getConnection())) {
1314
+ if (await vault.hasFarm()) {
1321
1315
  const userFarmState = allUserFarmStatesMap.get(state.vaultFarm);
1322
1316
  if (userFarmState) {
1323
1317
  console.log('there is a farm state for vault', vault.address);
@@ -1386,7 +1380,7 @@ class KaminoVaultClient {
1386
1380
  */
1387
1381
  async getTokensPerShareSingleVault(vaultOrState, slot, vaultReservesMap, currentSlot) {
1388
1382
  // Determine if we have a KaminoVault or VaultState
1389
- const vaultState = 'getState' in vaultOrState ? await vaultOrState.getState(this.getConnection()) : vaultOrState;
1383
+ const vaultState = 'getState' in vaultOrState ? await vaultOrState.getState() : vaultOrState;
1390
1384
  if (vaultState.sharesIssued.isZero()) {
1391
1385
  return new decimal_js_1.default(0);
1392
1386
  }
@@ -1465,7 +1459,7 @@ class KaminoVaultClient {
1465
1459
  if (!kaminoVaultAccount) {
1466
1460
  throw Error(`kaminoVault with pubkey ${kaminoVault.address} could not be decoded`);
1467
1461
  }
1468
- return new KaminoVault(kaminoVault.address, kaminoVaultAccount, this._kaminoVaultProgramId);
1462
+ return KaminoVault.loadWithClientAndState(this, kaminoVault.address, kaminoVaultAccount);
1469
1463
  });
1470
1464
  }
1471
1465
  /**
@@ -1480,7 +1474,7 @@ class KaminoVaultClient {
1480
1474
  const vaultStates = await (0, kliquidity_sdk_1.batchFetch)(vaults, (chunk) => this.getVaultsStates(chunk));
1481
1475
  return vaults.map((vault, index) => {
1482
1476
  const state = vaultStates[index];
1483
- return state ? new KaminoVault(vault, state, this._kaminoVaultProgramId) : null;
1477
+ return state ? KaminoVault.loadWithClientAndState(this, vault, state) : null;
1484
1478
  });
1485
1479
  }
1486
1480
  async getVaultsStates(vaults) {
@@ -1557,7 +1551,7 @@ class KaminoVaultClient {
1557
1551
  * @returns an HashMap of reserves (key) with the amount available to withdraw for each (value)
1558
1552
  */
1559
1553
  async getReserveAllocationAvailableLiquidityToWithdraw(vault, slot, vaultReservesMap) {
1560
- const vaultState = await vault.getState(this.getConnection());
1554
+ const vaultState = await vault.getState();
1561
1555
  const reserveAllocationAvailableLiquidityToWithdraw = new Map();
1562
1556
  vaultState.vaultAllocationStrategy.forEach((allocationStrategy) => {
1563
1557
  if (allocationStrategy.reserve === lib_1.DEFAULT_PUBLIC_KEY) {
@@ -1739,13 +1733,13 @@ class KaminoVaultClient {
1739
1733
  * @returns an VaultHoldings object representing the amount available (uninvested) in vault, total amount invested in reseves and a breakdown of the amount invested in each reserve
1740
1734
  */
1741
1735
  async getVaultHoldings(vault, slot, vaultReserves, currentSlot) {
1742
- const vaultHoldings = {
1736
+ const vaultHoldings = new VaultHoldings({
1743
1737
  available: new decimal_js_1.default(vault.tokenAvailable.toString()),
1744
1738
  invested: new decimal_js_1.default(0),
1745
1739
  investedInReserves: new Map(),
1746
1740
  totalAUMIncludingFees: new decimal_js_1.default(0),
1747
1741
  pendingFees: new decimal_js_1.default(0),
1748
- };
1742
+ });
1749
1743
  const currentSlotToUse = currentSlot ?? (await this.getConnection().getSlot({ commitment: 'confirmed' }).send());
1750
1744
  const vaultReservesState = vaultReserves ? vaultReserves : await this.loadVaultReserves(vault);
1751
1745
  const decimals = new decimal_js_1.default(vault.tokenMintDecimals.toString());
@@ -1794,13 +1788,13 @@ class KaminoVaultClient {
1794
1788
  const totalAvailableDecimal = (0, lib_1.lamportsToDecimal)(vaultHoldings.available, decimals);
1795
1789
  const totalInvestedDecimal = (0, lib_1.lamportsToDecimal)(vaultHoldings.invested, decimals);
1796
1790
  const pendingFees = (0, lib_1.lamportsToDecimal)(totalPendingFees, decimals);
1797
- return {
1791
+ return new VaultHoldings({
1798
1792
  available: totalAvailableDecimal,
1799
1793
  invested: totalInvestedDecimal,
1800
1794
  investedInReserves: vaultHoldings.investedInReserves,
1801
1795
  totalAUMIncludingFees: totalAvailableDecimal.add(totalInvestedDecimal),
1802
1796
  pendingFees: pendingFees,
1803
- };
1797
+ });
1804
1798
  }
1805
1799
  /**
1806
1800
  * This will return an VaultOverview object that encapsulates all the information about the vault, including the holdings, reserves details, theoretical APY, utilization ratio and total borrowed amount
@@ -1838,19 +1832,21 @@ class KaminoVaultClient {
1838
1832
  * @returns an VaultOverview object with details about the tokens available and invested in the vault, denominated in tokens and USD, along sie APYs
1839
1833
  */
1840
1834
  async getVaultOverview(vault, vaultTokenPrice, slot, vaultReservesMap, kaminoMarkets, currentSlot, tokensPrices) {
1841
- const vaultReservesState = vaultReservesMap ? vaultReservesMap : await this.loadVaultReserves(vault);
1842
- const vaultHoldingsWithUSDValuePromise = this.getVaultHoldingsWithPrice(vault, vaultTokenPrice, slot, vaultReservesState, currentSlot);
1835
+ const vaultState = await vault.getState();
1836
+ const vaultReservesState = vaultReservesMap ? vaultReservesMap : await this.loadVaultReserves(vaultState);
1837
+ const vaultHoldingsWithUSDValuePromise = this.getVaultHoldingsWithPrice(vaultState, vaultTokenPrice, slot, vaultReservesState, currentSlot);
1843
1838
  const slotForOverview = slot ? slot : await this.getConnection().getSlot().send();
1844
1839
  const farmsClient = new farms_sdk_1.Farms(this.getConnection());
1845
- const vaultTheoreticalAPYPromise = this.getVaultTheoreticalAPY(vault, slotForOverview, vaultReservesState);
1846
- const vaultActualAPYPromise = this.getVaultActualAPY(vault, slotForOverview, vaultReservesState);
1847
- const totalInvestedAndBorrowedPromise = this.getTotalBorrowedAndInvested(vault, slotForOverview, vaultReservesState);
1848
- const vaultCollateralsPromise = this.getVaultCollaterals(vault, slotForOverview, vaultReservesState, kaminoMarkets);
1849
- const reservesOverviewPromise = this.getVaultReservesDetails(vault, slotForOverview, vaultReservesState);
1840
+ const vaultTheoreticalAPYPromise = this.getVaultTheoreticalAPY(vaultState, slotForOverview, vaultReservesState);
1841
+ const vaultActualAPYPromise = this.getVaultActualAPY(vaultState, slotForOverview, vaultReservesState);
1842
+ const totalInvestedAndBorrowedPromise = this.getTotalBorrowedAndInvested(vaultState, slotForOverview, vaultReservesState);
1843
+ const vaultCollateralsPromise = this.getVaultCollaterals(vaultState, slotForOverview, vaultReservesState, kaminoMarkets);
1844
+ const reservesOverviewPromise = this.getVaultReservesDetails(vaultState, slotForOverview, vaultReservesState);
1850
1845
  const vaultFarmIncentivesPromise = this.getVaultRewardsAPY(vault, vaultTokenPrice, farmsClient, slotForOverview, tokensPrices);
1851
1846
  const vaultReservesFarmIncentivesPromise = this.getVaultReservesFarmsIncentives(vault, vaultTokenPrice, farmsClient, slotForOverview, vaultReservesState, tokensPrices);
1847
+ const vaultDelegatedFarmIncentivesPromise = this.getVaultDelegatedFarmRewardsAPY(vault, vaultTokenPrice, farmsClient, slotForOverview, tokensPrices);
1852
1848
  // all the async part of the functions above just read the vaultReservesState which is read beforehand, so excepting vaultCollateralsPromise they should do no additional network calls
1853
- const [vaultHoldingsWithUSDValue, vaultTheoreticalAPYs, vaultActualAPYs, totalInvestedAndBorrowed, vaultCollaterals, reservesOverview, vaultFarmIncentives, vaultReservesFarmIncentives,] = await Promise.all([
1849
+ const [vaultHoldingsWithUSDValue, vaultTheoreticalAPYs, vaultActualAPYs, totalInvestedAndBorrowed, vaultCollaterals, reservesOverview, vaultFarmIncentives, vaultReservesFarmIncentives, vaultDelegatedFarmIncentives,] = await Promise.all([
1854
1850
  vaultHoldingsWithUSDValuePromise,
1855
1851
  vaultTheoreticalAPYPromise,
1856
1852
  vaultActualAPYPromise,
@@ -1859,6 +1855,7 @@ class KaminoVaultClient {
1859
1855
  reservesOverviewPromise,
1860
1856
  vaultFarmIncentivesPromise,
1861
1857
  vaultReservesFarmIncentivesPromise,
1858
+ vaultDelegatedFarmIncentivesPromise,
1862
1859
  ]);
1863
1860
  return {
1864
1861
  holdingsUSD: vaultHoldingsWithUSDValue,
@@ -1868,6 +1865,7 @@ class KaminoVaultClient {
1868
1865
  theoreticalSupplyAPY: vaultTheoreticalAPYs,
1869
1866
  vaultFarmIncentives: vaultFarmIncentives,
1870
1867
  reservesFarmsIncentives: vaultReservesFarmIncentives,
1868
+ delegatedFarmIncentives: vaultDelegatedFarmIncentives,
1871
1869
  totalBorrowed: totalInvestedAndBorrowed.totalBorrowed,
1872
1870
  totalBorrowedUSD: totalInvestedAndBorrowed.totalBorrowed.mul(vaultTokenPrice),
1873
1871
  utilizationRatio: totalInvestedAndBorrowed.utilizationRatio,
@@ -2098,10 +2096,40 @@ class KaminoVaultClient {
2098
2096
  */
2099
2097
  computeUserFarmStateDelegateePDAForUserInVault(farmsProgramId, vault, reserve, user) {
2100
2098
  return (0, kit_1.getProgramDerivedAddress)({
2101
- seeds: [addressEncoder.encode(vault), addressEncoder.encode(reserve), addressEncoder.encode(user)],
2099
+ seeds: [addressEncoder.encode(reserve), addressEncoder.encode(vault), addressEncoder.encode(user)],
2102
2100
  programAddress: farmsProgramId,
2103
2101
  });
2104
2102
  }
2103
+ /**
2104
+ * Compute the delegatee PDA for the user farm state for a vault delegate farm
2105
+ * @param farmProgramID - the program ID of the farm program
2106
+ * @param vault - the address of the vault
2107
+ * @param farm - the address of the delegated farm
2108
+ * @param user - the address of the user
2109
+ * @returns the PDA of the delegatee user farm state for the delegated farm
2110
+ */
2111
+ async computeUserFarmStateDelegateePDAForUserInDelegatedVaultFarm(farmProgramID, vault, farm, user) {
2112
+ return (0, kit_1.getProgramDerivedAddress)({
2113
+ seeds: [addressEncoder.encode(vault), addressEncoder.encode(farm), addressEncoder.encode(user)],
2114
+ programAddress: farmProgramID,
2115
+ });
2116
+ }
2117
+ /**
2118
+ * Compute the user state PDA for a user in a delegated vault farm
2119
+ * @param farmProgramID - the program ID of the farm program
2120
+ * @param vault - the address of the vault
2121
+ * @param farm - the address of the delegated farm
2122
+ * @param user - the address of the user
2123
+ * @returns the PDA of the user state for the delegated farm
2124
+ */
2125
+ async computeUserStatePDAForUserInDelegatedVaultFarm(farmProgramID, vault, farm, user) {
2126
+ const delegateePDA = await this.computeDelegateeForUserInDelegatedFarm(farmProgramID, vault, farm, user);
2127
+ return (0, dist_1.getUserStatePDA)(farmProgramID, farm, delegateePDA);
2128
+ }
2129
+ async computeDelegateeForUserInDelegatedFarm(farmProgramID, vault, farm, user) {
2130
+ const delegateePDA = await this.computeUserFarmStateDelegateePDAForUserInDelegatedVaultFarm(farmProgramID, vault, farm, user);
2131
+ return delegateePDA[0];
2132
+ }
2105
2133
  /**
2106
2134
  * Read the APY of the farm built on top of the vault (farm in vaultState.vaultFarm)
2107
2135
  * @param vault - the vault to read the farm APY for
@@ -2112,7 +2140,7 @@ class KaminoVaultClient {
2112
2140
  */
2113
2141
  async getVaultRewardsAPY(vaultOrState, vaultTokenPrice, farmsClient, slot, tokensPrices) {
2114
2142
  // Determine if we have a KaminoVault or VaultState
2115
- const vaultState = 'getState' in vaultOrState ? await vaultOrState.getState(this.getConnection()) : vaultOrState;
2143
+ const vaultState = 'getState' in vaultOrState ? await vaultOrState.getState() : vaultOrState;
2116
2144
  if (vaultState.vaultFarm === lib_1.DEFAULT_PUBLIC_KEY) {
2117
2145
  return {
2118
2146
  incentivesStats: [],
@@ -2125,6 +2153,30 @@ class KaminoVaultClient {
2125
2153
  const kFarmsClient = farmsClient ? farmsClient : new farms_sdk_1.Farms(this.getConnection());
2126
2154
  return (0, apy_1.getFarmIncentives)(kFarmsClient, vaultState.vaultFarm, sharePrice, stakedTokenMintDecimals, tokensPrices);
2127
2155
  }
2156
+ /**
2157
+ * Read the APY of the delegated farm providing incentives for vault depositors
2158
+ * @param vault - the vault to read the farm APY for
2159
+ * @param vaultTokenPrice - the price of the vault token in USD (e.g. 1.0 for USDC)
2160
+ * @param [farmsClient] - the farms client to use. Optional. If not provided, the function will create a new one
2161
+ * @param [slot] - the slot to read the farm APY for. Optional. If not provided, the function will read the current slot
2162
+ * @param [tokensPrices] - the prices of the tokens in USD. Optional. If not provided, the function will fetch the prices
2163
+ * @returns the APY of the delegated farm providing incentives for vault depositors
2164
+ */
2165
+ async getVaultDelegatedFarmRewardsAPY(vault, vaultTokenPrice, farmsClient, slot, tokensPrices) {
2166
+ const delegatedFarm = await this.getDelegatedFarmForVault(vault.address);
2167
+ if (!delegatedFarm) {
2168
+ return {
2169
+ incentivesStats: [],
2170
+ totalIncentivesApy: 0,
2171
+ };
2172
+ }
2173
+ const vaultState = await vault.getState();
2174
+ const tokensPerShare = await this.getTokensPerShareSingleVault(vaultState, slot);
2175
+ const sharePrice = tokensPerShare.mul(vaultTokenPrice);
2176
+ const stakedTokenMintDecimals = vaultState.sharesMintDecimals.toNumber();
2177
+ const kFarmsClient = farmsClient ? farmsClient : new farms_sdk_1.Farms(this.getConnection());
2178
+ return (0, apy_1.getFarmIncentives)(kFarmsClient, delegatedFarm, sharePrice, stakedTokenMintDecimals, tokensPrices);
2179
+ }
2128
2180
  /**
2129
2181
  * Get all the token mints of the vault, vault farm rewards and the allocation rewards
2130
2182
  * @param vaults - the vaults to get the token mints for
@@ -2138,9 +2190,9 @@ class KaminoVaultClient {
2138
2190
  const farmsToFetch = new Set();
2139
2191
  const reservesToFetch = new Set();
2140
2192
  for (const vault of vaults) {
2141
- const vaultState = await vault.getState(this.getConnection());
2193
+ const vaultState = await vault.getState();
2142
2194
  vaultsTokenMints.add(vaultState.tokenMint);
2143
- const hasFarm = await vault.hasFarm(this.getConnection());
2195
+ const hasFarm = await vault.hasFarm();
2144
2196
  if (hasFarm) {
2145
2197
  const farmAddress = vaultState.vaultFarm;
2146
2198
  if (!kFarmsMap.has(farmAddress)) {
@@ -2216,9 +2268,9 @@ class KaminoVaultClient {
2216
2268
  return vaultsTokenMints;
2217
2269
  }
2218
2270
  async getVaultReservesFarmsIncentives(vaultOrState, vaultTokenPrice, farmsClient, slot, vaultReservesMap, tokensPrices) {
2219
- const vaultState = 'getState' in vaultOrState ? await vaultOrState.getState(this.getConnection()) : vaultOrState;
2271
+ const vaultState = 'getState' in vaultOrState ? await vaultOrState.getState() : vaultOrState;
2220
2272
  const vaultReservesState = vaultReservesMap ? vaultReservesMap : await this.loadVaultReserves(vaultState);
2221
- const currentSlot = slot ? slot : await this.getConnection().getSlot({ commitment: 'confirmed' }).send();
2273
+ const currentSlot = slot ?? (await this.getConnection().getSlot({ commitment: 'confirmed' }).send());
2222
2274
  const holdings = await this.getVaultHoldings(vaultState, currentSlot, vaultReservesState);
2223
2275
  const vaultReservesAddresses = vaultState.vaultAllocationStrategy.map((allocationStrategy) => allocationStrategy.reserve);
2224
2276
  const vaultReservesFarmsIncentives = new Map();
@@ -2237,7 +2289,7 @@ class KaminoVaultClient {
2237
2289
  });
2238
2290
  continue;
2239
2291
  }
2240
- const reserveFarmIncentives = await (0, farmUtils_1.getReserveFarmRewardsAPY)(this._rpc, this.recentSlotDurationMs, reserveAddress, vaultTokenPrice, this._kaminoLendProgramId, kFarmsClient, currentSlot, reserveState.state, undefined, tokensPrices);
2292
+ const reserveFarmIncentives = await (0, farmUtils_1.getReserveFarmRewardsAPY)(this._rpc, this.recentSlotDurationMs, reserveAddress, vaultTokenPrice, this._kaminoLendProgramId, kFarmsClient, currentSlot, reserveState.state, tokensPrices);
2241
2293
  vaultReservesFarmsIncentives.set(reserveAddress, reserveFarmIncentives.collateralFarmIncentives);
2242
2294
  const investedInReserve = holdings.investedInReserves.get(reserveAddress);
2243
2295
  const weightedReserveAPY = new decimal_js_1.default(reserveFarmIncentives.collateralFarmIncentives.totalIncentivesApy)
@@ -2250,6 +2302,227 @@ class KaminoVaultClient {
2250
2302
  totalIncentivesAPY: totalIncentivesApy,
2251
2303
  };
2252
2304
  }
2305
+ /// reads the pending rewards for a user in the vault farm
2306
+ /// @param user - the user address
2307
+ /// @param vault - the vault
2308
+ /// @returns a map of the pending rewards token mint and amount in lamports
2309
+ async getUserPendingRewardsInVaultFarm(user, vault) {
2310
+ const vaultState = await vault.getState();
2311
+ const hasFarm = await vault.hasFarm();
2312
+ if (!hasFarm) {
2313
+ return new Map();
2314
+ }
2315
+ const farmClient = new farms_sdk_1.Farms(this.getConnection());
2316
+ const userState = await (0, dist_1.getUserStatePDA)(farmClient.getProgramID(), vaultState.vaultFarm, user);
2317
+ return (0, farm_utils_1.getUserPendingRewardsInFarm)(this.getConnection(), userState, vaultState.vaultFarm);
2318
+ }
2319
+ /// reads the pending rewards for a user in a delegated vault farm
2320
+ /// @param user - the user address
2321
+ /// @param vaultAddress - the address of the vault
2322
+ /// @returns a map of the pending rewards token mint and amount in lamports
2323
+ async getUserPendingRewardsInVaultDelegatedFarm(user, vaultAddress) {
2324
+ const delegatedFarm = await this.getDelegatedFarmForVault(vaultAddress);
2325
+ if (!delegatedFarm) {
2326
+ return new Map();
2327
+ }
2328
+ const farmClient = new farms_sdk_1.Farms(this.getConnection());
2329
+ const userState = await this.computeUserStatePDAForUserInDelegatedVaultFarm(farmClient.getProgramID(), vaultAddress, delegatedFarm, user);
2330
+ return (0, farm_utils_1.getUserPendingRewardsInFarm)(this.getConnection(), userState, delegatedFarm);
2331
+ }
2332
+ /// gets the delegated farm for a vault
2333
+ async getDelegatedFarmForVault(vault) {
2334
+ const response = await fetch(`${utils_2.CDN_ENDPOINT}/resources.json`);
2335
+ if (!response.ok) {
2336
+ console.log(`Failed to fetch CDN for user pending rewards in vault delegated farm: ${response.statusText}`);
2337
+ return undefined;
2338
+ }
2339
+ const data = (await response.json());
2340
+ const delegatedVaultFarms = data['mainnet-beta']?.delegatedVaultFarms;
2341
+ if (!delegatedVaultFarms) {
2342
+ return undefined;
2343
+ }
2344
+ const delegatedFarmWithVault = delegatedVaultFarms.find((vaultWithFarm) => vaultWithFarm.vault === vault);
2345
+ if (!delegatedFarmWithVault) {
2346
+ return undefined;
2347
+ }
2348
+ return (0, kit_1.address)(delegatedFarmWithVault.farm);
2349
+ }
2350
+ /// reads the pending rewards for a user in the reserves farms of a vault
2351
+ /// @param user - the user address
2352
+ /// @param vault - the vault
2353
+ /// @param [vaultReservesMap] - the vault reserves map to get the reserves for; if not provided, the function will fetch the reserves
2354
+ /// @returns a map of the pending rewards token mint and amount in lamports
2355
+ async getUserPendingRewardsInVaultReservesFarms(user, vault, vaultReservesMap) {
2356
+ const vaultState = await vault.getState();
2357
+ const vaultReservesState = vaultReservesMap ? vaultReservesMap : await this.loadVaultReserves(vaultState);
2358
+ const vaultReserves = vaultState.vaultAllocationStrategy
2359
+ .map((allocationStrategy) => allocationStrategy.reserve)
2360
+ .filter((reserve) => reserve !== lib_1.DEFAULT_PUBLIC_KEY);
2361
+ const pendingRewardsPerToken = new Map();
2362
+ const farmClient = new farms_sdk_1.Farms(this.getConnection());
2363
+ for (const reserveAddress of vaultReserves) {
2364
+ const reserveState = vaultReservesState.get(reserveAddress);
2365
+ if (!reserveState) {
2366
+ console.log(`Reserve to read farm incentives for not found: ${reserveAddress}`);
2367
+ continue;
2368
+ }
2369
+ if (reserveState.state.farmCollateral === lib_1.DEFAULT_PUBLIC_KEY) {
2370
+ continue;
2371
+ }
2372
+ const delegatee = await this.computeUserFarmStateDelegateePDAForUserInVault(farmClient.getProgramID(), vault.address, reserveAddress, user);
2373
+ const userState = await (0, dist_1.getUserStatePDA)(farmClient.getProgramID(), reserveState.state.farmCollateral, delegatee[0]);
2374
+ const pendingRewards = await (0, farm_utils_1.getUserPendingRewardsInFarm)(this.getConnection(), userState, reserveState.state.farmCollateral);
2375
+ pendingRewards.forEach((reward, token) => {
2376
+ const existingReward = pendingRewardsPerToken.get(token);
2377
+ if (existingReward) {
2378
+ pendingRewardsPerToken.set(token, existingReward.add(reward));
2379
+ }
2380
+ else {
2381
+ pendingRewardsPerToken.set(token, reward);
2382
+ }
2383
+ });
2384
+ }
2385
+ return pendingRewardsPerToken;
2386
+ }
2387
+ /// reads the pending rewards for a user in the vault farm, the reserves farms of the vault and the delegated vault farm
2388
+ /// @param user - the user address
2389
+ /// @param vault - the vault
2390
+ /// @param [vaultReservesMap] - the vault reserves map to get the reserves for; if not provided, the function will fetch the reserves
2391
+ /// @returns a struct containing the pending rewards in the vault farm, the reserves farms of the vault and the delegated vault farm, and the total pending rewards in lamports
2392
+ async getAllPendingRewardsForUserInVault(user, vault, vaultReservesMap) {
2393
+ const pendingRewardsInVaultFarm = await this.getUserPendingRewardsInVaultFarm(user, vault);
2394
+ const pendingRewardsInVaultReservesFarms = await this.getUserPendingRewardsInVaultReservesFarms(user, vault, vaultReservesMap);
2395
+ const pendingRewardsInVaultDelegatedFarm = await this.getUserPendingRewardsInVaultDelegatedFarm(user, vault.address);
2396
+ const totalPendingRewards = new Map();
2397
+ pendingRewardsInVaultFarm.forEach((reward, token) => {
2398
+ const existingReward = totalPendingRewards.get(token);
2399
+ if (existingReward) {
2400
+ totalPendingRewards.set(token, existingReward.add(reward));
2401
+ }
2402
+ else {
2403
+ totalPendingRewards.set(token, reward);
2404
+ }
2405
+ });
2406
+ pendingRewardsInVaultReservesFarms.forEach((reward, token) => {
2407
+ const existingReward = totalPendingRewards.get(token);
2408
+ if (existingReward) {
2409
+ totalPendingRewards.set(token, existingReward.add(reward));
2410
+ }
2411
+ else {
2412
+ totalPendingRewards.set(token, reward);
2413
+ }
2414
+ });
2415
+ pendingRewardsInVaultDelegatedFarm.forEach((reward, token) => {
2416
+ const existingReward = totalPendingRewards.get(token);
2417
+ if (existingReward) {
2418
+ totalPendingRewards.set(token, existingReward.add(reward));
2419
+ }
2420
+ else {
2421
+ totalPendingRewards.set(token, reward);
2422
+ }
2423
+ });
2424
+ return {
2425
+ pendingRewardsInVaultFarm,
2426
+ pendingRewardsInVaultReservesFarms,
2427
+ pendingRewardsInVaultDelegatedFarm,
2428
+ totalPendingRewards,
2429
+ };
2430
+ }
2431
+ /**
2432
+ * This function will return the instructions to claim the rewards for the farm of a vault, the delegated farm of the vault and the reserves farms of the vault
2433
+ * @param user - the user to claim the rewards
2434
+ * @param vault - the vault
2435
+ * @param [vaultReservesMap] - the vault reserves map to get the reserves for; if not provided, the function will fetch the reserves
2436
+ * @returns the instructions to claim the rewards for the farm of the vault, the delegated farm of the vault and the reserves farms of the vault
2437
+ */
2438
+ async getClaimAllRewardsForVaultIxs(user, vault, vaultReservesMap) {
2439
+ const [vaultFarmIxs, delegatedFarmIxs, reservesFarmsIxs] = await Promise.all([
2440
+ this.getClaimVaultFarmRewardsIxs(user, vault),
2441
+ this.getClaimVaultDelegatedFarmRewardsIxs(user, vault),
2442
+ this.getClaimVaultReservesFarmsRewardsIxs(user, vault, vaultReservesMap),
2443
+ ]);
2444
+ return [...new Set([...vaultFarmIxs, ...delegatedFarmIxs, ...reservesFarmsIxs])];
2445
+ }
2446
+ /**
2447
+ * This function will return the instructions to claim the rewards for the farm of a vault
2448
+ * @param user - the user to claim the rewards
2449
+ * @param vault - the vault
2450
+ * @returns the instructions to claim the rewards for the farm of the vault
2451
+ */
2452
+ async getClaimVaultFarmRewardsIxs(user, vault) {
2453
+ const vaultState = await vault.getState();
2454
+ const hasFarm = await vault.hasFarm();
2455
+ if (!hasFarm) {
2456
+ return [];
2457
+ }
2458
+ const farmClient = new farms_sdk_1.Farms(this.getConnection());
2459
+ const pendingRewardsInVaultFarm = await this.getUserPendingRewardsInVaultFarm(user.address, vault);
2460
+ // if there are no pending rewards of their total is 0 no ix is needed
2461
+ const totalPendingRewards = Array.from(pendingRewardsInVaultFarm.values()).reduce((acc, reward) => acc.add(reward), new decimal_js_1.default(0));
2462
+ if (totalPendingRewards.eq(0)) {
2463
+ return [];
2464
+ }
2465
+ return farmClient.claimForUserForFarmAllRewardsIx(user, vaultState.vaultFarm, false);
2466
+ }
2467
+ /**
2468
+ * This function will return the instructions to claim the rewards for the delegated farm of a vault
2469
+ * @param user - the user to claim the rewards
2470
+ * @param vault - the vault
2471
+ * @returns the instructions to claim the rewards for the delegated farm of the vault
2472
+ */
2473
+ async getClaimVaultDelegatedFarmRewardsIxs(user, vault) {
2474
+ const delegatedFarm = await this.getDelegatedFarmForVault(vault.address);
2475
+ if (!delegatedFarm) {
2476
+ return [];
2477
+ }
2478
+ const farmClient = new farms_sdk_1.Farms(this.getConnection());
2479
+ const delegatee = await this.computeDelegateeForUserInDelegatedFarm(farmClient.getProgramID(), vault.address, delegatedFarm, user.address);
2480
+ const userState = await (0, dist_1.getUserStatePDA)(farmClient.getProgramID(), delegatedFarm, delegatee);
2481
+ // check if the user state exists
2482
+ const userStateExists = await (0, kit_1.fetchEncodedAccount)(this.getConnection(), userState);
2483
+ if (!userStateExists.exists) {
2484
+ return [];
2485
+ }
2486
+ return farmClient.claimForUserForFarmAllRewardsIx(user, delegatedFarm, true, [delegatee]);
2487
+ }
2488
+ /**
2489
+ * This function will return the instructions to claim the rewards for the reserves farms of a vault
2490
+ * @param user - the user to claim the rewards
2491
+ * @param vault - the vault
2492
+ * @param [vaultReservesMap] - the vault reserves map to get the reserves for; if not provided, the function will fetch the reserves
2493
+ * @returns the instructions to claim the rewards for the reserves farms of the vault
2494
+ */
2495
+ async getClaimVaultReservesFarmsRewardsIxs(user, vault, vaultReservesMap) {
2496
+ const vaultState = await vault.getState();
2497
+ const vaultReservesState = vaultReservesMap ? vaultReservesMap : await this.loadVaultReserves(vaultState);
2498
+ const vaultReserves = vaultState.vaultAllocationStrategy
2499
+ .map((allocationStrategy) => allocationStrategy.reserve)
2500
+ .filter((reserve) => reserve !== lib_1.DEFAULT_PUBLIC_KEY);
2501
+ const ixs = [];
2502
+ const farmClient = new farms_sdk_1.Farms(this.getConnection());
2503
+ for (const reserveAddress of vaultReserves) {
2504
+ const reserveState = vaultReservesState.get(reserveAddress);
2505
+ if (!reserveState) {
2506
+ console.log(`Reserve to read farm incentives for not found: ${reserveAddress}`);
2507
+ continue;
2508
+ }
2509
+ if (reserveState.state.farmCollateral === lib_1.DEFAULT_PUBLIC_KEY) {
2510
+ continue;
2511
+ }
2512
+ const delegatee = await this.computeUserFarmStateDelegateePDAForUserInVault(farmClient.getProgramID(), vault.address, reserveAddress, user.address);
2513
+ const userState = await (0, dist_1.getUserStatePDA)(farmClient.getProgramID(), reserveState.state.farmCollateral, delegatee[0]);
2514
+ const pendingRewards = await (0, farm_utils_1.getUserPendingRewardsInFarm)(this.getConnection(), userState, reserveState.state.farmCollateral);
2515
+ const totalPendingRewards = Array.from(pendingRewards.values()).reduce((acc, reward) => acc.add(reward), new decimal_js_1.default(0));
2516
+ if (totalPendingRewards.eq(0)) {
2517
+ continue;
2518
+ }
2519
+ const ix = await farmClient.claimForUserForFarmAllRewardsIx(user, reserveState.state.farmCollateral, true, [
2520
+ delegatee[0],
2521
+ ]);
2522
+ ixs.push(...ix);
2523
+ }
2524
+ return ixs;
2525
+ }
2253
2526
  appendRemainingAccountsForVaultReserves(ix, vaultReserves, vaultReservesState) {
2254
2527
  let vaultReservesAccountMetas = [];
2255
2528
  let vaultReservesLendingMarkets = [];
@@ -2274,14 +2547,24 @@ class KaminoVault {
2274
2547
  address;
2275
2548
  state;
2276
2549
  programId;
2277
- constructor(vaultAddress, state, programId = exports.kaminoVaultId) {
2550
+ client;
2551
+ vaultReservesStateCache;
2552
+ constructor(rpc, vaultAddress, state, programId = exports.kaminoVaultId, recentSlotDurationMs = lib_1.DEFAULT_RECENT_SLOT_DURATION_MS) {
2278
2553
  this.address = vaultAddress;
2279
2554
  this.state = state;
2280
2555
  this.programId = programId;
2556
+ this.client = new KaminoVaultClient(rpc, recentSlotDurationMs);
2557
+ }
2558
+ static loadWithClientAndState(client, vaultAddress, state) {
2559
+ const vault = new KaminoVault(client.getConnection(), vaultAddress);
2560
+ vault.state = state;
2561
+ vault.programId = client.getProgramID();
2562
+ vault.client = client;
2563
+ return vault;
2281
2564
  }
2282
- async getState(rpc) {
2565
+ async getState() {
2283
2566
  if (!this.state) {
2284
- const res = await accounts_1.VaultState.fetch(rpc, this.address, this.programId);
2567
+ const res = await accounts_1.VaultState.fetch(this.client.getConnection(), this.address, this.programId);
2285
2568
  if (!res) {
2286
2569
  throw new Error('Invalid vault');
2287
2570
  }
@@ -2292,17 +2575,110 @@ class KaminoVault {
2292
2575
  return this.state;
2293
2576
  }
2294
2577
  }
2295
- async reloadState(rpc) {
2296
- this.state = await accounts_1.VaultState.fetch(rpc, this.address, this.programId);
2578
+ async reloadVaultReserves() {
2579
+ this.vaultReservesStateCache = await this.client.loadVaultReserves(this.state);
2580
+ }
2581
+ async reloadState() {
2582
+ this.state = await accounts_1.VaultState.fetch(this.client.getConnection(), this.address, this.programId);
2297
2583
  if (!this.state) {
2298
2584
  throw new Error('Could not fetch vault');
2299
2585
  }
2300
2586
  return this.state;
2301
2587
  }
2302
- async hasFarm(rpc) {
2303
- const state = await this.getState(rpc);
2588
+ async hasFarm() {
2589
+ const state = await this.getState();
2304
2590
  return state.vaultFarm !== lib_1.DEFAULT_PUBLIC_KEY;
2305
2591
  }
2592
+ /**
2593
+ * This will return an VaultHoldings object which contains the amount available (uninvested) in vault, total amount invested in reseves and a breakdown of the amount invested in each reserve
2594
+ * @returns an VaultHoldings object representing the amount available (uninvested) in vault, total amount invested in reseves and a breakdown of the amount invested in each reserve
2595
+ */
2596
+ async getVaultHoldings() {
2597
+ if (!this.state || !this.vaultReservesStateCache) {
2598
+ await this.reloadState();
2599
+ await this.reloadVaultReserves();
2600
+ }
2601
+ return await this.client.getVaultHoldings(this.state, undefined, this.vaultReservesStateCache, undefined);
2602
+ }
2603
+ /**
2604
+ * This will return the a map between reserve pubkey and the allocation overview for the reserve
2605
+ * @returns a map between reserve pubkey and the allocation overview for the reserve
2606
+ */
2607
+ async getVaultAllocations() {
2608
+ if (!this.state) {
2609
+ await this.reloadState();
2610
+ }
2611
+ return this.client.getVaultAllocations(this.state);
2612
+ }
2613
+ /**
2614
+ * This will return the APY of the vault based on the current invested amounts and the theoretical APY if all the available tokens were invested
2615
+ * @returns a struct containing actualAPY and theoreticalAPY for the vault
2616
+ */
2617
+ async getAPYs(slot) {
2618
+ if (!this.state || !this.vaultReservesStateCache) {
2619
+ await this.reloadState();
2620
+ await this.reloadVaultReserves();
2621
+ }
2622
+ const latestSlot = slot ?? (await this.client.getConnection().getSlot({ commitment: 'confirmed' }).send());
2623
+ const actualApy = await this.client.getVaultActualAPY(this.state, latestSlot, this.vaultReservesStateCache);
2624
+ const theoreticalApy = await this.client.getVaultTheoreticalAPY(this.state, latestSlot, this.vaultReservesStateCache);
2625
+ return {
2626
+ actualAPY: actualApy,
2627
+ theoreticalAPY: theoreticalApy,
2628
+ };
2629
+ }
2630
+ /**
2631
+ * This method returns the exchange rate of the vault (tokens per share)
2632
+ * @returns - Decimal representing the exchange rate (tokens per share)
2633
+ */
2634
+ async getExchangeRate(slot) {
2635
+ if (!this.state || !this.vaultReservesStateCache) {
2636
+ await this.reloadState();
2637
+ await this.reloadVaultReserves();
2638
+ }
2639
+ const latestSlot = slot ?? (await this.client.getConnection().getSlot({ commitment: 'confirmed' }).send());
2640
+ const tokensPerShare = await this.client.getTokensPerShareSingleVault(this.state, latestSlot);
2641
+ return tokensPerShare;
2642
+ }
2643
+ /**
2644
+ * This method returns the user shares balance for a given vault
2645
+ * @param user - user to calculate the shares balance for
2646
+ * @param vault - vault to calculate shares balance for
2647
+ * @returns - a struct of user share balance (staked in vault farm if the vault has a farm and unstaked) in decimal (not lamports)
2648
+ */
2649
+ async getUserShares(user) {
2650
+ return this.client.getUserSharesBalanceSingleVault(user, this);
2651
+ }
2652
+ /**
2653
+ * This function creates instructions to deposit into a vault. It will also create ATA creation instructions for the vault shares that the user receives in return
2654
+ * @param user - user to deposit
2655
+ * @param tokenAmount - token amount to be deposited, in decimals (will be converted in lamports)
2656
+ * @param [vaultReservesMap] - optional parameter; a hashmap from each reserve pubkey to the reserve state. Optional. If provided the function will be significantly faster as it will not have to fetch the reserves
2657
+ * @param [farmState] - the state of the vault farm, if the vault has a farm. Optional. If not provided, it will be fetched
2658
+ * @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
2659
+ */
2660
+ async depositIxs(user, tokenAmount, vaultReservesMap, farmState) {
2661
+ if (vaultReservesMap) {
2662
+ this.vaultReservesStateCache = vaultReservesMap;
2663
+ }
2664
+ return this.client.depositIxs(user, this, tokenAmount, this.vaultReservesStateCache, farmState);
2665
+ }
2666
+ /**
2667
+ * 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
2668
+ * @param user - user to withdraw
2669
+ * @param shareAmount - share amount to withdraw (in tokens, not lamports), in order to withdraw everything, any value > user share amount
2670
+ * @param slot - current slot, used to estimate the interest earned in the different reserves with allocation from the vault
2671
+ * @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
2672
+ * @param [farmState] - the state of the vault farm, if the vault has a farm. Optional. If not provided, it will be fetched
2673
+ * @returns an array of instructions to create missing ATAs if needed and the withdraw instructions
2674
+ */
2675
+ async withdrawIxs(user, shareAmount, slot, vaultReservesMap, farmState) {
2676
+ if (vaultReservesMap) {
2677
+ this.vaultReservesStateCache = vaultReservesMap;
2678
+ }
2679
+ const currentSlot = slot ?? (await this.client.getConnection().getSlot({ commitment: 'confirmed' }).send());
2680
+ return this.client.withdrawIxs(user, this, shareAmount, currentSlot, this.vaultReservesStateCache, farmState);
2681
+ }
2306
2682
  }
2307
2683
  exports.KaminoVault = KaminoVault;
2308
2684
  /**
@@ -2385,12 +2761,36 @@ function parseVaultAdmin(vault, signer) {
2385
2761
  function parseVaultPendingAdmin(vault, signer) {
2386
2762
  return signer ?? (0, signer_1.noopSigner)(vault.pendingAdmin);
2387
2763
  }
2388
- function printHoldings(holdings) {
2389
- console.log('Holdings:');
2390
- console.log(' Available:', holdings.available.toString());
2391
- console.log(' Invested:', holdings.invested.toString());
2392
- console.log(' Total AUM including fees:', holdings.totalAUMIncludingFees.toString());
2393
- console.log(' Pending fees:', holdings.pendingFees.toString());
2394
- console.log(' Invested in reserves:', (0, utils_1.pubkeyHashMapToJson)(holdings.investedInReserves));
2764
+ class VaultHoldings {
2765
+ available;
2766
+ invested;
2767
+ investedInReserves;
2768
+ pendingFees;
2769
+ totalAUMIncludingFees;
2770
+ constructor(params) {
2771
+ this.available = params.available;
2772
+ this.invested = params.invested;
2773
+ this.investedInReserves = params.investedInReserves;
2774
+ this.pendingFees = params.pendingFees;
2775
+ this.totalAUMIncludingFees = params.totalAUMIncludingFees;
2776
+ }
2777
+ asJSON() {
2778
+ return {
2779
+ available: this.available.toString(),
2780
+ invested: this.invested.toString(),
2781
+ totalAUMIncludingFees: this.totalAUMIncludingFees.toString(),
2782
+ pendingFees: this.pendingFees.toString(),
2783
+ investedInReserves: (0, utils_1.pubkeyHashMapToJson)(this.investedInReserves),
2784
+ };
2785
+ }
2786
+ print() {
2787
+ console.log('Holdings:');
2788
+ console.log(' Available:', this.available.toString());
2789
+ console.log(' Invested:', this.invested.toString());
2790
+ console.log(' Total AUM including fees:', this.totalAUMIncludingFees.toString());
2791
+ console.log(' Pending fees:', this.pendingFees.toString());
2792
+ console.log(' Invested in reserves:', (0, utils_1.pubkeyHashMapToJson)(this.investedInReserves));
2793
+ }
2395
2794
  }
2795
+ exports.VaultHoldings = VaultHoldings;
2396
2796
  //# sourceMappingURL=vault.js.map