@kamino-finance/klend-sdk 5.10.6 → 5.10.8

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 (80) hide show
  1. package/dist/classes/action.d.ts +7 -3
  2. package/dist/classes/action.d.ts.map +1 -1
  3. package/dist/classes/action.js +28 -13
  4. package/dist/classes/action.js.map +1 -1
  5. package/dist/classes/manager.d.ts +20 -18
  6. package/dist/classes/manager.d.ts.map +1 -1
  7. package/dist/classes/manager.js +27 -20
  8. package/dist/classes/manager.js.map +1 -1
  9. package/dist/classes/market.d.ts +11 -0
  10. package/dist/classes/market.d.ts.map +1 -1
  11. package/dist/classes/market.js +26 -0
  12. package/dist/classes/market.js.map +1 -1
  13. package/dist/classes/obligation.d.ts +14 -0
  14. package/dist/classes/obligation.d.ts.map +1 -1
  15. package/dist/classes/obligation.js +25 -0
  16. package/dist/classes/obligation.js.map +1 -1
  17. package/dist/classes/types_utils.d.ts +11 -0
  18. package/dist/classes/types_utils.d.ts.map +1 -0
  19. package/dist/classes/types_utils.js +21 -0
  20. package/dist/classes/types_utils.js.map +1 -0
  21. package/dist/classes/utils.d.ts +7 -0
  22. package/dist/classes/utils.d.ts.map +1 -1
  23. package/dist/classes/utils.js +21 -0
  24. package/dist/classes/utils.js.map +1 -1
  25. package/dist/classes/vault.d.ts +23 -14
  26. package/dist/classes/vault.d.ts.map +1 -1
  27. package/dist/classes/vault.js +65 -33
  28. package/dist/classes/vault.js.map +1 -1
  29. package/dist/client_kamino_manager.d.ts.map +1 -1
  30. package/dist/client_kamino_manager.js +25 -2
  31. package/dist/client_kamino_manager.js.map +1 -1
  32. package/dist/lending_operations/index.d.ts +1 -0
  33. package/dist/lending_operations/index.d.ts.map +1 -1
  34. package/dist/lending_operations/index.js +1 -0
  35. package/dist/lending_operations/index.js.map +1 -1
  36. package/dist/lending_operations/repay_with_collateral_operations.d.ts +5 -5
  37. package/dist/lending_operations/repay_with_collateral_operations.d.ts.map +1 -1
  38. package/dist/lending_operations/repay_with_collateral_operations.js +3 -2
  39. package/dist/lending_operations/repay_with_collateral_operations.js.map +1 -1
  40. package/dist/lending_operations/swap_collateral_operations.d.ts +102 -0
  41. package/dist/lending_operations/swap_collateral_operations.d.ts.map +1 -0
  42. package/dist/lending_operations/swap_collateral_operations.js +306 -0
  43. package/dist/lending_operations/swap_collateral_operations.js.map +1 -0
  44. package/dist/leverage/operations.d.ts +2 -2
  45. package/dist/leverage/operations.d.ts.map +1 -1
  46. package/dist/leverage/operations.js +4 -0
  47. package/dist/leverage/operations.js.map +1 -1
  48. package/dist/leverage/types.d.ts +5 -5
  49. package/dist/leverage/types.d.ts.map +1 -1
  50. package/dist/leverage/utils.d.ts +5 -5
  51. package/dist/leverage/utils.d.ts.map +1 -1
  52. package/dist/leverage/utils.js.map +1 -1
  53. package/dist/utils/constants.d.ts +1 -0
  54. package/dist/utils/constants.d.ts.map +1 -1
  55. package/dist/utils/constants.js +2 -1
  56. package/dist/utils/constants.js.map +1 -1
  57. package/dist/utils/pubkey.d.ts +3 -0
  58. package/dist/utils/pubkey.d.ts.map +1 -1
  59. package/dist/utils/pubkey.js +16 -2
  60. package/dist/utils/pubkey.js.map +1 -1
  61. package/dist/utils/seeds.d.ts +1 -1
  62. package/dist/utils/seeds.js +1 -1
  63. package/package.json +4 -4
  64. package/src/classes/action.ts +37 -19
  65. package/src/classes/manager.ts +40 -23
  66. package/src/classes/market.ts +35 -1
  67. package/src/classes/obligation.ts +75 -0
  68. package/src/classes/types_utils.ts +19 -0
  69. package/src/classes/utils.ts +22 -1
  70. package/src/classes/vault.ts +93 -41
  71. package/src/client_kamino_manager.ts +43 -4
  72. package/src/lending_operations/index.ts +1 -0
  73. package/src/lending_operations/repay_with_collateral_operations.ts +10 -9
  74. package/src/lending_operations/swap_collateral_operations.ts +586 -0
  75. package/src/leverage/operations.ts +14 -10
  76. package/src/leverage/types.ts +6 -6
  77. package/src/leverage/utils.ts +8 -8
  78. package/src/utils/constants.ts +2 -0
  79. package/src/utils/pubkey.ts +19 -2
  80. package/src/utils/seeds.ts +1 -1
@@ -411,7 +411,7 @@ export class KaminoAction {
411
411
  obligation: KaminoObligation | ObligationType,
412
412
  extraComputeBudget: number = 1_000_000, // if > 0 then adds the ixn
413
413
  includeAtaIxns: boolean = true, // if true it includes create and close wsol and token atas,
414
- requestElevationGroup: boolean = false,
414
+ requestElevationGroup: boolean = false, // to be requested *before* the deposit
415
415
  includeUserMetadata: boolean = true, // if true it includes user metadata
416
416
  referrer: PublicKey = PublicKey.default,
417
417
  currentSlot: number = 0,
@@ -855,11 +855,17 @@ export class KaminoAction {
855
855
  obligation: KaminoObligation | ObligationType,
856
856
  extraComputeBudget: number = 1_000_000, // if > 0 then adds the ixn
857
857
  includeAtaIxns: boolean = true, // if true it includes create and close wsol and token atas,
858
- requestElevationGroup: boolean = false,
858
+ requestElevationGroup: boolean = false, // to be requested *after* the withdraw
859
859
  includeUserMetadata: boolean = true, // if true it includes user metadata
860
860
  referrer: PublicKey = PublicKey.default,
861
861
  currentSlot: number = 0,
862
- scopeRefresh: ScopeRefresh = { includeScopeRefresh: false, scopeFeed: 'hubble' }
862
+ scopeRefresh: ScopeRefresh | undefined = undefined,
863
+ overrideElevationGroupRequest?: number,
864
+ // Optional customizations which may be needed if the obligation was mutated by some previous ixn.
865
+ obligationCustomizations?: {
866
+ // Any newly-added deposit reserves.
867
+ addedDepositReserves?: PublicKey[];
868
+ }
863
869
  ) {
864
870
  const axn = await KaminoAction.initialize(
865
871
  'withdraw',
@@ -877,6 +883,8 @@ export class KaminoAction {
877
883
  axn.addComputeBudgetIxn(extraComputeBudget);
878
884
  }
879
885
 
886
+ axn.depositReserves.push(...(obligationCustomizations?.addedDepositReserves || []));
887
+
880
888
  const allReserves = new PublicKeySet<PublicKey>([
881
889
  ...axn.depositReserves,
882
890
  ...axn.borrowReserves,
@@ -884,7 +892,7 @@ export class KaminoAction {
884
892
  ]).toArray();
885
893
  const tokenIds = axn.getTokenIdsForScopeRefresh(kaminoMarket, allReserves);
886
894
 
887
- if (tokenIds.length > 0 && scopeRefresh.includeScopeRefresh) {
895
+ if (tokenIds.length > 0 && scopeRefresh && scopeRefresh.includeScopeRefresh) {
888
896
  await axn.addScopeRefreshIxs(tokenIds, scopeRefresh.scopeFeed);
889
897
  }
890
898
 
@@ -893,7 +901,9 @@ export class KaminoAction {
893
901
  includeAtaIxns,
894
902
  requestElevationGroup,
895
903
  includeUserMetadata,
896
- addInitObligationForFarm
904
+ addInitObligationForFarm,
905
+ false,
906
+ overrideElevationGroupRequest
897
907
  );
898
908
  await axn.addWithdrawIx();
899
909
  axn.addRefreshFarmsCleanupTxnIxsToCleanupIxs();
@@ -1827,6 +1837,16 @@ export class KaminoAction {
1827
1837
  this.addRefreshReserveIxs(allReservesExcludingCurrent, addAsSupportIx);
1828
1838
  this.addRefreshReserveIxs(currentReserveAddresses.toArray(), addAsSupportIx);
1829
1839
  this.addRefreshObligationIx(addAsSupportIx);
1840
+ } else if (
1841
+ action === 'withdraw' &&
1842
+ overrideElevationGroupRequest !== undefined
1843
+ // Note: contrary to the 'deposit' case above, we allow requesting the same group as in the [stale, cached] obligation state, since our current use-case is "deposit X, withdraw Y"
1844
+ ) {
1845
+ console.log('Withdraw: Requesting elevation group', overrideElevationGroupRequest);
1846
+ // Skip the withdrawn reserve if we are in the process of closing it:
1847
+ const skipReserveIfClosing = this.amount.eq(new BN(U64_MAX)) ? [this.reserve.address] : [];
1848
+ this.addRefreshObligationIx('cleanup', skipReserveIfClosing);
1849
+ this.addRequestElevationIx(overrideElevationGroupRequest, 'cleanup', skipReserveIfClosing);
1830
1850
  }
1831
1851
  }
1832
1852
 
@@ -1996,7 +2016,7 @@ export class KaminoAction {
1996
2016
  });
1997
2017
  }
1998
2018
 
1999
- private addRefreshObligationIx(addAsSupportIx: AuxiliaryIx = 'setup', borrowReservesToSkip: PublicKey[] = []) {
2019
+ private addRefreshObligationIx(addAsSupportIx: AuxiliaryIx = 'setup', skipReserves: PublicKey[] = []) {
2000
2020
  const marketAddress = this.kaminoMarket.getAddress();
2001
2021
  const obligationPda = this.getObligationPda();
2002
2022
  const refreshObligationIx = refreshObligation(
@@ -2007,15 +2027,16 @@ export class KaminoAction {
2007
2027
  this.kaminoMarket.programId
2008
2028
  );
2009
2029
 
2010
- const depositReservesList = this.getAdditionalDepositReservesList();
2030
+ const skipReservesSet = new PublicKeySet(skipReserves);
2011
2031
 
2032
+ const depositReservesList = this.getAdditionalDepositReservesList().filter(
2033
+ (reserve) => !skipReservesSet.contains(reserve)
2034
+ );
2012
2035
  const depositReserveAccountMetas = depositReservesList.map((reserve) => {
2013
2036
  return { pubkey: reserve, isSigner: false, isWritable: true };
2014
2037
  });
2015
2038
 
2016
- const skipBorrowsSet = new PublicKeySet(borrowReservesToSkip);
2017
- const borrowReservesList = this.borrowReserves.filter((reserve) => !skipBorrowsSet.contains(reserve));
2018
-
2039
+ const borrowReservesList = this.borrowReserves.filter((reserve) => !skipReservesSet.contains(reserve));
2019
2040
  const borrowReserveAccountMetas = borrowReservesList.map((reserve) => {
2020
2041
  return { pubkey: reserve, isSigner: false, isWritable: true };
2021
2042
  });
@@ -2048,11 +2069,7 @@ export class KaminoAction {
2048
2069
  }
2049
2070
  }
2050
2071
 
2051
- private addRequestElevationIx(
2052
- elevationGroup: number,
2053
- addAsSupportIx: AuxiliaryIx,
2054
- skipBorrowReserves: PublicKey[] = []
2055
- ) {
2072
+ private addRequestElevationIx(elevationGroup: number, addAsSupportIx: AuxiliaryIx, skipReserves: PublicKey[] = []) {
2056
2073
  const obligationPda = this.getObligationPda();
2057
2074
  const args: RequestElevationGroupArgs = {
2058
2075
  elevationGroup,
@@ -2065,15 +2082,16 @@ export class KaminoAction {
2065
2082
 
2066
2083
  const requestElevationGroupIx = requestElevationGroup(args, accounts, this.kaminoMarket.programId);
2067
2084
 
2068
- const depositReservesList = this.getAdditionalDepositReservesList();
2085
+ const skipReservesSet = new PublicKeySet<PublicKey>(skipReserves);
2069
2086
 
2087
+ const depositReservesList = this.getAdditionalDepositReservesList().filter(
2088
+ (reserve) => !skipReservesSet.contains(reserve)
2089
+ );
2070
2090
  const depositReserveAccountMetas = depositReservesList.map((reserve) => {
2071
2091
  return { pubkey: reserve, isSigner: false, isWritable: true };
2072
2092
  });
2073
2093
 
2074
- const skipBorrowReservesSet = new PublicKeySet<PublicKey>(skipBorrowReserves);
2075
- const borrowReservesList = this.borrowReserves.filter((reserve) => !skipBorrowReservesSet.contains(reserve));
2076
-
2094
+ const borrowReservesList = this.borrowReserves.filter((reserve) => !skipReservesSet.contains(reserve));
2077
2095
  const borrowReserveAccountMetas = borrowReservesList.map((reserve) => {
2078
2096
  return { pubkey: reserve, isSigner: false, isWritable: true };
2079
2097
  });
@@ -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
  /**
@@ -169,6 +169,26 @@ export class KaminoMarket {
169
169
  return this.state.elevationGroups[elevationGroup - 1];
170
170
  }
171
171
 
172
+ /**
173
+ * Returns this market's elevation group of the given ID, or `null` for the default group `0`, or throws an error
174
+ * (including the given description) if the requested group does not exist.
175
+ */
176
+ getExistingElevationGroup(
177
+ elevationGroupId: number,
178
+ description: string = 'Requested'
179
+ ): ElevationGroupDescription | null {
180
+ if (elevationGroupId === 0) {
181
+ return null;
182
+ }
183
+ const elevationGroup = this.getMarketElevationGroupDescriptions().find(
184
+ (candidate) => candidate.elevationGroup === elevationGroupId
185
+ );
186
+ if (elevationGroup === undefined) {
187
+ throw new Error(`${description} elevation group ${elevationGroupId} not found in market ${this.getAddress()}`);
188
+ }
189
+ return elevationGroup;
190
+ }
191
+
172
192
  getMinNetValueObligation(): Decimal {
173
193
  return new Fraction(this.state.minNetValueInObligationSf).toDecimal();
174
194
  }
@@ -399,7 +419,7 @@ export class KaminoMarket {
399
419
  return this.reserves.get(address);
400
420
  }
401
421
 
402
- getReserveByMint(address: PublicKey) {
422
+ getReserveByMint(address: PublicKey): KaminoReserve | undefined {
403
423
  for (const reserve of this.reserves.values()) {
404
424
  if (reserve.getLiquidityMint().equals(address)) {
405
425
  return reserve;
@@ -408,6 +428,18 @@ export class KaminoMarket {
408
428
  return undefined;
409
429
  }
410
430
 
431
+ /**
432
+ * Returns this market's reserve of the given mint address, or throws an error (including the given description) if
433
+ * such reserve does not exist.
434
+ */
435
+ getExistingReserveByMint(address: PublicKey, description: string = 'Requested'): KaminoReserve {
436
+ const reserve = this.getReserveByMint(address);
437
+ if (!reserve) {
438
+ throw new Error(`${description} reserve with mint ${address} not found in market ${this.getAddress()}`);
439
+ }
440
+ return reserve;
441
+ }
442
+
411
443
  getReserveBySymbol(symbol: string) {
412
444
  for (const reserve of this.reserves.values()) {
413
445
  if (reserve.symbol === symbol) {
@@ -1297,6 +1329,7 @@ export class KaminoMarket {
1297
1329
  debtReserve: elevationGroup.debtReserve,
1298
1330
  debtLiquidityMint: PublicKey.default,
1299
1331
  elevationGroup: elevationGroup.id,
1332
+ maxReservesAsCollateral: elevationGroup.maxReservesAsCollateral,
1300
1333
  });
1301
1334
  }
1302
1335
 
@@ -1392,6 +1425,7 @@ export type ElevationGroupDescription = {
1392
1425
  debtReserve: PublicKey;
1393
1426
  debtLiquidityMint: PublicKey;
1394
1427
  elevationGroup: number;
1428
+ maxReservesAsCollateral: number;
1395
1429
  };
1396
1430
 
1397
1431
  export type KlendPrices = {
@@ -262,6 +262,11 @@ export class KaminoObligation {
262
262
  return undefined;
263
263
  }
264
264
 
265
+ getBorrowAmountByReserve(reserve: KaminoReserve): Decimal {
266
+ const amountLamports = this.getBorrowByMint(reserve.getLiquidityMint())?.amount ?? new Decimal(0);
267
+ return amountLamports.div(reserve.getMintFactor());
268
+ }
269
+
265
270
  getDepositByMint(mint: PublicKey): Position | undefined {
266
271
  for (const value of this.deposits.values()) {
267
272
  if (value.mintAddress.equals(mint)) {
@@ -271,6 +276,11 @@ export class KaminoObligation {
271
276
  return undefined;
272
277
  }
273
278
 
279
+ getDepositAmountByReserve(reserve: KaminoReserve): Decimal {
280
+ const amountLamports = this.getDepositByMint(reserve.getLiquidityMint())?.amount ?? new Decimal(0);
281
+ return amountLamports.div(reserve.getMintFactor());
282
+ }
283
+
274
284
  /**
275
285
  *
276
286
  * @returns Market value of the borrow in the specified obligation liquidity/borrow asset (USD) (no borrow factor weighting)
@@ -652,6 +662,71 @@ export class KaminoObligation {
652
662
  };
653
663
  }
654
664
 
665
+ /**
666
+ * Calculates the stats of the obligation after a hypothetical collateral swap.
667
+ */
668
+ getPostSwapCollObligationStats(params: {
669
+ withdrawAmountLamports: Decimal;
670
+ withdrawReserveAddress: PublicKey;
671
+ depositAmountLamports: Decimal;
672
+ depositReserveAddress: PublicKey;
673
+ newElevationGroup: number;
674
+ market: KaminoMarket;
675
+ slot: number;
676
+ }): ObligationStats {
677
+ const {
678
+ withdrawAmountLamports,
679
+ withdrawReserveAddress,
680
+ depositAmountLamports,
681
+ depositReserveAddress,
682
+ newElevationGroup,
683
+ market,
684
+ slot,
685
+ } = params;
686
+
687
+ const additionalReserves = [withdrawReserveAddress, depositReserveAddress].filter(
688
+ (reserveAddress) => !market.isReserveInObligation(this, reserveAddress)
689
+ );
690
+
691
+ const { collateralExchangeRates } = KaminoObligation.getRatesForObligation(
692
+ market,
693
+ this.state,
694
+ slot,
695
+ additionalReserves
696
+ );
697
+
698
+ let newObligationDeposits = this.state.deposits;
699
+ newObligationDeposits = this.simulateDepositChange(
700
+ newObligationDeposits,
701
+ withdrawAmountLamports.neg().toNumber(),
702
+ withdrawReserveAddress,
703
+ collateralExchangeRates
704
+ );
705
+ newObligationDeposits = this.simulateDepositChange(
706
+ newObligationDeposits,
707
+ depositAmountLamports.toNumber(),
708
+ depositReserveAddress,
709
+ collateralExchangeRates
710
+ );
711
+
712
+ const { refreshedStats } = this.calculatePositions(
713
+ market,
714
+ newObligationDeposits,
715
+ this.state.borrows,
716
+ newElevationGroup,
717
+ collateralExchangeRates,
718
+ null
719
+ );
720
+
721
+ const newStats = refreshedStats;
722
+ newStats.netAccountValue = newStats.userTotalDeposit.minus(newStats.userTotalBorrow);
723
+ newStats.loanToValue = valueOrZero(
724
+ newStats.userTotalBorrowBorrowFactorAdjusted.dividedBy(newStats.userTotalCollateralDeposit)
725
+ );
726
+ newStats.leverage = valueOrZero(newStats.userTotalDeposit.dividedBy(newStats.netAccountValue));
727
+ return newStats;
728
+ }
729
+
655
730
  estimateObligationInterestRate = (
656
731
  market: KaminoMarket,
657
732
  reserve: KaminoReserve,
@@ -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
+ }