@kamino-finance/klend-sdk 6.0.5-beta.2 → 6.0.5-beta.20

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 (62) hide show
  1. package/dist/classes/action.d.ts +1 -1
  2. package/dist/classes/action.d.ts.map +1 -1
  3. package/dist/classes/action.js +32 -16
  4. package/dist/classes/action.js.map +1 -1
  5. package/dist/classes/manager.d.ts +29 -18
  6. package/dist/classes/manager.d.ts.map +1 -1
  7. package/dist/classes/manager.js +66 -49
  8. package/dist/classes/manager.js.map +1 -1
  9. package/dist/classes/market.d.ts +12 -11
  10. package/dist/classes/market.d.ts.map +1 -1
  11. package/dist/classes/market.js +77 -37
  12. package/dist/classes/market.js.map +1 -1
  13. package/dist/classes/vault.d.ts +5 -3
  14. package/dist/classes/vault.d.ts.map +1 -1
  15. package/dist/classes/vault.js +8 -6
  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 +30 -22
  19. package/dist/client_kamino_manager.js.map +1 -1
  20. package/dist/lending_operations/repay_with_collateral_operations.d.ts.map +1 -1
  21. package/dist/lending_operations/repay_with_collateral_operations.js +36 -32
  22. package/dist/lending_operations/repay_with_collateral_operations.js.map +1 -1
  23. package/dist/lending_operations/swap_collateral_operations.d.ts.map +1 -1
  24. package/dist/lending_operations/swap_collateral_operations.js +4 -4
  25. package/dist/lending_operations/swap_collateral_operations.js.map +1 -1
  26. package/dist/leverage/operations.d.ts +4 -3
  27. package/dist/leverage/operations.d.ts.map +1 -1
  28. package/dist/leverage/operations.js +186 -154
  29. package/dist/leverage/operations.js.map +1 -1
  30. package/dist/leverage/types.d.ts +1 -0
  31. package/dist/leverage/types.d.ts.map +1 -1
  32. package/dist/utils/managerTypes.d.ts +1 -2
  33. package/dist/utils/managerTypes.d.ts.map +1 -1
  34. package/dist/utils/managerTypes.js +9 -9
  35. package/dist/utils/managerTypes.js.map +1 -1
  36. package/dist/utils/obligations.d.ts +5 -0
  37. package/dist/utils/obligations.d.ts.map +1 -0
  38. package/dist/utils/obligations.js +53 -0
  39. package/dist/utils/obligations.js.map +1 -0
  40. package/dist/utils/oracle.d.ts +3 -3
  41. package/dist/utils/oracle.d.ts.map +1 -1
  42. package/dist/utils/oracle.js +2 -2
  43. package/dist/utils/oracle.js.map +1 -1
  44. package/dist/utils/pubkey.d.ts +1 -0
  45. package/dist/utils/pubkey.d.ts.map +1 -1
  46. package/dist/utils/pubkey.js +10 -0
  47. package/dist/utils/pubkey.js.map +1 -1
  48. package/package.json +3 -3
  49. package/src/classes/action.ts +32 -20
  50. package/src/classes/manager.ts +87 -53
  51. package/src/classes/market.ts +132 -52
  52. package/src/classes/vault.ts +17 -6
  53. package/src/client.ts +4 -4
  54. package/src/client_kamino_manager.ts +40 -35
  55. package/src/lending_operations/repay_with_collateral_operations.ts +76 -72
  56. package/src/lending_operations/swap_collateral_operations.ts +13 -11
  57. package/src/leverage/operations.ts +362 -328
  58. package/src/leverage/types.ts +1 -0
  59. package/src/utils/managerTypes.ts +1 -2
  60. package/src/utils/obligations.ts +69 -0
  61. package/src/utils/oracle.ts +5 -4
  62. package/src/utils/pubkey.ts +9 -0
@@ -22,6 +22,8 @@ import {
22
22
  CandidatePrice,
23
23
  PublicKeySet,
24
24
  DEPOSITS_LIMIT,
25
+ setOrAppend,
26
+ AllOracleAccounts,
25
27
  } from '../utils';
26
28
  import base58 from 'bs58';
27
29
  import { BN } from '@coral-xyz/anchor';
@@ -31,7 +33,7 @@ import { PROGRAM_ID } from '../idl_codegen/programId';
31
33
  import bs58 from 'bs58';
32
34
  import { OraclePrices, Scope, U16_MAX } from '@kamino-finance/scope-sdk';
33
35
  import { Fraction } from './fraction';
34
- import { chunks, KaminoPrices, MintToPriceMap } from '@kamino-finance/kliquidity-sdk';
36
+ import { batchFetch, chunks, KaminoPrices, MintToPriceMap } from '@kamino-finance/kliquidity-sdk';
35
37
  import { parseTokenSymbol, parseZeroPaddedUtf8 } from './utils';
36
38
  import SwitchboardProgram from '@switchboard-xyz/sbv2-lite';
37
39
  import { ObligationZP } from '../idl_codegen/zero_padding';
@@ -62,9 +64,7 @@ export class KaminoMarket {
62
64
 
63
65
  private readonly recentSlotDurationMs: number;
64
66
 
65
- // key = scope feed (oracle prices) pubkey
66
- // value = reserve pubkey
67
- private readonly reserveScopeFeeds: PubkeyHashMap<PublicKey, PublicKey>;
67
+ readonly scopeFeeds: PublicKeySet<PublicKey>;
68
68
 
69
69
  private constructor(
70
70
  connection: Connection,
@@ -74,6 +74,10 @@ export class KaminoMarket {
74
74
  recentSlotDurationMs: number,
75
75
  programId: PublicKey = PROGRAM_ID
76
76
  ) {
77
+ if (recentSlotDurationMs <= 0) {
78
+ throw new Error('Recent slot duration cannot be 0 or less');
79
+ }
80
+
77
81
  this.address = marketAddress;
78
82
  this.connection = connection;
79
83
  this.state = state;
@@ -81,10 +85,10 @@ export class KaminoMarket {
81
85
  this.reservesActive = getReservesActive(this.reserves);
82
86
  this.programId = programId;
83
87
  this.recentSlotDurationMs = recentSlotDurationMs;
84
- this.reserveScopeFeeds = new PubkeyHashMap(
88
+ this.scopeFeeds = new PublicKeySet(
85
89
  Array.from(this.reserves.values())
86
90
  .filter((r) => isNotNullPubkey(r.state.config.tokenInfo.scopeConfiguration.priceFeed))
87
- .map((r) => [r.address, r.state.config.tokenInfo.scopeConfiguration.priceFeed])
91
+ .map((r) => r.state.config.tokenInfo.scopeConfiguration.priceFeed)
88
92
  );
89
93
  }
90
94
 
@@ -95,7 +99,6 @@ export class KaminoMarket {
95
99
  * @param recentSlotDurationMs
96
100
  * @param programId
97
101
  * @param withReserves
98
- * @param setupLocalTest
99
102
  * @param withReserves
100
103
  */
101
104
  static async load(
@@ -111,10 +114,6 @@ export class KaminoMarket {
111
114
  return null;
112
115
  }
113
116
 
114
- if (recentSlotDurationMs <= 0) {
115
- throw new Error('Recent slot duration cannot be 0');
116
- }
117
-
118
117
  const reserves = withReserves
119
118
  ? await getReservesForMarket(marketAddress, connection, programId, recentSlotDurationMs)
120
119
  : new Map<PublicKey, KaminoReserve>();
@@ -133,6 +132,68 @@ export class KaminoMarket {
133
132
  return new KaminoMarket(connection, market, marketAddress.toString(), reserves, recentSlotDurationMs, programId);
134
133
  }
135
134
 
135
+ static async loadMultiple(
136
+ connection: Connection,
137
+ markets: PublicKey[],
138
+ recentSlotDurationMs: number,
139
+ programId: PublicKey = PROGRAM_ID,
140
+ withReserves: boolean = true,
141
+ oracleAccounts?: AllOracleAccounts
142
+ ) {
143
+ const marketStates = await batchFetch(markets, (market) =>
144
+ LendingMarket.fetchMultiple(connection, market, programId)
145
+ );
146
+ const kaminoMarkets = new PubkeyHashMap<PublicKey, KaminoMarket>();
147
+ for (let i = 0; i < markets.length; i++) {
148
+ const market = marketStates[i];
149
+ const marketAddress = markets[i];
150
+ if (market === null) {
151
+ throw Error(`Could not fetch LendingMarket account state for market ${marketAddress}`);
152
+ }
153
+
154
+ const marketReserves = withReserves
155
+ ? await getReservesForMarket(marketAddress, connection, programId, recentSlotDurationMs, oracleAccounts)
156
+ : new PubkeyHashMap<PublicKey, KaminoReserve>();
157
+
158
+ kaminoMarkets.set(
159
+ marketAddress,
160
+ new KaminoMarket(connection, market, marketAddress.toString(), marketReserves, recentSlotDurationMs, programId)
161
+ );
162
+ }
163
+ return kaminoMarkets;
164
+ }
165
+
166
+ static async loadMultipleWithReserves(
167
+ connection: Connection,
168
+ markets: PublicKey[],
169
+ reserves: PubkeyHashMap<PublicKey, PubkeyHashMap<PublicKey, KaminoReserve>>,
170
+ recentSlotDurationMs: number,
171
+ programId: PublicKey = PROGRAM_ID
172
+ ) {
173
+ const marketStates = await batchFetch(markets, (market) =>
174
+ LendingMarket.fetchMultiple(connection, market, programId)
175
+ );
176
+ const kaminoMarkets = new PubkeyHashMap<PublicKey, KaminoMarket>();
177
+ for (let i = 0; i < markets.length; i++) {
178
+ const market = marketStates[i];
179
+ const marketAddress = markets[i];
180
+ if (market === null) {
181
+ throw Error(`Could not fetch LendingMarket account state for market ${marketAddress}`);
182
+ }
183
+ const marketReserves = reserves.get(marketAddress);
184
+ if (!marketReserves) {
185
+ throw Error(
186
+ `Could not get reserves for market ${marketAddress} from the reserves map argument supplied to this method`
187
+ );
188
+ }
189
+ kaminoMarkets.set(
190
+ marketAddress,
191
+ new KaminoMarket(connection, market, marketAddress.toString(), marketReserves, recentSlotDurationMs, programId)
192
+ );
193
+ }
194
+ return kaminoMarkets;
195
+ }
196
+
136
197
  async reload(): Promise<void> {
137
198
  const market = await LendingMarket.fetch(this.connection, this.getAddress(), this.programId);
138
199
  if (market === null) {
@@ -373,19 +434,19 @@ export class KaminoMarket {
373
434
  /**
374
435
  * @returns The max borrowable amount for leverage positions
375
436
  */
376
- async getMaxLeverageBorrowableAmount(
437
+ getMaxLeverageBorrowableAmount(
377
438
  collReserve: KaminoReserve,
378
439
  debtReserve: KaminoReserve,
379
440
  slot: number,
380
441
  requestElevationGroup: boolean,
381
442
  obligation?: KaminoObligation
382
- ): Promise<Decimal> {
443
+ ): Decimal {
383
444
  return obligation
384
445
  ? obligation.getMaxBorrowAmount(this, debtReserve.getLiquidityMint(), slot, requestElevationGroup)
385
446
  : debtReserve.getMaxBorrowAmountWithCollReserve(this, collReserve, slot);
386
447
  }
387
448
 
388
- async loadReserves() {
449
+ async loadReserves(oracleAccounts?: AllOracleAccounts) {
389
450
  const addresses = [...this.reserves.keys()];
390
451
  const reserveAccounts = await this.connection.getMultipleAccountsInfo(addresses, 'processed');
391
452
  const deserializedReserves = reserveAccounts.map((reserve, i) => {
@@ -399,7 +460,7 @@ export class KaminoMarket {
399
460
  }
400
461
  return reserveAccount;
401
462
  });
402
- const reservesAndOracles = await getTokenOracleData(this.connection, deserializedReserves);
463
+ const reservesAndOracles = await getTokenOracleData(this.connection, deserializedReserves, oracleAccounts);
403
464
  const kaminoReserves = new PubkeyHashMap<PublicKey, KaminoReserve>();
404
465
  reservesAndOracles.forEach(([reserve, oracle], index) => {
405
466
  if (!oracle) {
@@ -851,9 +912,13 @@ export class KaminoMarket {
851
912
  return finalObligations;
852
913
  }
853
914
 
854
- async getAllUserObligations(user: PublicKey, commitment = this.connection.commitment): Promise<KaminoObligation[]> {
915
+ async getAllUserObligations(
916
+ user: PublicKey,
917
+ commitment = this.connection.commitment,
918
+ slot?: number
919
+ ): Promise<KaminoObligation[]> {
855
920
  const [currentSlot, obligations] = await Promise.all([
856
- this.connection.getSlot(),
921
+ slot !== undefined ? Promise.resolve(slot) : this.connection.getSlot(),
857
922
  this.connection.getProgramAccounts(this.programId, {
858
923
  filters: [
859
924
  {
@@ -1211,12 +1276,16 @@ export class KaminoMarket {
1211
1276
  */
1212
1277
  async getReserveOraclePrices(scope: Scope): Promise<PubkeyHashMap<PublicKey, OraclePrices>> {
1213
1278
  const reserveOraclePrices: PubkeyHashMap<PublicKey, OraclePrices> = new PubkeyHashMap();
1214
- const oraclePrices = await scope.getMultipleOraclePrices(Array.from(this.reserveScopeFeeds.values()));
1215
- for (const [feed, oraclePricesAccount] of oraclePrices) {
1216
- const reserve = this.reserveScopeFeeds.get(feed);
1217
- if (reserve) {
1218
- reserveOraclePrices.set(reserve, oraclePricesAccount);
1219
- }
1279
+ const oraclePrices = await scope.getMultipleOraclePrices(this.scopeFeeds.toArray());
1280
+ const oraclePriceMap = new PubkeyHashMap<PublicKey, OraclePrices>();
1281
+ for (const [feed, account] of oraclePrices) {
1282
+ oraclePriceMap.set(feed, account);
1283
+ }
1284
+ for (const [reserveAddress, reserve] of this.reserves) {
1285
+ reserveOraclePrices.set(
1286
+ reserveAddress,
1287
+ oraclePriceMap.get(reserve.state.config.tokenInfo.scopeConfiguration.priceFeed)!
1288
+ );
1220
1289
  }
1221
1290
  return reserveOraclePrices;
1222
1291
  }
@@ -1224,8 +1293,10 @@ export class KaminoMarket {
1224
1293
  /**
1225
1294
  * Get all Scope prices used by all the market reserves
1226
1295
  */
1227
- async getAllScopePrices(scope: Scope): Promise<KaminoPrices> {
1228
- const allOraclePrices = await this.getReserveOraclePrices(scope);
1296
+ async getAllScopePrices(
1297
+ scope: Scope,
1298
+ allOraclePrices: PubkeyHashMap<PublicKey, OraclePrices>
1299
+ ): Promise<KaminoPrices> {
1229
1300
  const spot: MintToPriceMap = {};
1230
1301
  const twaps: MintToPriceMap = {};
1231
1302
  for (const reserve of this.reserves.values()) {
@@ -1234,7 +1305,7 @@ export class KaminoMarket {
1234
1305
  const oracle = reserve.state.config.tokenInfo.scopeConfiguration.priceFeed;
1235
1306
  const chain = reserve.state.config.tokenInfo.scopeConfiguration.priceChain;
1236
1307
  const twapChain = reserve.state.config.tokenInfo.scopeConfiguration.twapChain.filter((x) => x > 0);
1237
- const oraclePrices = allOraclePrices.get(reserve.address);
1308
+ const oraclePrices = allOraclePrices.get(oracle);
1238
1309
  if (oraclePrices && oracle && isNotNullPubkey(oracle) && chain && Scope.isScopeChainValid(chain)) {
1239
1310
  const spotPrice = await scope.getPriceFromChain(chain, oraclePrices);
1240
1311
  spot[tokenMint] = { price: spotPrice.price, name: tokenName };
@@ -1250,16 +1321,18 @@ export class KaminoMarket {
1250
1321
  /**
1251
1322
  * Get all Scope/Pyth/Switchboard prices used by all the market reserves
1252
1323
  */
1253
- async getAllPrices(): Promise<KlendPrices> {
1324
+ async getAllPrices(oracleAccounts?: AllOracleAccounts): Promise<KlendPrices> {
1254
1325
  const klendPrices: KlendPrices = {
1255
1326
  scope: { spot: {}, twap: {} },
1256
1327
  pyth: { spot: {}, twap: {} },
1257
1328
  switchboard: { spot: {}, twap: {} },
1258
1329
  };
1259
- const allOracleAccounts = await getAllOracleAccounts(
1260
- this.connection,
1261
- this.getReserves().map((x) => x.state)
1262
- );
1330
+ const allOracleAccounts =
1331
+ oracleAccounts ??
1332
+ (await getAllOracleAccounts(
1333
+ this.connection,
1334
+ this.getReserves().map((x) => x.state)
1335
+ ));
1263
1336
  const pythCache = new PubkeyHashMap<PublicKey, PythPrices>();
1264
1337
  const switchboardCache = new PubkeyHashMap<PublicKey, CandidatePrice>();
1265
1338
  const scopeCache = new PubkeyHashMap<PublicKey, OraclePrices>();
@@ -1471,7 +1544,8 @@ export async function getReservesForMarket(
1471
1544
  marketAddress: PublicKey,
1472
1545
  connection: Connection,
1473
1546
  programId: PublicKey,
1474
- recentSlotDurationMs: number
1547
+ recentSlotDurationMs: number,
1548
+ oracleAccounts?: AllOracleAccounts
1475
1549
  ): Promise<Map<PublicKey, KaminoReserve>> {
1476
1550
  const reserves = await connection.getProgramAccounts(programId, {
1477
1551
  filters: [
@@ -1499,7 +1573,7 @@ export async function getReservesForMarket(
1499
1573
  return reserveAccount;
1500
1574
  });
1501
1575
  const allBuffers = reserves.map((reserve) => reserve.account);
1502
- const reservesAndOracles = await getTokenOracleData(connection, deserializedReserves);
1576
+ const reservesAndOracles = await getTokenOracleData(connection, deserializedReserves, oracleAccounts);
1503
1577
  const reservesByAddress = new PubkeyHashMap<PublicKey, KaminoReserve>();
1504
1578
  reservesAndOracles.forEach(([reserve, oracle], index) => {
1505
1579
  if (!oracle) {
@@ -1522,7 +1596,8 @@ export async function getSingleReserve(
1522
1596
  reservePk: PublicKey,
1523
1597
  connection: Connection,
1524
1598
  recentSlotDurationMs: number,
1525
- accountData?: AccountInfo<Buffer>
1599
+ accountData?: AccountInfo<Buffer>,
1600
+ oracleAccounts?: AllOracleAccounts
1526
1601
  ): Promise<KaminoReserve> {
1527
1602
  const reserve = accountData ? accountData : await connection.getAccountInfo(reservePk);
1528
1603
 
@@ -1536,22 +1611,13 @@ export async function getSingleReserve(
1536
1611
  throw Error(`Could not parse reserve ${reservePk.toBase58()}`);
1537
1612
  }
1538
1613
 
1539
- const reservesAndOracles = await getTokenOracleData(connection, [reserveAccount]);
1614
+ const reservesAndOracles = await getTokenOracleData(connection, [reserveAccount], oracleAccounts);
1540
1615
  const [reserveState, oracle] = reservesAndOracles[0];
1541
1616
 
1542
1617
  if (!oracle) {
1543
1618
  throw Error(`Could not find oracle for ${parseTokenSymbol(reserveState.config.tokenInfo.name)} reserve`);
1544
1619
  }
1545
- const kaminoReserve = KaminoReserve.initialize(
1546
- reserve,
1547
- reservePk,
1548
- reserveState,
1549
- oracle,
1550
- connection,
1551
- recentSlotDurationMs
1552
- );
1553
-
1554
- return kaminoReserve;
1620
+ return KaminoReserve.initialize(reserve, reservePk, reserveState, oracle, connection, recentSlotDurationMs);
1555
1621
  }
1556
1622
 
1557
1623
  export function getReservesActive(reserves: Map<PublicKey, KaminoReserve>): Map<PublicKey, KaminoReserve> {
@@ -1564,8 +1630,11 @@ export function getReservesActive(reserves: Map<PublicKey, KaminoReserve>): Map<
1564
1630
  return reservesActive;
1565
1631
  }
1566
1632
 
1567
- export function getTokenIdsForScopeRefresh(kaminoMarket: KaminoMarket, reserves: PublicKey[]): number[] {
1568
- const tokenIds: number[] = [];
1633
+ export function getTokenIdsForScopeRefresh(
1634
+ kaminoMarket: KaminoMarket,
1635
+ reserves: PublicKey[]
1636
+ ): PubkeyHashMap<PublicKey, number[]> {
1637
+ const tokenIds = new PubkeyHashMap<PublicKey, number[]>();
1569
1638
 
1570
1639
  for (const reserveAddress of reserves) {
1571
1640
  const reserve = kaminoMarket.getReserveByAddress(reserveAddress);
@@ -1573,22 +1642,33 @@ export function getTokenIdsForScopeRefresh(kaminoMarket: KaminoMarket, reserves:
1573
1642
  throw new Error(`Reserve not found for reserve ${reserveAddress.toBase58()}`);
1574
1643
  }
1575
1644
 
1576
- if (!reserve.state.config.tokenInfo.scopeConfiguration.priceFeed.equals(PublicKey.default)) {
1645
+ const { scopeConfiguration } = reserve.state.config.tokenInfo;
1646
+ if (!scopeConfiguration.priceFeed.equals(PublicKey.default)) {
1577
1647
  let x = 0;
1578
1648
 
1579
- while (reserve.state.config.tokenInfo.scopeConfiguration.priceChain[x] !== U16_MAX) {
1580
- tokenIds.push(reserve.state.config.tokenInfo.scopeConfiguration.priceChain[x]);
1649
+ while (scopeConfiguration.priceChain[x] !== U16_MAX) {
1650
+ setOrAppend(tokenIds, scopeConfiguration.priceFeed, scopeConfiguration.priceChain[x]);
1581
1651
  x++;
1582
1652
  }
1583
1653
 
1584
1654
  x = 0;
1585
- while (reserve.state.config.tokenInfo.scopeConfiguration.twapChain[x] !== U16_MAX) {
1586
- tokenIds.push(reserve.state.config.tokenInfo.scopeConfiguration.twapChain[x]);
1655
+ while (scopeConfiguration.twapChain[x] !== U16_MAX) {
1656
+ setOrAppend(tokenIds, scopeConfiguration.priceFeed, scopeConfiguration.twapChain[x]);
1587
1657
  x++;
1588
1658
  }
1589
1659
  }
1590
1660
  }
1591
1661
 
1662
+ //TODO: remove code below
1663
+ // - currently Scope program does not allow multiple refreshPricesList instructions in 1 ix
1664
+ // - temporary fix is to only refresh one scope feed at this time
1665
+ const firstFeed = tokenIds.entries().next();
1666
+ tokenIds.clear();
1667
+ if (!firstFeed.done) {
1668
+ const [key, value] = firstFeed.value;
1669
+ tokenIds.set(key, value);
1670
+ }
1671
+
1592
1672
  return tokenIds;
1593
1673
  }
1594
1674
 
@@ -28,6 +28,7 @@ import {
28
28
  PubkeyHashMap,
29
29
  Reserve,
30
30
  UserState,
31
+ AllOracleAccounts,
31
32
  } from '../lib';
32
33
  import {
33
34
  DepositAccounts,
@@ -838,7 +839,7 @@ export class KaminoVaultClient {
838
839
  const [{ ata: adminTokenAta, createAtaIx }] = createAtasIdempotent(vaultState.vaultAdminAuthority, [
839
840
  {
840
841
  mint: vaultState.tokenMint,
841
- tokenProgram: TOKEN_PROGRAM_ID,
842
+ tokenProgram: vaultState.tokenProgram,
842
843
  },
843
844
  ]);
844
845
 
@@ -1635,7 +1636,7 @@ export class KaminoVaultClient {
1635
1636
  baseVaultAuthority: vaultState.baseVaultAuthority,
1636
1637
  tokenAta: adminTokenAta,
1637
1638
  tokenMint: vaultState.tokenMint,
1638
- tokenProgram: TOKEN_PROGRAM_ID,
1639
+ tokenProgram: vaultState.tokenProgram,
1639
1640
  /** CPI accounts */
1640
1641
  lendingMarket: marketAddress,
1641
1642
  lendingMarketAuthority: lendingMarketAuth,
@@ -2324,9 +2325,13 @@ export class KaminoVaultClient {
2324
2325
  /**
2325
2326
  * This will load the onchain state for all the reserves that the vaults have allocations for, deduplicating the reserves
2326
2327
  * @param vaults - the vault states to load reserves for
2328
+ * @param oracleAccounts
2327
2329
  * @returns a hashmap from each reserve pubkey to the reserve state
2328
2330
  */
2329
- async loadVaultsReserves(vaults: VaultState[]): Promise<PubkeyHashMap<PublicKey, KaminoReserve>> {
2331
+ async loadVaultsReserves(
2332
+ vaults: VaultState[],
2333
+ oracleAccounts?: AllOracleAccounts
2334
+ ): Promise<PubkeyHashMap<PublicKey, KaminoReserve>> {
2330
2335
  const vaultReservesAddressesSet = new PublicKeySet(vaults.flatMap((vault) => this.getVaultReserves(vault)));
2331
2336
  const vaultReservesAddresses = vaultReservesAddressesSet.toArray();
2332
2337
  const reserveAccounts = await this.getConnection().getMultipleAccountsInfo(vaultReservesAddresses, 'processed');
@@ -2343,7 +2348,7 @@ export class KaminoVaultClient {
2343
2348
  return reserveAccount;
2344
2349
  });
2345
2350
 
2346
- const reservesAndOracles = await getTokenOracleData(this.getConnection(), deserializedReserves);
2351
+ const reservesAndOracles = await getTokenOracleData(this.getConnection(), deserializedReserves, oracleAccounts);
2347
2352
 
2348
2353
  const kaminoReserves = new PubkeyHashMap<PublicKey, KaminoReserve>();
2349
2354
 
@@ -2371,13 +2376,15 @@ export class KaminoVaultClient {
2371
2376
  * @param [slot] - the slot for which to retrieve the vault collaterals for. Optional. If not provided the function will fetch the current slot
2372
2377
  * @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
2373
2378
  * @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
2379
+ * @param oracleAccounts
2374
2380
  * @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
2375
2381
  */
2376
2382
  async getVaultCollaterals(
2377
2383
  vaultState: VaultState,
2378
2384
  slot: number,
2379
2385
  vaultReservesMap?: PubkeyHashMap<PublicKey, KaminoReserve>,
2380
- kaminoMarkets?: KaminoMarket[]
2386
+ kaminoMarkets?: KaminoMarket[],
2387
+ oracleAccounts?: AllOracleAccounts
2381
2388
  ): Promise<PubkeyHashMap<PublicKey, MarketOverview>> {
2382
2389
  const vaultReservesStateMap = vaultReservesMap ? vaultReservesMap : await this.loadVaultReserves(vaultState);
2383
2390
  const vaultReservesState: KaminoReserve[] = [];
@@ -2401,7 +2408,11 @@ export class KaminoVaultClient {
2401
2408
  const missingReservesStates = (await Reserve.fetchMultiple(this.getConnection(), missingReserves.toArray())).filter(
2402
2409
  (reserve) => reserve !== null
2403
2410
  );
2404
- const missingReservesAndOracles = await getTokenOracleData(this.getConnection(), missingReservesStates);
2411
+ const missingReservesAndOracles = await getTokenOracleData(
2412
+ this.getConnection(),
2413
+ missingReservesStates,
2414
+ oracleAccounts
2415
+ );
2405
2416
  missingReservesAndOracles.forEach(([reserve, oracle], index) => {
2406
2417
  const fetchedReserve = new KaminoReserve(
2407
2418
  reserve,
package/src/client.ts CHANGED
@@ -362,7 +362,7 @@ async function deposit(connection: Connection, wallet: Keypair, token: string, d
362
362
  wallet.publicKey,
363
363
  new VanillaObligation(STAGING_LENDING_MARKET),
364
364
  true,
365
- { scope: new Scope('mainnet-beta', connection), scopeFeed: 'hubble' }
365
+ { scope: new Scope('mainnet-beta', connection), scopeConfigurations: [] }
366
366
  );
367
367
  console.log('User obligation', kaminoAction.getObligationPda().toString());
368
368
 
@@ -384,7 +384,7 @@ async function withdraw(connection: Connection, wallet: Keypair, token: string,
384
384
  wallet.publicKey,
385
385
  new VanillaObligation(new PublicKey(STAGING_LENDING_MARKET)),
386
386
  true,
387
- { scope: new Scope('mainnet-beta', connection), scopeFeed: 'hubble' }
387
+ { scope: new Scope('mainnet-beta', connection), scopeConfigurations: [] }
388
388
  );
389
389
  console.log('User obligation', kaminoAction.getObligationPda().toString());
390
390
 
@@ -406,7 +406,7 @@ async function borrow(connection: Connection, wallet: Keypair, token: string, bo
406
406
  wallet.publicKey,
407
407
  new VanillaObligation(new PublicKey(STAGING_LENDING_MARKET)),
408
408
  true,
409
- { scope: new Scope('mainnet-beta', connection), scopeFeed: 'hubble' }
409
+ { scope: new Scope('mainnet-beta', connection), scopeConfigurations: [] }
410
410
  );
411
411
  console.log('User obligation', kaminoAction.getObligationPda().toString());
412
412
 
@@ -428,7 +428,7 @@ async function repay(connection: Connection, wallet: Keypair, token: string, bor
428
428
  wallet.publicKey,
429
429
  new VanillaObligation(new PublicKey(STAGING_LENDING_MARKET)),
430
430
  true,
431
- { scope: new Scope('mainnet-beta', connection), scopeFeed: 'hubble' },
431
+ { scope: new Scope('mainnet-beta', connection), scopeConfigurations: [] },
432
432
  await connection.getSlot()
433
433
  );
434
434
  console.log('User obligation', kaminoAction.getObligationPda().toString());
@@ -52,18 +52,18 @@ import {
52
52
  TokenInfo,
53
53
  WithdrawalCaps,
54
54
  } from './idl_codegen/types';
55
- import { Fraction } from './classes/fraction';
55
+ import { Fraction } from './classes';
56
56
  import Decimal from 'decimal.js';
57
57
  import { BN } from '@coral-xyz/anchor';
58
58
  import { PythConfiguration, SwitchboardConfiguration } from './idl_codegen_kamino_vault/types';
59
59
  import * as fs from 'fs';
60
- import { MarketWithAddress } from './utils/managerTypes';
60
+ import { MarketWithAddress } from './utils';
61
61
  import {
62
62
  ManagementFeeBps,
63
63
  PendingVaultAdmin,
64
64
  PerformanceFeeBps,
65
65
  } from './idl_codegen_kamino_vault/types/VaultConfigField';
66
- import { getAccountOwner } from './utils/rpc';
66
+ import { getAccountOwner } from './utils';
67
67
  import { getAssociatedTokenAddressSync } from '@solana/spl-token';
68
68
 
69
69
  dotenv.config({
@@ -157,12 +157,12 @@ async function main() {
157
157
 
158
158
  console.log('reserve: ', reserve.publicKey);
159
159
 
160
- const _createReserveSig = await processTxn(env.client, env.payer, txnIxs[0], mode, 2500, [reserve]);
160
+ await processTxn(env.client, env.payer, txnIxs[0], mode, 2500, [reserve]);
161
161
 
162
162
  const [lut, createLutIxs] = await createUpdateReserveConfigLutIxs(env, marketAddress, reserve.publicKey);
163
163
  await processTxn(env.client, env.payer, createLutIxs, mode, 2500, []);
164
164
 
165
- const _updateSig = await processTxn(env.client, env.payer, txnIxs[1], mode, 2500, [], 400_000, 1000, [lut]);
165
+ await processTxn(env.client, env.payer, txnIxs[1], mode, 2500, [], 400_000, 1000, [lut]);
166
166
 
167
167
  mode === 'execute' &&
168
168
  console.log(
@@ -224,7 +224,7 @@ async function main() {
224
224
  updateEntireConfig
225
225
  );
226
226
 
227
- const _updateReserveSig = await processTxn(env.client, env.payer, ixs, mode, 2500, [], 400_000);
227
+ await processTxn(env.client, env.payer, ixs, mode, 2500, [], 400_000);
228
228
 
229
229
  mode === 'execute' && console.log('Reserve Updated with config -> ', JSON.parse(JSON.stringify(reserveConfig)));
230
230
  });
@@ -296,7 +296,7 @@ async function main() {
296
296
 
297
297
  const { vault: vaultKp, initVaultIxs: instructions } = await kaminoManager.createVaultIxs(kaminoVaultConfig);
298
298
 
299
- const _createVaultSig = await processTxn(
299
+ await processTxn(
300
300
  env.client,
301
301
  env.payer,
302
302
  [...instructions.createAtaIfNeededIxs, ...instructions.initVaultIxs, instructions.createLUTIx],
@@ -305,7 +305,7 @@ async function main() {
305
305
  [vaultKp]
306
306
  );
307
307
  await sleep(2000);
308
- const _populateLUTSig = await processTxn(
308
+ await processTxn(
309
309
  env.client,
310
310
  env.payer,
311
311
  [...instructions.populateLUTIxs, ...instructions.cleanupIxs],
@@ -314,14 +314,7 @@ async function main() {
314
314
  []
315
315
  );
316
316
 
317
- const _setSharesMetadataSig = await processTxn(
318
- env.client,
319
- env.payer,
320
- [instructions.initSharesMetadataIx],
321
- mode,
322
- 2500,
323
- []
324
- );
317
+ await processTxn(env.client, env.payer, [instructions.initSharesMetadataIx], mode, 2500, []);
325
318
  mode === 'execute' && console.log('Vault created:', vaultKp.publicKey.toBase58());
326
319
  });
327
320
 
@@ -1173,19 +1166,33 @@ async function main() {
1173
1166
  }
1174
1167
  });
1175
1168
 
1176
- commands.command('get-oracle-mappings').action(async () => {
1177
- const env = initializeClient(false, false);
1178
- const kaminoManager = new KaminoManager(
1179
- env.connection,
1180
- DEFAULT_RECENT_SLOT_DURATION_MS,
1181
- env.kLendProgramId,
1182
- env.kVaultProgramId
1183
- );
1169
+ commands
1170
+ .command('get-oracle-mappings')
1171
+ .requiredOption('--lending-market <string>', 'Lending Market Address')
1172
+ .action(async ({ lendingMarket }) => {
1173
+ const env = initializeClient(false, false);
1174
+ const kaminoManager = new KaminoManager(
1175
+ env.connection,
1176
+ DEFAULT_RECENT_SLOT_DURATION_MS,
1177
+ env.kLendProgramId,
1178
+ env.kVaultProgramId
1179
+ );
1180
+ const market = await KaminoMarket.load(
1181
+ env.connection,
1182
+ new PublicKey(lendingMarket),
1183
+ DEFAULT_RECENT_SLOT_DURATION_MS,
1184
+ env.kLendProgramId
1185
+ );
1186
+ if (!market) {
1187
+ throw Error(`Lending market ${lendingMarket} not found`);
1188
+ }
1184
1189
 
1185
- console.log('Getting oracle mappings');
1186
- const oracleConfigs = await kaminoManager.getScopeOracleConfigs();
1187
- console.log('oracleConfigs', JSON.parse(JSON.stringify(oracleConfigs)));
1188
- });
1190
+ console.log('Getting oracle mappings');
1191
+ const oracleConfigs = await kaminoManager.getScopeOracleConfigs(market);
1192
+ for (const [oraclePrices, configs] of oracleConfigs.entries()) {
1193
+ console.log(`OraclePrices pubkey: ${oraclePrices.toString()}`, 'Configs:', JSON.parse(JSON.stringify(configs)));
1194
+ }
1195
+ });
1189
1196
 
1190
1197
  commands.command('get-all-vaults').action(async () => {
1191
1198
  const env = initializeClient(false, false);
@@ -1370,7 +1377,7 @@ async function main() {
1370
1377
  // executing 6 ixs in a txn to make sure they fit
1371
1378
  for (let ixnIndex = 0; ixnIndex < ixs.length; ixnIndex += 6) {
1372
1379
  const ixnToExecute = ixs.slice(ixnIndex, ixnIndex + 6);
1373
- const _updateLendingMarketSig = await processTxn(env.client, env.payer, ixnToExecute, mode, 2500, [], 400_000);
1380
+ await processTxn(env.client, env.payer, ixnToExecute, mode, 2500, [], 400_000);
1374
1381
  }
1375
1382
 
1376
1383
  mode === 'execute' &&
@@ -1410,7 +1417,7 @@ async function main() {
1410
1417
 
1411
1418
  const ix = kaminoManager.updateLendingMarketOwnerIxs(marketWithAddress);
1412
1419
 
1413
- const _updateLendingMarketSig = await processTxn(env.client, env.payer, [ix], mode, 2500, [], 400_000);
1420
+ await processTxn(env.client, env.payer, [ix], mode, 2500, [], 400_000);
1414
1421
 
1415
1422
  mode === 'execute' &&
1416
1423
  console.log(
@@ -1465,7 +1472,7 @@ async function main() {
1465
1472
 
1466
1473
  const ixs = kaminoManager.updateLendingMarketIxs(marketWithAddress, newLendingMarket);
1467
1474
 
1468
- const _updateLendingMarketSig = await processTxn(env.client, env.payer, ixs, mode, 2500, [], 400_000);
1475
+ await processTxn(env.client, env.payer, ixs, mode, 2500, [], 400_000);
1469
1476
 
1470
1477
  mode === 'execute' &&
1471
1478
  console.log(
@@ -1520,7 +1527,7 @@ async function main() {
1520
1527
 
1521
1528
  const ixs = await kaminoManager.updateReserveIxs(marketWithAddress, reserveAddress, newReserveConfig);
1522
1529
 
1523
- const _updateLendingMarketSig = await processTxn(env.client, env.payer, ixs, mode, 2500, [], 400_000);
1530
+ await processTxn(env.client, env.payer, ixs, mode, 2500, [], 400_000);
1524
1531
 
1525
1532
  mode === 'execute' &&
1526
1533
  console.log(
@@ -1811,9 +1818,7 @@ function parseBorrowRateCurve(reserveConfigFromFile: any): BorrowRateCurve {
1811
1818
 
1812
1819
  curvePoints.forEach((curvePoint, index) => (finalCurvePoints[index] = curvePoint));
1813
1820
 
1814
- const borrowRateCurve = new BorrowRateCurve({ points: finalCurvePoints });
1815
-
1816
- return borrowRateCurve;
1821
+ return new BorrowRateCurve({ points: finalCurvePoints });
1817
1822
  }
1818
1823
 
1819
1824
  function parseReserveBorrowLimitAgainstCollInEmode(reserveConfigFromFile: any): BN[] {