@kamino-finance/klend-sdk 5.10.7 → 5.10.9

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.
@@ -66,7 +66,7 @@ import { TOKEN_PROGRAM_ID } from '@solana/spl-token';
66
66
  import { Data } from '@kamino-finance/kliquidity-sdk';
67
67
  import bs58 from 'bs58';
68
68
  import { getProgramAccounts } from '../utils/rpc';
69
- import { VaultConfigFieldKind } from '../idl_codegen_kamino_vault/types';
69
+ import { VaultConfigField, VaultConfigFieldKind } from '../idl_codegen_kamino_vault/types';
70
70
  import {
71
71
  AcceptVaultOwnershipIxs,
72
72
  DepositIxs,
@@ -369,9 +369,14 @@ export class KaminoManager {
369
369
  */
370
370
  async updateVaultConfigIxs(
371
371
  vault: KaminoVault,
372
- mode: VaultConfigFieldKind,
372
+ mode: VaultConfigFieldKind | string,
373
373
  value: string
374
374
  ): Promise<UpdateVaultConfigIxs> {
375
+ if (typeof mode === 'string') {
376
+ const field = VaultConfigField.fromDecoded({ [mode]: '' });
377
+ return this._vaultClient.updateVaultConfigIxs(vault, field, value);
378
+ }
379
+
375
380
  return this._vaultClient.updateVaultConfigIxs(vault, mode, value);
376
381
  }
377
382
 
@@ -465,21 +470,26 @@ export class KaminoManager {
465
470
  /**
466
471
  * This method calculates the token per share value. This will always change based on interest earned from the vault, but calculating it requires a bunch of rpc requests. Caching this for a short duration would be optimal
467
472
  * @param vault - vault to calculate tokensPerShare for
468
- * @param slot - current slot, used to estimate the interest earned in the different reserves with allocation from the vault
473
+ * @param [slot] - the slot at which we retrieve the tokens per share. Optional. If not provided, the function will fetch the current slot
474
+ * @param [vaultReservesMap] - 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
469
475
  * @returns - token per share value
470
476
  */
471
- async getTokensPerShareSingleVault(vault: KaminoVault, slot: number): Promise<Decimal> {
472
- return this._vaultClient.getTokensPerShareSingleVault(vault, slot);
477
+ async getTokensPerShareSingleVault(
478
+ vault: KaminoVault,
479
+ slot?: number,
480
+ vaultReservesMap?: PubkeyHashMap<PublicKey, KaminoReserve>
481
+ ): Promise<Decimal> {
482
+ return this._vaultClient.getTokensPerShareSingleVault(vault, slot, vaultReservesMap);
473
483
  }
474
484
 
475
485
  /**
476
486
  * This method calculates the price of one vault share(kToken)
477
487
  * @param vault - vault to calculate sharePrice for
478
- * @param slot - current slot, used to estimate the interest earned in the different reserves with allocation from the vault
479
488
  * @param tokenPrice - the price of the vault token (e.g. SOL) in USD
489
+ * @param [slot] - the slot at which we retrieve the tokens per share. Optional. If not provided, the function will fetch the current slot
480
490
  * @returns - share value in USD
481
491
  */
482
- async getSharePriceInUSD(vault: KaminoVault, slot: number, tokenPrice: Decimal): Promise<Decimal> {
492
+ async getSharePriceInUSD(vault: KaminoVault, tokenPrice: Decimal, slot?: number): Promise<Decimal> {
483
493
  const tokensPerShare = await this.getTokensPerShareSingleVault(vault, slot);
484
494
  return tokensPerShare.mul(tokenPrice);
485
495
  }
@@ -638,13 +648,13 @@ export class KaminoManager {
638
648
  /**
639
649
  * 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
640
650
  * @param vault - the kamino vault to get available liquidity to withdraw for
641
- * @param slot - current slot
642
- * @param vaultReserves - 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
651
+ * @param [slot] - the slot for which to calculate the holdings. Optional. If not provided the function will fetch the current slot
652
+ * @param [vaultReserves] - 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
643
653
  * @returns an VaultHoldings object
644
654
  */
645
655
  async getVaultHoldings(
646
656
  vault: VaultState,
647
- slot: number,
657
+ slot?: number,
648
658
  vaultReserves?: PubkeyHashMap<PublicKey, KaminoReserve>
649
659
  ): Promise<VaultHoldings> {
650
660
  return this._vaultClient.getVaultHoldings(vault, slot, vaultReserves);
@@ -653,37 +663,37 @@ export class KaminoManager {
653
663
  /**
654
664
  * This will return an VaultHoldingsWithUSDValue object which contains an holdings field representing the amount available (uninvested) in vault, total amount invested in reseves and a breakdown of the amount invested in each reserve and additional fields for the total USD value of the available and invested amounts
655
665
  * @param vault - the kamino vault to get available liquidity to withdraw for
656
- * @param slot - current slot
657
- * @param vaultReserves - 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
658
666
  * @param price - the price of the token in the vault (e.g. USDC)
667
+ * @param [slot] - the slot for which to calculate the holdings. Optional. If not provided the function will fetch the current slot
668
+ * @param [vaultReservesMap] - 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
659
669
  * @returns an VaultHoldingsWithUSDValue object with details about the tokens available and invested in the vault, denominated in tokens and USD
660
670
  */
661
671
  async getVaultHoldingsWithPrice(
662
672
  vault: VaultState,
663
- slot: number,
664
673
  price: Decimal,
674
+ slot?: number,
665
675
  vaultReserves?: PubkeyHashMap<PublicKey, KaminoReserve>
666
676
  ): Promise<VaultHoldingsWithUSDValue> {
667
- return this._vaultClient.getVaultHoldingsWithPrice(vault, slot, price, vaultReserves);
677
+ return this._vaultClient.getVaultHoldingsWithPrice(vault, price, slot, vaultReserves);
668
678
  }
669
679
 
670
680
  /**
671
681
  * 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
672
682
  * @param vault - the kamino vault to get available liquidity to withdraw for
673
- * @param slot - current slot
674
683
  * @param price - the price of the token in the vault (e.g. USDC)
675
- * @param vaultReserves - 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
676
- * @param kaminoMarkets - optional parameter; a list of all kamino markets. If provided the function will be significantly faster as it will not have to fetch the markets
677
- * @returns an VaultHoldingsWithUSDValue object with details about the tokens available and invested in the vault, denominated in tokens and USD
684
+ * @param [slot] - the slot for which to retrieve the vault overview for. Optional. If not provided the function will fetch the current slot
685
+ * @param [vaultReservesMap] - 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
686
+ * @param [kaminoMarkets] - a list of all kamino markets. Optional. If provided the function will be significantly faster as it will not have to fetch the markets
687
+ * @returns an VaultOverview object with details about the tokens available and invested in the vault, denominated in tokens and USD
678
688
  */
679
689
  async getVaultOverview(
680
690
  vault: VaultState,
681
- slot: number,
682
691
  price: Decimal,
692
+ slot?: number,
683
693
  vaultReserves?: PubkeyHashMap<PublicKey, KaminoReserve>,
684
694
  kaminoMarkets?: KaminoMarket[]
685
695
  ): Promise<VaultOverview> {
686
- return this._vaultClient.getVaultOverview(vault, slot, price, vaultReserves, kaminoMarkets);
696
+ return this._vaultClient.getVaultOverview(vault, price, slot, vaultReserves, kaminoMarkets);
687
697
  }
688
698
 
689
699
  /**
@@ -743,16 +753,23 @@ export class KaminoManager {
743
753
  /**
744
754
  * Simulate the current holdings of the vault and the earned interest
745
755
  * @param vaultState the kamino vault state to get simulated holdings and earnings for
746
- * @param vaultReserves optional; the state of the reserves in the vault allocation
756
+ * @param [vaultReservesMap] - 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
747
757
  * @param [currentSlot] - the current slot. Optional. If not provided it will fetch the current slot
758
+ * @param [previousTotalAUM] - the previous AUM of the vault to compute the earned interest relative to this value. Optional. If not provided the function will estimate the total AUM at the slot of the last state update on chain
748
759
  * @returns a struct of simulated vault holdings and earned interest
749
760
  */
750
761
  async calculateSimulatedHoldingsWithInterest(
751
762
  vaultState: VaultState,
752
763
  vaultReserves?: PubkeyHashMap<PublicKey, KaminoReserve>,
753
- currentSlot?: number
764
+ currentSlot?: number,
765
+ previousTotalAUM?: Decimal
754
766
  ): Promise<SimulatedVaultHoldingsWithEarnedInterest> {
755
- return this._vaultClient.calculateSimulatedHoldingsWithInterest(vaultState, vaultReserves, currentSlot);
767
+ return this._vaultClient.calculateSimulatedHoldingsWithInterest(
768
+ vaultState,
769
+ vaultReserves,
770
+ currentSlot,
771
+ previousTotalAUM
772
+ );
756
773
  }
757
774
 
758
775
  /**
@@ -0,0 +1,19 @@
1
+ import { pubkeyHashMapToJson } from './utils';
2
+ import { VaultHoldings } from './vault';
3
+
4
+ export function holdingsToJson(holdings: VaultHoldings) {
5
+ return {
6
+ available: holdings.available.toString(),
7
+ invested: holdings.invested.toString(),
8
+ total: holdings.total.toString(),
9
+ investedInReserves: pubkeyHashMapToJson(holdings.investedInReserves),
10
+ };
11
+ }
12
+
13
+ export function printHoldings(holdings: VaultHoldings) {
14
+ console.log('Holdings:');
15
+ console.log(' Available:', holdings.available.toString());
16
+ console.log(' Invested:', holdings.invested.toString());
17
+ console.log(' Total:', holdings.total.toString());
18
+ console.log(' Invested in reserves:', pubkeyHashMapToJson(holdings.investedInReserves));
19
+ }
@@ -1,4 +1,4 @@
1
- import { SLOTS_PER_SECOND, SLOTS_PER_YEAR } from '../utils';
1
+ import { PubkeyHashMap, SLOTS_PER_SECOND, SLOTS_PER_YEAR } from '../utils';
2
2
  import Decimal from 'decimal.js';
3
3
  import { AccountInfo, PublicKey } from '@solana/web3.js';
4
4
  import { SOL_MINTS } from '../lib';
@@ -250,3 +250,24 @@ export function truncateDecimals(num: Decimal.Value, maxDecimals: number): Decim
250
250
  const factor = new Decimal(10).pow(maxDecimals);
251
251
  return new Decimal(num).times(factor).floor().dividedBy(factor);
252
252
  }
253
+
254
+ /**Convert an u8 array to a string */
255
+ export function decodeVaultName(token: number[]): string {
256
+ const maxArray = new Uint8Array(token);
257
+ let s: string = new TextDecoder().decode(maxArray);
258
+ // Remove trailing zeros and spaces
259
+ s = s.replace(/[\0 ]+$/, '');
260
+ return s;
261
+ }
262
+
263
+ export function pubkeyHashMapToJson(map: PubkeyHashMap<PublicKey, any>): { [key: string]: string } {
264
+ const obj: { [key: string]: any } = {};
265
+ map.forEach((value, key) => {
266
+ obj[key.toBase58()] = value.toString();
267
+ });
268
+ return obj;
269
+ }
270
+
271
+ export function printPubkeyHashMap<V>(map: PubkeyHashMap<PublicKey, V>) {
272
+ console.log(pubkeyHashMapToJson(map));
273
+ }
@@ -53,7 +53,13 @@ import {
53
53
  import { VaultConfigField, VaultConfigFieldKind } from '../idl_codegen_kamino_vault/types';
54
54
  import { VaultState } from '../idl_codegen_kamino_vault/accounts';
55
55
  import Decimal from 'decimal.js';
56
- import { bpsToPct, getTokenBalanceFromAccountInfoLamports, numberToLamportsDecimal, parseTokenSymbol } from './utils';
56
+ import {
57
+ bpsToPct,
58
+ decodeVaultName,
59
+ getTokenBalanceFromAccountInfoLamports,
60
+ numberToLamportsDecimal,
61
+ parseTokenSymbol,
62
+ } from './utils';
57
63
  import { deposit } from '../idl_codegen_kamino_vault/instructions';
58
64
  import { withdraw } from '../idl_codegen_kamino_vault/instructions';
59
65
  import { PROGRAM_ID } from '../idl_codegen/programId';
@@ -83,6 +89,7 @@ import {
83
89
  getSharesInFarmUserPosition,
84
90
  getUserSharesInFarm,
85
91
  } from './farm_utils';
92
+ import { printHoldings } from './types_utils';
86
93
 
87
94
  export const kaminoVaultId = new PublicKey('kvauTFR8qm1dhniz6pYuBZkuene3Hfrs1VQhVRgCNrr');
88
95
  export const kaminoVaultStagingId = new PublicKey('STkvh7ostar39Fwr4uZKASs1RNNuYMFMTsE77FiRsL2');
@@ -125,6 +132,36 @@ export class KaminoVaultClient {
125
132
  return;
126
133
  }
127
134
 
135
+ /**
136
+ * Prints a vault in a human readable form
137
+ * @param vaultPubkey - the address of the vault
138
+ * @param [vaultState] - optional parameter to pass the vault state directly; this will save a network call
139
+ * @returns - void; prints the vault to the console
140
+ */
141
+ async printVault(vaultPubkey: PublicKey, vaultState?: VaultState) {
142
+ const vault = vaultState ? vaultState : await VaultState.fetch(this.getConnection(), vaultPubkey);
143
+
144
+ if (!vault) {
145
+ console.log(`Vault ${vaultPubkey.toString()} not found`);
146
+ return;
147
+ }
148
+
149
+ const kaminoVault = new KaminoVault(vaultPubkey, vault, this._kaminoVaultProgramId);
150
+ const vaultName = this.decodeVaultName(vault.name);
151
+ const slot = await this.getConnection().getSlot('confirmed');
152
+ const tokensPerShare = await this.getTokensPerShareSingleVault(kaminoVault, slot);
153
+ const holdings = await this.getVaultHoldings(vault);
154
+
155
+ const sharesIssued = new Decimal(vault.sharesIssued.toString()!).div(
156
+ new Decimal(vault.sharesMintDecimals.toString())
157
+ );
158
+
159
+ console.log('Name: ', vaultName);
160
+ console.log('Shares issued: ', sharesIssued);
161
+ printHoldings(holdings);
162
+ console.log('Tokens per share: ', tokensPerShare);
163
+ }
164
+
128
165
  /**
129
166
  * This method will create a vault with a given config. The config can be changed later on, but it is recommended to set it up correctly from the start
130
167
  * @param vaultConfig - the config object used to create a vault
@@ -1058,11 +1095,7 @@ export class KaminoVaultClient {
1058
1095
 
1059
1096
  /**Convert an u8 array to a string */
1060
1097
  decodeVaultName(token: number[]): string {
1061
- const maxArray = new Uint8Array(token);
1062
- let s: string = new TextDecoder().decode(maxArray);
1063
- // Remove trailing zeros and spaces
1064
- s = s.replace(/[\0 ]+$/, '');
1065
- return s;
1098
+ return decodeVaultName(token);
1066
1099
  }
1067
1100
 
1068
1101
  private withdrawIxn(
@@ -1491,15 +1524,15 @@ export class KaminoVaultClient {
1491
1524
  }
1492
1525
 
1493
1526
  /**
1494
- * This method calculates the token per shar value. This will always change based on interest earned from the vault, but calculating it requires a bunch of rpc requests. Caching this for a short duration would be optimal
1527
+ * This method calculates the token per share value. This will always change based on interest earned from the vault, but calculating it requires a bunch of rpc requests. Caching this for a short duration would be optimal
1495
1528
  * @param vault - vault to calculate tokensPerShare for
1496
- * @param slot - current slot, used to estimate the interest earned in the different reserves with allocation from the vault
1529
+ * @param [slot] - the slot at which we retrieve the tokens per share. Optional. If not provided, the function will fetch the current slot
1497
1530
  * @param [vaultReservesMap] - 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
1498
1531
  * @returns - token per share value
1499
1532
  */
1500
1533
  async getTokensPerShareSingleVault(
1501
1534
  vault: KaminoVault,
1502
- slot: number,
1535
+ slot?: number,
1503
1536
  vaultReservesMap?: PubkeyHashMap<PublicKey, KaminoReserve>
1504
1537
  ): Promise<Decimal> {
1505
1538
  const vaultState = await vault.getState(this.getConnection());
@@ -1514,11 +1547,7 @@ export class KaminoVaultClient {
1514
1547
  vaultState.sharesMintDecimals.toString()
1515
1548
  );
1516
1549
 
1517
- const holdings = await this.getVaultHoldings(
1518
- vaultState,
1519
- await this.getConnection().getSlot('confirmed'),
1520
- vaultReservesState
1521
- );
1550
+ const holdings = await this.getVaultHoldings(vaultState, slot, vaultReservesState);
1522
1551
 
1523
1552
  return holdings.total.div(sharesDecimal);
1524
1553
  }
@@ -1759,7 +1788,7 @@ export class KaminoVaultClient {
1759
1788
  /**
1760
1789
  * This will retrieve all the tokens that can be used as collateral by the users who borrow the token in the vault alongside details about the min and max loan to value ratio
1761
1790
  * @param vaultState - the vault state to load reserves for
1762
- * @param slot - current slot
1791
+ * @param [slot] - the slot for which to retrieve the vault collaterals for. Optional. If not provided the function will fetch the current slot
1763
1792
  * @param [vaultReservesMap] - 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
1764
1793
  * @param [kaminoMarkets] - a list of all the kamino markets. Optional. If provided the function will be significantly faster as it will not have to fetch the markets
1765
1794
  * @returns a hashmap from each reserve pubkey to the market overview of the collaterals that can be used and the min and max loan to value ratio in that market
@@ -1831,13 +1860,13 @@ export class KaminoVaultClient {
1831
1860
  /**
1832
1861
  * 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
1833
1862
  * @param vault - the kamino vault to get available liquidity to withdraw for
1834
- * @param slot - current slot
1835
- * @param vaultReserves - 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
1863
+ * @param [slot] - the slot for which to calculate the holdings. Optional. If not provided the function will fetch the current slot
1864
+ * @param [vaultReserves] - 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
1836
1865
  * @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
1837
1866
  */
1838
1867
  async getVaultHoldings(
1839
1868
  vault: VaultState,
1840
- slot: number,
1869
+ slot?: number,
1841
1870
  vaultReserves?: PubkeyHashMap<PublicKey, KaminoReserve>
1842
1871
  ): Promise<VaultHoldings> {
1843
1872
  const vaultHoldings: VaultHoldings = {
@@ -1860,7 +1889,12 @@ export class KaminoVaultClient {
1860
1889
  throw new Error(`Reserve ${allocationStrategy.reserve.toBase58()} not found`);
1861
1890
  }
1862
1891
 
1863
- const reserveCollExchangeRate = reserve.getEstimatedCollateralExchangeRate(slot, 0);
1892
+ let reserveCollExchangeRate: Decimal;
1893
+ if (slot) {
1894
+ reserveCollExchangeRate = reserve.getEstimatedCollateralExchangeRate(slot, 0);
1895
+ } else {
1896
+ reserveCollExchangeRate = reserve.getCollateralExchangeRate();
1897
+ }
1864
1898
  const reserveAllocationLiquidityAmount = new Decimal(allocationStrategy.ctokenAllocation.toString()).div(
1865
1899
  reserveCollExchangeRate
1866
1900
  );
@@ -1883,17 +1917,18 @@ export class KaminoVaultClient {
1883
1917
  }
1884
1918
 
1885
1919
  /**
1886
- * This will return an VaultHoldingsWithUSDValue object which contains an holdings field representing the amount available (uninvested) in vault, total amount invested in reseves and a breakdown of the amount invested in each reserve and additional fields for the total USD value of the available and invested amounts
1920
+ * 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
1887
1921
  * @param vault - the kamino vault to get available liquidity to withdraw for
1888
- * @param slot - current slot
1889
1922
  * @param price - the price of the token in the vault (e.g. USDC)
1923
+ * @param [slot] - the slot for which to retrieve the vault overview for. Optional. If not provided the function will fetch the current slot
1890
1924
  * @param [vaultReservesMap] - 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
1891
- * @returns an VaultHoldingsWithUSDValue object with details about the tokens available and invested in the vault, denominated in tokens and USD
1925
+ * @param [kaminoMarkets] - a list of all kamino markets. Optional. If provided the function will be significantly faster as it will not have to fetch the markets
1926
+ * @returns an VaultOverview object with details about the tokens available and invested in the vault, denominated in tokens and USD
1892
1927
  */
1893
1928
  async getVaultHoldingsWithPrice(
1894
1929
  vault: VaultState,
1895
- slot: number,
1896
1930
  price: Decimal,
1931
+ slot?: number,
1897
1932
  vaultReservesMap?: PubkeyHashMap<PublicKey, KaminoReserve>
1898
1933
  ): Promise<VaultHoldingsWithUSDValue> {
1899
1934
  const holdings = await this.getVaultHoldings(vault, slot, vaultReservesMap);
@@ -1914,16 +1949,16 @@ export class KaminoVaultClient {
1914
1949
  /**
1915
1950
  * 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
1916
1951
  * @param vault - the kamino vault to get available liquidity to withdraw for
1917
- * @param slot - current slot
1918
1952
  * @param price - the price of the token in the vault (e.g. USDC)
1953
+ * @param [slot] - the slot for which to retrieve the vault overview for. Optional. If not provided the function will fetch the current slot
1919
1954
  * @param [vaultReservesMap] - 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
1920
1955
  * @param [kaminoMarkets] - a list of all kamino markets. Optional. If provided the function will be significantly faster as it will not have to fetch the markets
1921
1956
  * @returns an VaultOverview object with details about the tokens available and invested in the vault, denominated in tokens and USD
1922
1957
  */
1923
1958
  async getVaultOverview(
1924
1959
  vault: VaultState,
1925
- slot: number,
1926
1960
  price: Decimal,
1961
+ slot?: number,
1927
1962
  vaultReservesMap?: PubkeyHashMap<PublicKey, KaminoReserve>,
1928
1963
  kaminoMarkets?: KaminoMarket[]
1929
1964
  ): Promise<VaultOverview> {
@@ -1931,14 +1966,26 @@ export class KaminoVaultClient {
1931
1966
 
1932
1967
  const vaultHoldingsWithUSDValuePromise = await this.getVaultHoldingsWithPrice(
1933
1968
  vault,
1934
- slot,
1935
1969
  price,
1970
+ slot,
1936
1971
  vaultReservesState
1937
1972
  );
1938
- const vaultTheoreticalAPYPromise = await this.getVaultTheoreticalAPY(vault, slot, vaultReservesState);
1939
- const totalInvestedAndBorrowedPromise = await this.getTotalBorrowedAndInvested(vault, slot, vaultReservesState);
1940
- const vaultCollateralsPromise = await this.getVaultCollaterals(vault, slot, vaultReservesState, kaminoMarkets);
1941
- const reservesOverviewPromise = await this.getVaultReservesDetails(vault, slot, vaultReservesState);
1973
+
1974
+ const currentSlot = slot ? slot : await this.getConnection().getSlot();
1975
+
1976
+ const vaultTheoreticalAPYPromise = await this.getVaultTheoreticalAPY(vault, currentSlot, vaultReservesState);
1977
+ const totalInvestedAndBorrowedPromise = await this.getTotalBorrowedAndInvested(
1978
+ vault,
1979
+ currentSlot,
1980
+ vaultReservesState
1981
+ );
1982
+ const vaultCollateralsPromise = await this.getVaultCollaterals(
1983
+ vault,
1984
+ currentSlot,
1985
+ vaultReservesState,
1986
+ kaminoMarkets
1987
+ );
1988
+ const reservesOverviewPromise = await this.getVaultReservesDetails(vault, currentSlot, vaultReservesState);
1942
1989
 
1943
1990
  // 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
1944
1991
  const [
@@ -2115,26 +2162,31 @@ export class KaminoVaultClient {
2115
2162
  * @param vaultState the kamino vault state to get simulated holdings and earnings for
2116
2163
  * @param [vaultReservesMap] - 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
2117
2164
  * @param [currentSlot] - the current slot. Optional. If not provided it will fetch the current slot
2165
+ * @param [previousTotalAUM] - the previous AUM of the vault to compute the earned interest relative to this value. Optional. If not provided the function will estimate the total AUM at the slot of the last state update on chain
2118
2166
  * @returns a struct of simulated vault holdings and earned interest
2119
2167
  */
2120
2168
  async calculateSimulatedHoldingsWithInterest(
2121
2169
  vaultState: VaultState,
2122
2170
  vaultReservesMap?: PubkeyHashMap<PublicKey, KaminoReserve>,
2123
- currentSlot?: number
2171
+ currentSlot?: number,
2172
+ previousTotalAUM?: Decimal
2124
2173
  ): Promise<SimulatedVaultHoldingsWithEarnedInterest> {
2125
- const latestUpdateTs = vaultState.lastFeeChargeTimestamp.toNumber();
2126
- const lastUpdateSlot = latestUpdateTs / this.recentSlotDurationMs;
2174
+ let prevAUM: Decimal;
2127
2175
 
2128
- const slot = currentSlot ? currentSlot : await this.getConnection().getSlot('confirmed');
2176
+ if (previousTotalAUM) {
2177
+ prevAUM = previousTotalAUM;
2178
+ } else {
2179
+ const latestUpdateTs = vaultState.lastFeeChargeTimestamp.toNumber();
2180
+ const lastUpdateSlot = latestUpdateTs / this.recentSlotDurationMs;
2129
2181
 
2130
- const lastUpdateHoldingsPromise = this.getVaultHoldings(vaultState, lastUpdateSlot, vaultReservesMap);
2131
- const currentHoldingsPromise = this.getVaultHoldings(vaultState, slot, vaultReservesMap);
2132
- const [lastUpdateHoldings, currentHoldings] = await Promise.all([
2133
- lastUpdateHoldingsPromise,
2134
- currentHoldingsPromise,
2135
- ]);
2182
+ const lastUpdateHoldings = await this.getVaultHoldings(vaultState, lastUpdateSlot, vaultReservesMap);
2183
+ prevAUM = lastUpdateHoldings.total;
2184
+ }
2185
+
2186
+ const slot = currentSlot ? currentSlot : await this.getConnection().getSlot('confirmed');
2136
2187
 
2137
- const earnedInterest = currentHoldings.total.sub(lastUpdateHoldings.total);
2188
+ const currentHoldings = await this.getVaultHoldings(vaultState, slot, vaultReservesMap);
2189
+ const earnedInterest = currentHoldings.total.sub(prevAUM);
2138
2190
 
2139
2191
  return {
2140
2192
  holdings: currentHoldings,
@@ -2170,7 +2222,10 @@ export class KaminoVaultClient {
2170
2222
  .mul(new Decimal(vaultState.managementFeeBps.toString()))
2171
2223
  .div(new Decimal(SECONDS_PER_YEAR))
2172
2224
  .div(FullBPSDecimal);
2173
- const prevAUM = new Fraction(vaultState.prevAumSf).toDecimal();
2225
+ const prevAUM = lamportsToDecimal(
2226
+ new Fraction(vaultState.prevAumSf).toDecimal(),
2227
+ vaultState.tokenMintDecimals.toNumber()
2228
+ );
2174
2229
  const mgmtFee = prevAUM.mul(managementFeeFactor);
2175
2230
 
2176
2231
  return {
@@ -53,6 +53,7 @@ import {
53
53
  PerformanceFeeBps,
54
54
  } from './idl_codegen_kamino_vault/types/VaultConfigField';
55
55
  import { getAccountOwner } from './utils/rpc';
56
+ import { printHoldings } from './classes/types_utils';
56
57
 
57
58
  dotenv.config({
58
59
  path: `.env${process.env.ENV ? '.' + process.env.ENV : ''}`,
@@ -293,6 +294,42 @@ async function main() {
293
294
  mode === 'execute' && console.log('Pending admin updated:', updateVaultPendingAdminSig);
294
295
  });
295
296
 
297
+ commands
298
+ .command('update-vault-config')
299
+ .requiredOption('--vault <string>', 'Vault address')
300
+ .requiredOption('--field <string>', 'The field to update')
301
+ .requiredOption('--value <string>', 'The value to update the field to')
302
+ .requiredOption(
303
+ `--mode <string>`,
304
+ 'simulate - to print txn simulation, inspect - to get txn simulation in explorer, execute - execute txn, multisig - to get bs58 txn for multisig usage'
305
+ )
306
+ .option(`--staging`, 'If true, will use the staging programs')
307
+ .option(`--multisig <string>`, 'If using multisig mode this is required, otherwise will be ignored')
308
+ .action(async ({ vault, field, value, mode, staging, multisig }) => {
309
+ const env = initializeClient(mode === 'multisig', staging);
310
+ const vaultAddress = new PublicKey(vault);
311
+
312
+ if (mode === 'multisig' && !multisig) {
313
+ throw new Error('If using multisig mode, multisig is required');
314
+ }
315
+
316
+ const kaminoManager = new KaminoManager(env.connection, env.kLendProgramId, env.kVaultProgramId);
317
+
318
+ const kaminoVault = new KaminoVault(vaultAddress, undefined, env.kVaultProgramId);
319
+ const instructions = await kaminoManager.updateVaultConfigIxs(kaminoVault, field, value);
320
+
321
+ const updateVaultPendingAdminSig = await processTxn(
322
+ env.client,
323
+ env.payer,
324
+ [instructions.updateVaultConfigIx, ...instructions.updateLUTIxs],
325
+ mode,
326
+ 2500,
327
+ []
328
+ );
329
+
330
+ mode === 'execute' && console.log('Pending admin updated:', updateVaultPendingAdminSig);
331
+ });
332
+
296
333
  commands
297
334
  .command('update-vault-mgmt-fee')
298
335
  .requiredOption('--vault <string>', 'Vault address')
@@ -796,8 +833,8 @@ async function main() {
796
833
  const vaultState = await new KaminoVault(vaultAddress, undefined, env.kVaultProgramId).getState(env.connection);
797
834
  const vaultOverview = await kaminoManager.getVaultOverview(
798
835
  vaultState,
799
- await env.connection.getSlot('confirmed'),
800
- new Decimal(1.0)
836
+ new Decimal(1.0),
837
+ await env.connection.getSlot('confirmed')
801
838
  );
802
839
 
803
840
  console.log('vaultOverview', vaultOverview);
@@ -890,8 +927,11 @@ async function main() {
890
927
  const sharesIssued = new Decimal(vaultState.sharesIssued.toString()!).div(
891
928
  new Decimal(vaultState.sharesMintDecimals.toString())
892
929
  );
930
+
931
+ console.log('farm', vaultState.vaultFarm.toString());
932
+ console.log('Name: ', kaminoManager.getDecodedVaultName(kaminoVault.state!));
893
933
  console.log('Shares issued: ', sharesIssued);
894
- console.log('Holdings: ', holdings);
934
+ printHoldings(holdings);
895
935
  console.log(`Tokens per share for vault ${vaultAddress.toBase58()}: ${tokensPerShare}`);
896
936
  });
897
937
 
@@ -937,7 +977,6 @@ async function main() {
937
977
  const simulatedHoldings = await kaminoManager.calculateSimulatedHoldingsWithInterest(vaultState);
938
978
 
939
979
  console.log('Simulated holdings with interest', simulatedHoldings);
940
-
941
980
  const simulatedFees = await kaminoManager.calculateSimulatedFees(vaultState, simulatedHoldings);
942
981
 
943
982
  console.log('Simulated fees', simulatedFees);