@kamino-finance/klend-sdk 7.3.2 → 7.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -104,6 +104,7 @@ import type { AccountInfoBase, AccountInfoWithJsonData, AccountInfoWithPubkey }
104
104
  import { arrayElementConfigItems, ConfigUpdater } from './configItems';
105
105
  import { OracleMappings } from '@kamino-finance/scope-sdk/dist/@codegen/scope/accounts';
106
106
  import { getReserveFarmRewardsAPY as getReserveFarmRewardsAPYUtils, ReserveIncentives } from '../utils/farmUtils';
107
+ import { fetchKaminoCdnData } from '../utils/readCdnData';
107
108
 
108
109
  const base58Decoder = getBase58Decoder();
109
110
 
@@ -585,6 +586,25 @@ export class KaminoManager {
585
586
  return this._vaultClient.depositIxs(user, vault, tokenAmount, vaultReservesMap, farmState);
586
587
  }
587
588
 
589
+ /**
590
+ * This function creates instructions to buy shares (i.e. deposit) into a vault. It will also create ATA creation instructions for the vault shares that the user receives in return
591
+ * @param user - user to nuy shares
592
+ * @param vault - vault to buy shares from (if the state is not provided, it will be fetched)
593
+ * @param tokenAmount - token amount to be swapped for shares, in decimals (will be converted in lamports)
594
+ * @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
595
+ * @param [farmState] - the state of the vault farm, if the vault has a farm. Optional. If not provided, it will be fetched
596
+ * @returns - an instance of DepositIxs which contains the instructions to buy shares in vault and the instructions to stake the shares in the farm if the vault has a farm
597
+ */
598
+ async buyVaultSharesIxs(
599
+ user: TransactionSigner,
600
+ vault: KaminoVault,
601
+ tokenAmount: Decimal,
602
+ vaultReservesMap?: Map<Address, KaminoReserve>,
603
+ farmState?: FarmState
604
+ ): Promise<DepositIxs> {
605
+ return this._vaultClient.buySharesIxs(user, vault, tokenAmount, vaultReservesMap, farmState);
606
+ }
607
+
588
608
  /**
589
609
  * This function creates instructions to stake the shares in the vault farm if the vault has a farm
590
610
  * @param user - user to stake
@@ -718,6 +738,27 @@ export class KaminoManager {
718
738
  return this._vaultClient.withdrawIxs(user, vault, shareAmount, slot, vaultReservesMap, farmState);
719
739
  }
720
740
 
741
+ /**
742
+ * 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
743
+ * @param user - user to sell shares for vault tokens
744
+ * @param vault - vault to sell shares from
745
+ * @param shareAmount - share amount to sell (in tokens, not lamports), in order to withdraw everything, any value > user share amount
746
+ * @param slot - current slot, used to estimate the interest earned in the different reserves with allocation from the vault
747
+ * @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
748
+ * @param [farmState] - the state of the vault farm, if the vault has a farm. Optional. If not provided, it will be fetched
749
+ * @returns an array of instructions to create missing ATAs if needed and the withdraw instructions
750
+ */
751
+ async sellVaultSharesIxs(
752
+ user: TransactionSigner,
753
+ vault: KaminoVault,
754
+ shareAmount: Decimal,
755
+ slot: Slot,
756
+ vaultReservesMap?: Map<Address, KaminoReserve>,
757
+ farmState?: FarmState
758
+ ): Promise<WithdrawIxs> {
759
+ return this._vaultClient.sellSharesIxs(user, vault, shareAmount, slot, vaultReservesMap, farmState);
760
+ }
761
+
721
762
  /**
722
763
  * This method withdraws all the pending fees from the vault to the owner's token ATA
723
764
  * @param vault - vault for which the admin withdraws the pending fees
@@ -880,7 +921,10 @@ export class KaminoManager {
880
921
  const allReserves = reservePairs.map(([, reserve]) => reserve);
881
922
 
882
923
  // Get all oracle accounts
883
- const allOracleAccounts = await getAllOracleAccounts(this.getRpc(), allReserves);
924
+ const [allOracleAccounts, cdnResourcesData] = await Promise.all([
925
+ getAllOracleAccounts(this.getRpc(), allReserves),
926
+ fetchKaminoCdnData(),
927
+ ]);
884
928
  // Group reserves by market
885
929
  const marketToReserve = new Map<Address, ReserveWithAddress[]>();
886
930
  for (const [reserveAddress, reserveState] of reservePairs) {
@@ -919,7 +963,8 @@ export class KaminoManager {
919
963
  state,
920
964
  oracle,
921
965
  this.getRpc(),
922
- this.recentSlotDurationMs
966
+ this.recentSlotDurationMs,
967
+ cdnResourcesData
923
968
  );
924
969
  reservesByAddress.set(kaminoReserve.address, kaminoReserve);
925
970
  });
@@ -56,6 +56,7 @@ import { ObligationZP } from '../@codegen/klend/zero_padding';
56
56
  import { checkDefined } from '../utils/validations';
57
57
  import { Buffer } from 'buffer';
58
58
  import { ReserveStatus } from '../@codegen/klend/types';
59
+ import { fetchKaminoCdnData } from '../utils/readCdnData';
59
60
 
60
61
  export type KaminoMarketRpcApi = GetAccountInfoApi &
61
62
  GetMultipleAccountsApi &
@@ -479,7 +480,10 @@ export class KaminoMarket {
479
480
  state: reserveAccount,
480
481
  };
481
482
  });
482
- const reservesAndOracles = await getTokenOracleData(this.getRpc(), deserializedReserves, oracleAccounts);
483
+ const [reservesAndOracles, cdnResourcesData] = await Promise.all([
484
+ getTokenOracleData(this.getRpc(), deserializedReserves, oracleAccounts),
485
+ fetchKaminoCdnData(),
486
+ ]);
483
487
  const kaminoReserves = new Map<Address, KaminoReserve>();
484
488
  reservesAndOracles.forEach(([reserve, oracle], index) => {
485
489
  if (!oracle) {
@@ -494,7 +498,8 @@ export class KaminoMarket {
494
498
  reserve,
495
499
  oracle,
496
500
  this.rpc,
497
- this.recentSlotDurationMs
501
+ this.recentSlotDurationMs,
502
+ cdnResourcesData
498
503
  );
499
504
  kaminoReserves.set(kaminoReserve.address, kaminoReserve);
500
505
  });
@@ -1712,7 +1717,10 @@ export async function getReservesForMarket(
1712
1717
  state: reserveAccount,
1713
1718
  };
1714
1719
  });
1715
- const reservesAndOracles = await getTokenOracleData(rpc, deserializedReserves, oracleAccounts);
1720
+ const [reservesAndOracles, cdnResourcesData] = await Promise.all([
1721
+ getTokenOracleData(rpc, deserializedReserves, oracleAccounts),
1722
+ fetchKaminoCdnData(),
1723
+ ]);
1716
1724
  const reservesByAddress = new Map<Address, KaminoReserve>();
1717
1725
  reservesAndOracles.forEach(([reserve, oracle], index) => {
1718
1726
  if (reserve.config.status === ReserveStatus.Obsolete.discriminator) {
@@ -1725,7 +1733,14 @@ export async function getReservesForMarket(
1725
1733
  }) reserve in market ${reserve.lendingMarket}`
1726
1734
  );
1727
1735
  }
1728
- const kaminoReserve = KaminoReserve.initialize(reserves[index].pubkey, reserve, oracle, rpc, recentSlotDurationMs);
1736
+ const kaminoReserve = KaminoReserve.initialize(
1737
+ reserves[index].pubkey,
1738
+ reserve,
1739
+ oracle,
1740
+ rpc,
1741
+ recentSlotDurationMs,
1742
+ cdnResourcesData
1743
+ );
1729
1744
  reservesByAddress.set(kaminoReserve.address, kaminoReserve);
1730
1745
  });
1731
1746
  return reservesByAddress;
@@ -1743,7 +1758,10 @@ export async function getSingleReserve(
1743
1758
  if (reserve === null) {
1744
1759
  throw new Error(`Reserve account ${reservePk} does not exist`);
1745
1760
  }
1746
- const reservesAndOracles = await getTokenOracleData(rpc, [{ address: reservePk, state: reserve }], oracleAccounts);
1761
+ const [reservesAndOracles, cdnResourcesData] = await Promise.all([
1762
+ getTokenOracleData(rpc, [{ address: reservePk, state: reserve }], oracleAccounts),
1763
+ fetchKaminoCdnData(),
1764
+ ]);
1747
1765
  const [, oracle] = reservesAndOracles[0];
1748
1766
 
1749
1767
  if (!oracle) {
@@ -1753,7 +1771,7 @@ export async function getSingleReserve(
1753
1771
  }`
1754
1772
  );
1755
1773
  }
1756
- return KaminoReserve.initialize(reservePk, reserve, oracle, rpc, recentSlotDurationMs);
1774
+ return KaminoReserve.initialize(reservePk, reserve, oracle, rpc, recentSlotDurationMs, cdnResourcesData);
1757
1775
  }
1758
1776
 
1759
1777
  export function getReservesActive(reserves: Map<Address, KaminoReserve>): Map<Address, KaminoReserve> {
@@ -57,6 +57,7 @@ import { SYSVAR_RENT_ADDRESS } from '@solana/sysvars';
57
57
  import { noopSigner } from '../utils/signer';
58
58
  import { getRewardPerTimeUnitSecond } from './farm_utils';
59
59
  import { Scope, ScopeEntryMetadata } from '@kamino-finance/scope-sdk';
60
+ import { fetchKaminoCdnData, KaminoCdnData } from '../utils/readCdnData';
60
61
 
61
62
  export type KaminoReserveRpcApi = GetProgramAccountsApi & GetAccountInfoApi & GetMultipleAccountsApi;
62
63
 
@@ -97,10 +98,11 @@ export class KaminoReserve {
97
98
  state: Reserve,
98
99
  tokenOraclePrice: TokenOracleData,
99
100
  rpc: Rpc<KaminoReserveRpcApi>,
100
- recentSlotDurationMs: number
101
+ recentSlotDurationMs: number,
102
+ cdnResourcesData?: KaminoCdnData
101
103
  ): KaminoReserve {
102
104
  const reserve = new KaminoReserve(state, address, tokenOraclePrice, rpc, recentSlotDurationMs);
103
- reserve.stats = reserve.formatReserveData(state);
105
+ reserve.stats = reserve.formatReserveData(state, cdnResourcesData?.deprecatedAssets ?? []);
104
106
  return reserve;
105
107
  }
106
108
 
@@ -779,13 +781,16 @@ export class KaminoReserve {
779
781
  }
780
782
 
781
783
  async load(tokenOraclePrice: TokenOracleData) {
782
- const parsedData = await Reserve.fetch(this.rpc, this.address);
784
+ const [parsedData, cdnResourcesData] = await Promise.all([
785
+ Reserve.fetch(this.rpc, this.address),
786
+ fetchKaminoCdnData(),
787
+ ]);
783
788
  if (!parsedData) {
784
789
  throw Error(`Unable to parse data of reserve ${this.symbol}`);
785
790
  }
786
791
  this.state = parsedData;
787
792
  this.tokenOraclePrice = tokenOraclePrice;
788
- this.stats = this.formatReserveData(parsedData);
793
+ this.stats = this.formatReserveData(parsedData, cdnResourcesData?.deprecatedAssets ?? []);
789
794
  }
790
795
 
791
796
  totalSupplyAPY(currentSlot: Slot) {
@@ -874,7 +879,7 @@ export class KaminoReserve {
874
879
  return { apy: aprToApy(apr, 365), apr };
875
880
  }
876
881
 
877
- private formatReserveData(parsedData: ReserveFields): ReserveDataType {
882
+ private formatReserveData(parsedData: ReserveFields, deprecatedAssets: string[]): ReserveDataType {
878
883
  const mintTotalSupply = new Decimal(parsedData.collateral.mintTotalSupply.toString()).div(this.getMintFactor());
879
884
  let reserveStatus = ReserveStatus.Active;
880
885
  switch (parsedData.config.status) {
@@ -888,6 +893,8 @@ export class KaminoReserve {
888
893
  reserveStatus = ReserveStatus.Hidden;
889
894
  break;
890
895
  }
896
+ const reserveIsUIDeprecated =
897
+ deprecatedAssets.length > 0 ? deprecatedAssets.includes(this.address.toString()) : undefined;
891
898
  return {
892
899
  // Reserve config
893
900
 
@@ -910,6 +917,7 @@ export class KaminoReserve {
910
917
  depositLimitCrossedTimestamp: parsedData.liquidity.depositLimitCrossedTimestamp.toNumber(),
911
918
  borrowLimitCrossedTimestamp: parsedData.liquidity.borrowLimitCrossedTimestamp.toNumber(),
912
919
  borrowFactor: parsedData.config.borrowFactorPct.toNumber(),
920
+ isUIDeprecated: reserveIsUIDeprecated,
913
921
  };
914
922
  }
915
923
 
@@ -44,6 +44,7 @@ export type ReserveDataType = {
44
44
  accumulatedProtocolFees: Decimal;
45
45
  mintTotalSupply: Decimal;
46
46
  borrowFactor: number;
47
+ isUIDeprecated: boolean | undefined;
47
48
  };
48
49
 
49
50
  export type ReserveRewardYield = {