@kamino-finance/klend-sdk 5.3.0 → 5.4.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 (45) hide show
  1. package/dist/classes/action.d.ts +0 -3
  2. package/dist/classes/action.d.ts.map +1 -1
  3. package/dist/classes/action.js +7 -10
  4. package/dist/classes/action.js.map +1 -1
  5. package/dist/classes/manager.d.ts +17 -5
  6. package/dist/classes/manager.d.ts.map +1 -1
  7. package/dist/classes/manager.js +25 -15
  8. package/dist/classes/manager.js.map +1 -1
  9. package/dist/classes/market.d.ts +2 -2
  10. package/dist/classes/market.d.ts.map +1 -1
  11. package/dist/classes/market.js +52 -20
  12. package/dist/classes/market.js.map +1 -1
  13. package/dist/classes/vault.d.ts +24 -4
  14. package/dist/classes/vault.d.ts.map +1 -1
  15. package/dist/classes/vault.js +54 -15
  16. package/dist/classes/vault.js.map +1 -1
  17. package/dist/client_kamino_manager.d.ts.map +1 -1
  18. package/dist/client_kamino_manager.js +14 -1
  19. package/dist/client_kamino_manager.js.map +1 -1
  20. package/dist/idl_codegen_kamino_vault/accounts/VaultState.d.ts +6 -0
  21. package/dist/idl_codegen_kamino_vault/accounts/VaultState.d.ts.map +1 -1
  22. package/dist/idl_codegen_kamino_vault/accounts/VaultState.js +13 -1
  23. package/dist/idl_codegen_kamino_vault/accounts/VaultState.js.map +1 -1
  24. package/dist/idl_codegen_kamino_vault/types/VaultAllocation.d.ts +5 -0
  25. package/dist/idl_codegen_kamino_vault/types/VaultAllocation.d.ts.map +1 -1
  26. package/dist/idl_codegen_kamino_vault/types/VaultAllocation.js +8 -1
  27. package/dist/idl_codegen_kamino_vault/types/VaultAllocation.js.map +1 -1
  28. package/dist/utils/constants.d.ts +4 -0
  29. package/dist/utils/constants.d.ts.map +1 -1
  30. package/dist/utils/constants.js +5 -1
  31. package/dist/utils/constants.js.map +1 -1
  32. package/dist/utils/rpc.d.ts +2 -1
  33. package/dist/utils/rpc.d.ts.map +1 -1
  34. package/dist/utils/rpc.js +8 -6
  35. package/dist/utils/rpc.js.map +1 -1
  36. package/package.json +9 -4
  37. package/src/classes/action.ts +1 -4
  38. package/src/classes/manager.ts +38 -13
  39. package/src/classes/market.ts +73 -21
  40. package/src/classes/vault.ts +86 -15
  41. package/src/client_kamino_manager.ts +21 -1
  42. package/src/idl_codegen_kamino_vault/accounts/VaultState.ts +17 -1
  43. package/src/idl_codegen_kamino_vault/types/VaultAllocation.ts +10 -1
  44. package/src/utils/constants.ts +6 -0
  45. package/src/utils/rpc.ts +8 -5
package/dist/utils/rpc.js CHANGED
@@ -8,19 +8,21 @@ exports.getAccountOwner = getAccountOwner;
8
8
  const web3_js_1 = require("@solana/web3.js");
9
9
  const buffer_1 = require("buffer");
10
10
  const axios_1 = __importDefault(require("axios"));
11
- const zstd_wasm_1 = require("@bokuweb/zstd-wasm");
12
11
  const uuid_1 = require("uuid");
12
+ const zstddec_1 = require("zstddec");
13
+ const decoder = new zstddec_1.ZSTDDecoder();
13
14
  (async () => {
14
- await (0, zstd_wasm_1.init)();
15
+ await decoder.init();
15
16
  })();
16
17
  /**
17
18
  * Uses zstd compression when fetching all accounts owned by a program for a smaller response size
18
19
  * Uses axios instead of node-fetch to work around a bug in node-fetch that causes subsequent requests with different encoding to fail
19
20
  * @param connection
20
21
  * @param programId
22
+ * @param structSize - the size of the decompressed account data struct
21
23
  * @param configOrCommitment
22
24
  */
23
- async function getProgramAccounts(connection, programId, configOrCommitment) {
25
+ async function getProgramAccounts(connection, programId, structSize, configOrCommitment) {
24
26
  const programIdStr = programId.toBase58();
25
27
  const { commitment, config } = extractCommitmentFromConfig(configOrCommitment);
26
28
  const { encoding: _encoding, ...configWithoutEncoding } = config || {};
@@ -52,7 +54,7 @@ async function getProgramAccounts(connection, programId, configOrCommitment) {
52
54
  }
53
55
  const res = unsafeRes;
54
56
  const deser = res.result.map(async (account) => ({
55
- account: await deserializeAccountInfo(account.account),
57
+ account: await deserializeAccountInfo(account.account, structSize),
56
58
  pubkey: new web3_js_1.PublicKey(account.pubkey),
57
59
  }));
58
60
  const x = await Promise.all(deser);
@@ -65,8 +67,8 @@ async function getAccountOwner(connection, address) {
65
67
  }
66
68
  return acc.owner;
67
69
  }
68
- async function deserializeAccountInfo(accountInfo) {
69
- const data = (0, zstd_wasm_1.decompress)(buffer_1.Buffer.from(accountInfo.data[0], 'base64'));
70
+ async function deserializeAccountInfo(accountInfo, size) {
71
+ const data = decoder.decode(buffer_1.Buffer.from(accountInfo.data[0], 'base64'), size);
70
72
  return {
71
73
  owner: accountInfo.owner,
72
74
  lamports: accountInfo.lamports,
@@ -1 +1 @@
1
- {"version":3,"file":"rpc.js","sourceRoot":"","sources":["../../src/utils/rpc.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"rpc.js","sourceRoot":"","sources":["../../src/utils/rpc.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kamino-finance/klend-sdk",
3
- "version": "5.3.0",
3
+ "version": "5.4.1",
4
4
  "description": "Typescript SDK for interacting with the Kamino Lending (klend) protocol",
5
5
  "repository": {
6
6
  "type": "git",
@@ -29,6 +29,7 @@
29
29
  "docs": "typedoc",
30
30
  "build": "rm -rf dist/; tsc",
31
31
  "build:test": "yarn tsc --project ./tests/tsconfig.json",
32
+ "build:watch": "yarn tsc --watch",
32
33
  "test": "npx ts-mocha tests/**/*.test.ts --exit",
33
34
  "watch": "tsc --watch",
34
35
  "coverage": "jest --coverage",
@@ -43,10 +44,11 @@
43
44
  "start-validator": "solana-test-validator $(./deps/test-validator-params.sh)",
44
45
  "start-validator-and-test": "yarn start-server-and-test 'yarn start-validator' http://127.0.0.1:8899/health test",
45
46
  "dump-programs": "./deps/dump-from-mainnet.sh",
46
- "kamino-manager": "tsx src/client_kamino_manager.ts"
47
+ "kamino-manager": "tsx src/client_kamino_manager.ts",
48
+ "dev": "concurrently \"yarn build:watch\" \"nodemon --watch dist -e js,ts --exec 'yarn dev:push-and-update'\"",
49
+ "dev:push-and-update": "yalc publish && cd examples && yalc update"
47
50
  },
48
51
  "dependencies": {
49
- "@bokuweb/zstd-wasm": "^0.0.20",
50
52
  "@coral-xyz/anchor": "^0.28.0",
51
53
  "@coral-xyz/borsh": "^0.28.0",
52
54
  "@kamino-finance/farms-sdk": "^2.0.3",
@@ -64,7 +66,8 @@
64
66
  "commander": "^9.3.0",
65
67
  "decimal.js": "^10.4.3",
66
68
  "exponential-backoff": "^3.1.1",
67
- "uuid": "^10.0.0"
69
+ "uuid": "^10.0.0",
70
+ "zstddec": "^0.1.0"
68
71
  },
69
72
  "devDependencies": {
70
73
  "@orca-so/sdk": "^1.2.25",
@@ -81,6 +84,7 @@
81
84
  "anchor-client-gen": "^0.28.1",
82
85
  "chai": "^4.3.7",
83
86
  "chai-bignumber": "^3.1.0",
87
+ "concurrently": "^9.1.0",
84
88
  "eslint": "^8.4.0",
85
89
  "eslint-config-airbnb-base": "^15.0.0",
86
90
  "eslint-config-airbnb-typescript": "^16.1.0",
@@ -90,6 +94,7 @@
90
94
  "jest": "^27.4.3",
91
95
  "jest-fetch-mock": "^3.0.3",
92
96
  "mocha": "^10.2.0",
97
+ "nodemon": "^3.1.7",
93
98
  "prettier": "^2.8.8",
94
99
  "start-server-and-test": "^2.0.0",
95
100
  "ts-jest": "^28.0.7",
@@ -58,6 +58,7 @@ import {
58
58
  getAssociatedTokenAddress,
59
59
  ScopeRefresh,
60
60
  createAtasIdempotent,
61
+ POSITION_LIMIT,
61
62
  } from '../utils';
62
63
  import { KaminoMarket } from './market';
63
64
  import { KaminoObligation } from './obligation';
@@ -69,10 +70,6 @@ import { VanillaObligation } from '../utils/ObligationType';
69
70
  import { PROGRAM_ID } from '../lib';
70
71
  import { U16_MAX } from '@kamino-finance/scope-sdk';
71
72
 
72
- export const POSITION_LIMIT = 10;
73
- export const BORROWS_LIMIT = 5;
74
- export const DEPOSITS_LIMIT = 8;
75
-
76
73
  const SOL_PADDING_FOR_INTEREST = new BN('1000000');
77
74
 
78
75
  export type ActionType =
@@ -17,6 +17,8 @@ import {
17
17
  MarketOverview,
18
18
  ReserveAllocationConfig,
19
19
  ReserveOverview,
20
+ SimulatedVaultHoldingsWithEarnedInterest,
21
+ VaultFees,
20
22
  VaultFeesPct,
21
23
  VaultHolder,
22
24
  VaultHoldings,
@@ -495,20 +497,18 @@ export class KaminoManager {
495
497
 
496
498
  /**
497
499
  * Get all vaults
498
- * @param useOptimisedRPCCall - if set to true, it will use the optimized getProgramAccounts RPC call, which is more efficient but doesn't work in web environments
499
500
  * @returns an array of all vaults
500
501
  */
501
- async getAllVaults(useOptimisedRPCCall: boolean = true): Promise<KaminoVault[]> {
502
- return this._vaultClient.getAllVaults(useOptimisedRPCCall);
502
+ async getAllVaults(): Promise<KaminoVault[]> {
503
+ return this._vaultClient.getAllVaults();
503
504
  }
504
505
 
505
506
  /**
506
507
  * Get all vaults for owner
507
508
  * @param owner the pubkey of the vaults owner
508
- * @param useOptimisedRPCCall - if set to true, it will use the optimized getProgramAccounts RPC call, which is more efficient but doesn't work in web environments
509
509
  * @returns an array of all vaults owned by a given pubkey
510
510
  */
511
- async getAllVaultsForOwner(owner: PublicKey, useOptimisedRPCCall: boolean = true): Promise<KaminoVault[]> {
511
+ async getAllVaultsForOwner(owner: PublicKey): Promise<KaminoVault[]> {
512
512
  const filters = [
513
513
  {
514
514
  dataSize: VaultState.layout.span + 8,
@@ -527,16 +527,15 @@ export class KaminoManager {
527
527
  },
528
528
  ];
529
529
 
530
- let kaminoVaults: GetProgramAccountsResponse = [];
531
-
532
- if (useOptimisedRPCCall) {
533
- kaminoVaults = await getProgramAccounts(this._connection, this._kaminoVaultProgramId, {
530
+ const kaminoVaults: GetProgramAccountsResponse = await getProgramAccounts(
531
+ this._connection,
532
+ this._kaminoVaultProgramId,
533
+ VaultState.layout.span + 8,
534
+ {
534
535
  commitment: this._connection.commitment ?? 'processed',
535
536
  filters,
536
- });
537
- } else {
538
- kaminoVaults = await this._connection.getProgramAccounts(this._kaminoVaultProgramId, { filters });
539
- }
537
+ }
538
+ );
540
539
 
541
540
  return kaminoVaults.map((kaminoVault) => {
542
541
  if (kaminoVault.account === null) {
@@ -704,6 +703,32 @@ export class KaminoManager {
704
703
  return this._vaultClient.getVaultCumulativeInterest(vaultState);
705
704
  }
706
705
 
706
+ /**
707
+ * Simulate the current holdings of the vault and the earned interest
708
+ * @param vaultState the kamino vault state to get simulated holdings and earnings for
709
+ * @param vaultReserves optional; the state of the reserves in the vault allocation
710
+ * @returns a struct of simulated vault holdings and earned interest
711
+ */
712
+ async calculateSimulatedHoldingsWithInterest(
713
+ vaultState: VaultState,
714
+ vaultReserves?: PubkeyHashMap<PublicKey, KaminoReserve>
715
+ ): Promise<SimulatedVaultHoldingsWithEarnedInterest> {
716
+ return this._vaultClient.calculateSimulatedHoldingsWithInterest(vaultState, vaultReserves);
717
+ }
718
+
719
+ /**
720
+ * Simulate the current holdings and compute the fees that would be charged
721
+ * @param vaultState the kamino vault state to get simulated fees for
722
+ * @param simulatedCurrentHoldingsWithInterest optional; the simulated holdings and interest earned by the vault
723
+ * @returns a struct of simulated management and interest fees
724
+ */
725
+ async calculateSimulatedFees(
726
+ vaultState: VaultState,
727
+ simulatedCurrentHoldingsWithInterest?: SimulatedVaultHoldingsWithEarnedInterest
728
+ ): Promise<VaultFees> {
729
+ return this._vaultClient.calculateSimulatedFees(vaultState, simulatedCurrentHoldingsWithInterest);
730
+ }
731
+
707
732
  /**
708
733
  * This will load the onchain state for all the reserves that the vault has allocations for
709
734
  * @param vaultState - the vault state to load reserves for
@@ -1,4 +1,4 @@
1
- import { AccountInfo, Connection, GetProgramAccountsResponse, PublicKey } from '@solana/web3.js';
1
+ import { AccountInfo, Connection, PublicKey } from '@solana/web3.js';
2
2
  import { KaminoObligation } from './obligation';
3
3
  import { KaminoReserve } from './reserve';
4
4
  import { LendingMarket, Obligation, UserMetadata, ReferrerTokenState, Reserve } from '../idl_codegen/accounts';
@@ -21,6 +21,7 @@ import {
21
21
  PubkeyHashMap,
22
22
  CandidatePrice,
23
23
  PublicKeySet,
24
+ DEPOSITS_LIMIT,
24
25
  } from '../utils';
25
26
  import base58 from 'bs58';
26
27
  import { BN } from '@coral-xyz/anchor';
@@ -531,9 +532,8 @@ export class KaminoMarket {
531
532
  * This function will likely require an RPC capable of returning more than the default 100k rows in a single scan
532
533
  *
533
534
  * @param tag
534
- * @param useOptimisedRPCCall - use the optimised RPC call (compressed) to get all obligations
535
535
  */
536
- async getAllObligationsForMarket(tag?: number, useOptimisedRPCCall: boolean = true): Promise<KaminoObligation[]> {
536
+ async getAllObligationsForMarket(tag?: number): Promise<KaminoObligation[]> {
537
537
  const filters = [
538
538
  {
539
539
  dataSize: Obligation.layout.span + 8,
@@ -558,26 +558,14 @@ export class KaminoMarket {
558
558
  const collateralExchangeRates = new PubkeyHashMap<PublicKey, Decimal>();
559
559
  const cumulativeBorrowRates = new PubkeyHashMap<PublicKey, Decimal>();
560
560
 
561
- let obligations: GetProgramAccountsResponse = [];
562
- let slot = 0;
563
- const slotPromise = this.connection.getSlot();
564
-
565
- if (useOptimisedRPCCall) {
566
- [slot, obligations] = await Promise.all([
567
- slotPromise,
568
- getProgramAccounts(this.connection, this.programId, {
569
- commitment: this.connection.commitment ?? 'processed',
570
- filters,
571
- dataSlice: { offset: 0, length: ObligationZP.layout.span + 8 }, // truncate the padding
572
- }),
573
- ]);
574
- } else {
575
- const obligationsPromise = this.connection.getProgramAccounts(this.programId, {
561
+ const [slot, obligations] = await Promise.all([
562
+ this.connection.getSlot(),
563
+ getProgramAccounts(this.connection, this.programId, ObligationZP.layout.span + 8, {
564
+ commitment: this.connection.commitment ?? 'processed',
576
565
  filters,
577
566
  dataSlice: { offset: 0, length: ObligationZP.layout.span + 8 }, // truncate the padding
578
- });
579
- [slot, obligations] = await Promise.all([slotPromise, obligationsPromise]);
580
- }
567
+ }),
568
+ ]);
581
569
 
582
570
  return obligations.map((obligation) => {
583
571
  if (obligation.account === null) {
@@ -740,6 +728,70 @@ export class KaminoMarket {
740
728
  });
741
729
  }
742
730
 
731
+ async getAllObligationsByDepositedReserve(reserve: PublicKey) {
732
+ const finalObligations: KaminoObligation[] = [];
733
+ for (let i = 0; i < DEPOSITS_LIMIT; i++) {
734
+ const [slot, obligations] = await Promise.all([
735
+ this.connection.getSlot(),
736
+ this.connection.getProgramAccounts(this.programId, {
737
+ filters: [
738
+ {
739
+ dataSize: Obligation.layout.span + 8,
740
+ },
741
+ {
742
+ memcmp: {
743
+ offset: 96 + 136 * i,
744
+ bytes: reserve.toBase58(),
745
+ },
746
+ },
747
+ {
748
+ memcmp: {
749
+ offset: 32,
750
+ bytes: this.address,
751
+ },
752
+ },
753
+ ],
754
+ }),
755
+ ]);
756
+
757
+ const collateralExchangeRates = new PubkeyHashMap<PublicKey, Decimal>();
758
+ const cumulativeBorrowRates = new PubkeyHashMap<PublicKey, Decimal>();
759
+
760
+ const obligationsBatch = obligations.map((obligation) => {
761
+ if (obligation.account === null) {
762
+ throw new Error('Invalid account');
763
+ }
764
+ if (!obligation.account.owner.equals(this.programId)) {
765
+ throw new Error("account doesn't belong to this program");
766
+ }
767
+
768
+ const obligationAccount = Obligation.decode(obligation.account.data);
769
+
770
+ if (!obligationAccount) {
771
+ throw Error('Could not parse obligation.');
772
+ }
773
+
774
+ KaminoObligation.addRatesForObligation(
775
+ this,
776
+ obligationAccount,
777
+ collateralExchangeRates,
778
+ cumulativeBorrowRates,
779
+ slot
780
+ );
781
+
782
+ return new KaminoObligation(
783
+ this,
784
+ obligation.pubkey,
785
+ obligationAccount,
786
+ collateralExchangeRates,
787
+ cumulativeBorrowRates
788
+ );
789
+ });
790
+ finalObligations.push(...obligationsBatch);
791
+ }
792
+ return finalObligations;
793
+ }
794
+
743
795
  async getAllUserObligations(user: PublicKey) {
744
796
  const [currentSlot, obligations] = await Promise.all([
745
797
  this.connection.getSlot(),
@@ -58,7 +58,7 @@ import { withdraw } from '../idl_codegen_kamino_vault/instructions';
58
58
  import { PROGRAM_ID } from '../idl_codegen/programId';
59
59
  import { DEFAULT_RECENT_SLOT_DURATION_MS, ReserveWithAddress } from './reserve';
60
60
  import { Fraction } from './fraction';
61
- import { createAtasIdempotent, lendingMarketAuthPda, PublicKeySet } from '../utils';
61
+ import { createAtasIdempotent, lendingMarketAuthPda, PublicKeySet, SECONDS_PER_YEAR } from '../utils';
62
62
  import bs58 from 'bs58';
63
63
  import { getAccountOwner, getProgramAccounts } from '../utils/rpc';
64
64
  import {
@@ -69,6 +69,7 @@ import {
69
69
  UpdateVaultConfigIxs,
70
70
  } from './types';
71
71
  import { collToLamportsDecimal } from '@kamino-finance/kliquidity-sdk';
72
+ import { FullBPSDecimal } from '@kamino-finance/kliquidity-sdk/dist/utils/CreationParameters';
72
73
 
73
74
  export const kaminoVaultId = new PublicKey('kvauTFR8qm1dhniz6pYuBZkuene3Hfrs1VQhVRgCNrr');
74
75
  export const kaminoVaultStagingId = new PublicKey('STkvh7ostar39Fwr4uZKASs1RNNuYMFMTsE77FiRsL2');
@@ -1349,16 +1350,14 @@ export class KaminoVaultClient {
1349
1350
  * @param vaultsOverride - a list of vaults to get the tokens per share for; if provided with state it will not fetch the state again
1350
1351
  * @param vaultReservesMap - optional parameter; a hashmap from pubkey to reserve state. If provided the function will be significantly faster as it will not have to fetch the reserves
1351
1352
  * @param slot - current slot, used to estimate the interest earned in the different reserves with allocation from the vault
1352
- * @param useOptimisedRPCCall - if set to true, it will use the optimized getProgramAccounts RPC call, which is more efficient but doesn't work in web environments
1353
1353
  * @returns - token per share value
1354
1354
  */
1355
1355
  async getTokensPerShareAllVaults(
1356
1356
  slot: number,
1357
1357
  vaultsOverride?: Array<KaminoVault>,
1358
- vaultReservesMap?: PubkeyHashMap<PublicKey, KaminoReserve>,
1359
- useOptimisedRPCCall: boolean = true
1358
+ vaultReservesMap?: PubkeyHashMap<PublicKey, KaminoReserve>
1360
1359
  ): Promise<PubkeyHashMap<PublicKey, Decimal>> {
1361
- const vaults = vaultsOverride ? vaultsOverride : await this.getAllVaults(useOptimisedRPCCall);
1360
+ const vaults = vaultsOverride ? vaultsOverride : await this.getAllVaults();
1362
1361
  const vaultTokensPerShare = new PubkeyHashMap<PublicKey, Decimal>();
1363
1362
  for (const vault of vaults) {
1364
1363
  const tokensPerShare = await this.getTokensPerShareSingleVault(vault, slot, vaultReservesMap);
@@ -1370,10 +1369,9 @@ export class KaminoVaultClient {
1370
1369
 
1371
1370
  /**
1372
1371
  * Get all vaults
1373
- * @param useOptimisedRPCCall - if set to true, it will use the optimized getProgramAccounts RPC call, which is more efficient but doesn't work in web environments
1374
1372
  * @returns an array of all vaults
1375
1373
  */
1376
- async getAllVaults(useOptimisedRPCCall: boolean = true): Promise<KaminoVault[]> {
1374
+ async getAllVaults(): Promise<KaminoVault[]> {
1377
1375
  const filters = [
1378
1376
  {
1379
1377
  dataSize: VaultState.layout.span + 8,
@@ -1386,16 +1384,15 @@ export class KaminoVaultClient {
1386
1384
  },
1387
1385
  ];
1388
1386
 
1389
- let kaminoVaults: GetProgramAccountsResponse = [];
1390
-
1391
- if (useOptimisedRPCCall) {
1392
- kaminoVaults = await getProgramAccounts(this._connection, this._kaminoVaultProgramId, {
1387
+ const kaminoVaults: GetProgramAccountsResponse = await getProgramAccounts(
1388
+ this._connection,
1389
+ this._kaminoVaultProgramId,
1390
+ VaultState.layout.span + 8,
1391
+ {
1393
1392
  commitment: this._connection.commitment ?? 'processed',
1394
1393
  filters,
1395
- });
1396
- } else {
1397
- kaminoVaults = await this._connection.getProgramAccounts(this._kaminoVaultProgramId, { filters });
1398
- }
1394
+ }
1395
+ );
1399
1396
 
1400
1397
  return kaminoVaults.map((kaminoVault) => {
1401
1398
  if (kaminoVault.account === null) {
@@ -1926,6 +1923,70 @@ export class KaminoVaultClient {
1926
1923
  const netYieldLamports = new Fraction(vaultState.cumulativeEarnedInterestSf).toDecimal();
1927
1924
  return lamportsToDecimal(netYieldLamports, vaultState.tokenMintDecimals.toString());
1928
1925
  }
1926
+
1927
+ /**
1928
+ * Simulate the current holdings of the vault and the earned interest
1929
+ * @param vaultState the kamino vault state to get simulated holdings and earnings for
1930
+ * @param vaultReserves optional; the state of the reserves in the vault allocation
1931
+ * @returns a struct of simulated vault holdings and earned interest
1932
+ */
1933
+ async calculateSimulatedHoldingsWithInterest(
1934
+ vaultState: VaultState,
1935
+ vaultReserves?: PubkeyHashMap<PublicKey, KaminoReserve>
1936
+ ): Promise<SimulatedVaultHoldingsWithEarnedInterest> {
1937
+ const latestUpdateTs = vaultState.lastFeeChargeTimestamp.toNumber();
1938
+ const lastUpdateSlot = latestUpdateTs / this.recentSlotDurationMs;
1939
+
1940
+ const currentSlot = await this._connection.getSlot('confirmed');
1941
+
1942
+ const lastUpdateHoldingsPromise = this.getVaultHoldings(vaultState, lastUpdateSlot, vaultReserves);
1943
+ const currentHoldingsPromise = this.getVaultHoldings(vaultState, currentSlot, vaultReserves);
1944
+ const [lastUpdateHoldings, currentHoldings] = await Promise.all([
1945
+ lastUpdateHoldingsPromise,
1946
+ currentHoldingsPromise,
1947
+ ]);
1948
+
1949
+ const earnedInterest = currentHoldings.total.sub(lastUpdateHoldings.total);
1950
+
1951
+ return {
1952
+ holdings: currentHoldings,
1953
+ earnedInterest: earnedInterest,
1954
+ };
1955
+ }
1956
+
1957
+ /**
1958
+ * Simulate the current holdings and compute the fees that would be charged
1959
+ * @param vaultState the kamino vault state to get simulated fees for
1960
+ * @param simulatedCurrentHoldingsWithInterest optional; the simulated holdings and interest earned by the vault
1961
+ * @returns a struct of simulated management and interest fees
1962
+ */
1963
+ async calculateSimulatedFees(
1964
+ vaultState: VaultState,
1965
+ simulatedCurrentHoldingsWithInterest?: SimulatedVaultHoldingsWithEarnedInterest
1966
+ ): Promise<VaultFees> {
1967
+ const timestampNow = new Date().getTime();
1968
+ const timestampLastUpdate = vaultState.lastFeeChargeTimestamp.toNumber();
1969
+ const timeElapsed = timestampNow - timestampLastUpdate;
1970
+
1971
+ const simulatedCurrentHoldings = simulatedCurrentHoldingsWithInterest
1972
+ ? simulatedCurrentHoldingsWithInterest
1973
+ : await this.calculateSimulatedHoldingsWithInterest(vaultState);
1974
+
1975
+ const performanceFee = simulatedCurrentHoldings.earnedInterest.mul(
1976
+ new Decimal(vaultState.performanceFeeBps.toString()).div(FullBPSDecimal)
1977
+ );
1978
+
1979
+ const managementFeeFactor = new Decimal(timeElapsed)
1980
+ .mul(new Decimal(vaultState.managementFeeBps.toString()))
1981
+ .div(new Decimal(SECONDS_PER_YEAR));
1982
+ const prevAUM = new Fraction(vaultState.prevAumSf).toDecimal();
1983
+ const mgmtFee = prevAUM.mul(managementFeeFactor);
1984
+
1985
+ return {
1986
+ managementFee: mgmtFee,
1987
+ performanceFee: performanceFee,
1988
+ };
1989
+ }
1929
1990
  } // KaminoVaultClient
1930
1991
 
1931
1992
  export class KaminoVault {
@@ -2046,6 +2107,11 @@ export type VaultHoldings = {
2046
2107
  total: Decimal;
2047
2108
  };
2048
2109
 
2110
+ export type SimulatedVaultHoldingsWithEarnedInterest = {
2111
+ holdings: VaultHoldings;
2112
+ earnedInterest: Decimal;
2113
+ };
2114
+
2049
2115
  export type VaultHoldingsWithUSDValue = {
2050
2116
  holdings: VaultHoldings;
2051
2117
  availableUSD: Decimal;
@@ -2095,3 +2161,8 @@ export type VaultFeesPct = {
2095
2161
  managementFeePct: Decimal;
2096
2162
  performanceFeePct: Decimal;
2097
2163
  };
2164
+
2165
+ export type VaultFees = {
2166
+ managementFee: Decimal;
2167
+ performanceFee: Decimal;
2168
+ };
@@ -922,10 +922,30 @@ async function main() {
922
922
  const env = initializeClient(false, false);
923
923
  const kaminoManager = new KaminoManager(env.connection, env.kLendProgramId, env.kVaultProgramId);
924
924
 
925
- const allVaults = await kaminoManager.getAllVaults(true);
925
+ const allVaults = await kaminoManager.getAllVaults();
926
926
  console.log('all vaults', allVaults);
927
927
  });
928
928
 
929
+ commands
930
+ .command('get-simulated-interest-and-fees')
931
+ .requiredOption('--vault <string>', 'Vault address')
932
+ .action(async ({ vault }) => {
933
+ const env = initializeClient(false, false);
934
+
935
+ const vaultAddress = new PublicKey(vault);
936
+
937
+ const kaminoManager = new KaminoManager(env.connection, env.kLendProgramId, env.kVaultProgramId);
938
+ const vaultState = await new KaminoVault(vaultAddress, undefined, env.kVaultProgramId).getState(env.connection);
939
+
940
+ const simulatedHoldings = await kaminoManager.calculateSimulatedHoldingsWithInterest(vaultState);
941
+
942
+ console.log('Simulated holdings with interest', simulatedHoldings);
943
+
944
+ const simulatedFees = await kaminoManager.calculateSimulatedFees(vaultState, simulatedHoldings);
945
+
946
+ console.log('Simulated fees', simulatedFees);
947
+ });
948
+
929
949
  commands
930
950
  .command('download-lending-market-config')
931
951
  .requiredOption('--lending-market <string>', 'Lending Market Address')
@@ -36,6 +36,8 @@ export interface VaultStateFields {
36
36
  name: Array<number>
37
37
  vaultLookupTable: PublicKey
38
38
  vaultFarm: PublicKey
39
+ creationTimestamp: BN
40
+ padding1: BN
39
41
  padding2: Array<BN>
40
42
  }
41
43
 
@@ -71,6 +73,8 @@ export interface VaultStateJSON {
71
73
  name: Array<number>
72
74
  vaultLookupTable: string
73
75
  vaultFarm: string
76
+ creationTimestamp: string
77
+ padding1: string
74
78
  padding2: Array<string>
75
79
  }
76
80
 
@@ -106,6 +110,8 @@ export class VaultState {
106
110
  readonly name: Array<number>
107
111
  readonly vaultLookupTable: PublicKey
108
112
  readonly vaultFarm: PublicKey
113
+ readonly creationTimestamp: BN
114
+ readonly padding1: BN
109
115
  readonly padding2: Array<BN>
110
116
 
111
117
  static readonly discriminator = Buffer.from([
@@ -144,7 +150,9 @@ export class VaultState {
144
150
  borsh.array(borsh.u8(), 40, "name"),
145
151
  borsh.publicKey("vaultLookupTable"),
146
152
  borsh.publicKey("vaultFarm"),
147
- borsh.array(borsh.u128(), 245, "padding2"),
153
+ borsh.u64("creationTimestamp"),
154
+ borsh.u64("padding1"),
155
+ borsh.array(borsh.u128(), 244, "padding2"),
148
156
  ])
149
157
 
150
158
  constructor(fields: VaultStateFields) {
@@ -181,6 +189,8 @@ export class VaultState {
181
189
  this.name = fields.name
182
190
  this.vaultLookupTable = fields.vaultLookupTable
183
191
  this.vaultFarm = fields.vaultFarm
192
+ this.creationTimestamp = fields.creationTimestamp
193
+ this.padding1 = fields.padding1
184
194
  this.padding2 = fields.padding2
185
195
  }
186
196
 
@@ -263,6 +273,8 @@ export class VaultState {
263
273
  name: dec.name,
264
274
  vaultLookupTable: dec.vaultLookupTable,
265
275
  vaultFarm: dec.vaultFarm,
276
+ creationTimestamp: dec.creationTimestamp,
277
+ padding1: dec.padding1,
266
278
  padding2: dec.padding2,
267
279
  })
268
280
  }
@@ -302,6 +314,8 @@ export class VaultState {
302
314
  name: this.name,
303
315
  vaultLookupTable: this.vaultLookupTable.toString(),
304
316
  vaultFarm: this.vaultFarm.toString(),
317
+ creationTimestamp: this.creationTimestamp.toString(),
318
+ padding1: this.padding1.toString(),
305
319
  padding2: this.padding2.map((item) => item.toString()),
306
320
  }
307
321
  }
@@ -341,6 +355,8 @@ export class VaultState {
341
355
  name: obj.name,
342
356
  vaultLookupTable: new PublicKey(obj.vaultLookupTable),
343
357
  vaultFarm: new PublicKey(obj.vaultFarm),
358
+ creationTimestamp: new BN(obj.creationTimestamp),
359
+ padding1: new BN(obj.padding1),
344
360
  padding2: obj.padding2.map((item) => new BN(item)),
345
361
  })
346
362
  }