@kamino-finance/klend-sdk 7.0.10 → 7.0.12

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.
@@ -17,7 +17,7 @@ import {
17
17
  Slot,
18
18
  } from '@solana/kit';
19
19
  import { KaminoObligation } from './obligation';
20
- import { KaminoReserve, KaminoReserveRpcApi } from './reserve';
20
+ import { KaminoReserve, KaminoReserveRpcApi, ReserveWithAddress } from './reserve';
21
21
  import { LendingMarket, Obligation, ReferrerTokenState, Reserve, UserMetadata } from '../@codegen/klend/accounts';
22
22
  import {
23
23
  AllOracleAccounts,
@@ -446,13 +446,13 @@ export class KaminoMarket {
446
446
  /**
447
447
  * @returns The max borrowable amount for leverage positions
448
448
  */
449
- async getMaxLeverageBorrowableAmount(
449
+ getMaxLeverageBorrowableAmount(
450
450
  collReserve: KaminoReserve,
451
451
  debtReserve: KaminoReserve,
452
452
  slot: Slot,
453
453
  requestElevationGroup: boolean,
454
454
  obligation?: KaminoObligation
455
- ): Promise<Decimal> {
455
+ ): Decimal {
456
456
  return obligation
457
457
  ? obligation.getMaxBorrowAmount(this, debtReserve.getLiquidityMint(), slot, requestElevationGroup)
458
458
  : debtReserve.getMaxBorrowAmountWithCollReserve(this, collReserve, slot);
@@ -463,7 +463,7 @@ export class KaminoMarket {
463
463
  const reserveAccounts = await this.rpc
464
464
  .getMultipleAccounts(addresses, { commitment: 'processed', encoding: 'base64' })
465
465
  .send();
466
- const deserializedReserves = reserveAccounts.value.map((reserve, i) => {
466
+ const deserializedReserves: ReserveWithAddress[] = reserveAccounts.value.map((reserve, i) => {
467
467
  if (reserve === null) {
468
468
  // maybe reuse old here
469
469
  throw new Error(`Reserve account ${addresses[i]} was not found`);
@@ -472,7 +472,10 @@ export class KaminoMarket {
472
472
  if (!reserveAccount) {
473
473
  throw Error(`Could not parse reserve ${addresses[i]}`);
474
474
  }
475
- return reserveAccount;
475
+ return {
476
+ address: addresses[i],
477
+ state: reserveAccount,
478
+ };
476
479
  });
477
480
  const reservesAndOracles = await getTokenOracleData(this.getRpc(), deserializedReserves, oracleAccounts);
478
481
  const kaminoReserves = new Map<Address, KaminoReserve>();
@@ -1594,7 +1597,7 @@ export async function getReservesForMarket(
1594
1597
  encoding: 'base64',
1595
1598
  })
1596
1599
  .send();
1597
- const deserializedReserves = reserves.map((reserve) => {
1600
+ const deserializedReserves: ReserveWithAddress[] = reserves.map((reserve) => {
1598
1601
  if (reserve.account === null) {
1599
1602
  throw new Error(`Reserve account ${reserve.pubkey} does not exist`);
1600
1603
  }
@@ -1604,7 +1607,10 @@ export async function getReservesForMarket(
1604
1607
  if (!reserveAccount) {
1605
1608
  throw Error(`Could not parse reserve ${reserve.pubkey}`);
1606
1609
  }
1607
- return reserveAccount;
1610
+ return {
1611
+ address: reserve.pubkey,
1612
+ state: reserveAccount,
1613
+ };
1608
1614
  });
1609
1615
  const reservesAndOracles = await getTokenOracleData(rpc, deserializedReserves, oracleAccounts);
1610
1616
  const reservesByAddress = new Map<Address, KaminoReserve>();
@@ -1630,7 +1636,7 @@ export async function getSingleReserve(
1630
1636
  if (reserve === null) {
1631
1637
  throw new Error(`Reserve account ${reservePk} does not exist`);
1632
1638
  }
1633
- const reservesAndOracles = await getTokenOracleData(rpc, [reserve], oracleAccounts);
1639
+ const reservesAndOracles = await getTokenOracleData(rpc, [{ address: reservePk, state: reserve }], oracleAccounts);
1634
1640
  const [, oracle] = reservesAndOracles[0];
1635
1641
 
1636
1642
  if (!oracle) {
@@ -112,7 +112,11 @@ export class KaminoReserve {
112
112
  throw new Error(`Reserve account ${address} does not exist`);
113
113
  }
114
114
 
115
- const tokenOracleDataWithReserve = await getTokenOracleData(rpc, [reserve], oracleAccounts);
115
+ const tokenOracleDataWithReserve = await getTokenOracleData(
116
+ rpc,
117
+ [{ address: address, state: reserve }],
118
+ oracleAccounts
119
+ );
116
120
  if (!tokenOracleDataWithReserve[0]) {
117
121
  throw new Error('Token oracle data not found');
118
122
  }
@@ -2386,7 +2386,10 @@ export class KaminoVaultClient {
2386
2386
  if (!reserveAccount) {
2387
2387
  throw Error(`Could not parse reserve ${vaultReservesAddresses[i]}`);
2388
2388
  }
2389
- return reserveAccount;
2389
+ return {
2390
+ address: vaultReservesAddresses[i],
2391
+ state: reserveAccount,
2392
+ };
2390
2393
  });
2391
2394
 
2392
2395
  const reservesAndOracles = await getTokenOracleData(this.getConnection(), deserializedReserves, oracleAccounts);
@@ -2445,9 +2448,18 @@ export class KaminoVaultClient {
2445
2448
  });
2446
2449
 
2447
2450
  // read missing reserves
2448
- const missingReservesStates = (await Reserve.fetchMultiple(this.getConnection(), [...missingReserves])).filter(
2449
- (reserve) => reserve !== null
2450
- );
2451
+ const missingReserveAddresses = [...missingReserves];
2452
+ const missingReservesStates = (await Reserve.fetchMultiple(this.getConnection(), missingReserveAddresses))
2453
+ .map((reserve, index) => {
2454
+ if (!reserve) {
2455
+ return null;
2456
+ }
2457
+ return {
2458
+ address: missingReserveAddresses[index],
2459
+ state: reserve,
2460
+ };
2461
+ })
2462
+ .filter((state) => state !== null);
2451
2463
  const missingReservesAndOracles = await getTokenOracleData(
2452
2464
  this.getConnection(),
2453
2465
  missingReservesStates,
@@ -2456,7 +2468,7 @@ export class KaminoVaultClient {
2456
2468
  missingReservesAndOracles.forEach(([reserve, oracle], index) => {
2457
2469
  const fetchedReserve = new KaminoReserve(
2458
2470
  reserve,
2459
- [...missingReserves][index]!, // Set maintains order
2471
+ missingReserveAddresses[index]!, // Set maintains order
2460
2472
  oracle!,
2461
2473
  this.getConnection(),
2462
2474
  this.recentSlotDurationMs
@@ -3,7 +3,7 @@ import Decimal from 'decimal.js';
3
3
  import { Scope } from '@kamino-finance/scope-sdk';
4
4
  import { OraclePrices } from '@kamino-finance/scope-sdk/dist/@codegen/scope/accounts/OraclePrices';
5
5
  import { isNotNullPubkey } from './pubkey';
6
- import { parseTokenSymbol } from '../classes';
6
+ import { parseTokenSymbol, ReserveWithAddress } from '../classes';
7
7
  import { Reserve } from '../lib';
8
8
  import { batchFetch } from '@kamino-finance/kliquidity-sdk';
9
9
  import BN from 'bn.js';
@@ -43,12 +43,12 @@ export type ScopePriceRefreshConfig = {
43
43
  scopeConfigurations: [Address, Configuration][];
44
44
  };
45
45
 
46
- export function getTokenOracleDataSync(allOracleAccounts: AllOracleAccounts, reserves: Reserve[]) {
46
+ export function getTokenOracleDataSync(allOracleAccounts: AllOracleAccounts, reserves: ReserveWithAddress[]) {
47
47
  const tokenOracleDataForReserves: Array<[Reserve, TokenOracleData | undefined]> = [];
48
48
  const pythCache = new Map<Address, PythPrices>();
49
49
  const switchboardCache = new Map<Address, CandidatePrice>();
50
50
  const scopeCache = new Map<Address, OraclePrices>();
51
- for (const reserve of reserves) {
51
+ for (const { address, state: reserve } of reserves) {
52
52
  let currentBest: CandidatePrice | undefined = undefined;
53
53
  const oracle = {
54
54
  pythAddress: reserve.config.tokenInfo.pythConfiguration.price,
@@ -86,7 +86,10 @@ export function getTokenOracleDataSync(allOracleAccounts: AllOracleAccounts, res
86
86
  }
87
87
 
88
88
  if (!currentBest) {
89
- console.error(`No price found for reserve: ${parseTokenSymbol(reserve.config.tokenInfo.name)}`);
89
+ const reserveSymbol = parseTokenSymbol(reserve.config.tokenInfo.name);
90
+ console.error(
91
+ `No price found for reserve: ${reserveSymbol ?? 'unknown'} (${address}) in market: ${reserve.lendingMarket}`
92
+ );
90
93
  tokenOracleDataForReserves.push([reserve, undefined]);
91
94
  continue;
92
95
  }
@@ -105,10 +108,15 @@ export function getTokenOracleDataSync(allOracleAccounts: AllOracleAccounts, res
105
108
  // TODO: Add freshness of the latest price to match sc logic
106
109
  export async function getTokenOracleData(
107
110
  rpc: Rpc<GetMultipleAccountsApi>,
108
- reserves: Reserve[],
111
+ reserves: ReserveWithAddress[],
109
112
  oracleAccounts?: AllOracleAccounts
110
113
  ): Promise<Array<[Reserve, TokenOracleData | undefined]>> {
111
- const allOracleAccounts = oracleAccounts ?? (await getAllOracleAccounts(rpc, reserves));
114
+ const allOracleAccounts =
115
+ oracleAccounts ??
116
+ (await getAllOracleAccounts(
117
+ rpc,
118
+ reserves.map((r) => r.state)
119
+ ));
112
120
  return getTokenOracleDataSync(allOracleAccounts, reserves);
113
121
  }
114
122