@scallop-io/sui-scallop-sdk 2.0.13-merge-split-ve-sca-alpha.5 → 2.1.0-alpha.1

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 (55) hide show
  1. package/dist/index.d.mts +622 -697
  2. package/dist/index.d.ts +622 -697
  3. package/dist/index.js +32 -33
  4. package/dist/index.mjs +10 -10
  5. package/package.json +8 -18
  6. package/src/builders/borrowIncentiveBuilder.ts +9 -21
  7. package/src/builders/coreBuilder.ts +2 -2
  8. package/src/builders/index.ts +2 -2
  9. package/src/builders/oracles/index.ts +2 -3
  10. package/src/builders/oracles/pyth.ts +2 -2
  11. package/src/builders/spoolBuilder.ts +2 -2
  12. package/src/builders/vescaBuilder.ts +14 -132
  13. package/src/constants/queryKeys.ts +29 -54
  14. package/src/constants/testAddress.ts +6 -12
  15. package/src/index.ts +11 -1
  16. package/src/models/index.ts +11 -9
  17. package/src/models/interface.ts +36 -0
  18. package/src/models/scallop.ts +38 -133
  19. package/src/models/scallopAddress.ts +127 -142
  20. package/src/models/scallopAxios.ts +185 -0
  21. package/src/models/scallopBuilder.ts +45 -75
  22. package/src/models/scallopClient.ts +124 -154
  23. package/src/models/scallopConstants.ts +248 -323
  24. package/src/models/scallopIndexer.ts +54 -98
  25. package/src/models/scallopQuery.ts +145 -190
  26. package/src/models/scallopQueryClient.ts +29 -0
  27. package/src/models/scallopSuiKit.ts +432 -0
  28. package/src/models/scallopUtils.ts +260 -164
  29. package/src/queries/borrowIncentiveQuery.ts +28 -16
  30. package/src/queries/borrowLimitQuery.ts +1 -1
  31. package/src/queries/coreQuery.ts +148 -107
  32. package/src/queries/flashloanFeeQuery.ts +12 -6
  33. package/src/queries/index.ts +0 -1
  34. package/src/queries/isolatedAssetQuery.ts +3 -3
  35. package/src/queries/loyaltyProgramQuery.ts +10 -8
  36. package/src/queries/ownerQuery.ts +32 -0
  37. package/src/queries/portfolioQuery.ts +4 -5
  38. package/src/queries/priceQuery.ts +14 -8
  39. package/src/queries/referralQuery.ts +9 -3
  40. package/src/queries/sCoinQuery.ts +4 -4
  41. package/src/queries/spoolQuery.ts +11 -11
  42. package/src/queries/supplyLimitQuery.ts +1 -1
  43. package/src/queries/switchboardQuery.ts +1 -1
  44. package/src/queries/vescaQuery.ts +31 -27
  45. package/src/queries/xOracleQuery.ts +13 -8
  46. package/src/types/address.ts +0 -3
  47. package/src/types/builder/core.ts +1 -1
  48. package/src/types/builder/vesca.ts +10 -20
  49. package/src/types/constant/queryKeys.ts +48 -0
  50. package/src/types/index.ts +0 -1
  51. package/src/utils/builder.ts +1 -1
  52. package/src/utils/util.ts +1 -33
  53. package/src/models/scallopCache.ts +0 -428
  54. package/src/queries/objectsQuery.ts +0 -18
  55. package/src/types/model.ts +0 -117
@@ -1,121 +1,98 @@
1
1
  import {
2
- SUI_TYPE_ARG,
3
2
  normalizeStructTag,
4
3
  parseStructTag,
5
- } from '@mysten/sui/utils';
6
- import { SuiKit } from '@scallop-io/sui-kit';
7
- import { SuiPriceServiceConnection } from '@pythnetwork/pyth-sui-js';
8
- import { ScallopAddress } from './scallopAddress';
9
- import { UNLOCK_ROUND_DURATION, MAX_LOCK_DURATION } from '../constants';
10
- import { getPythPrices, queryObligation } from '../queries';
11
- import { parseDataFromPythPriceFeed, findClosestUnlockRound } from '../utils';
12
- import { ScallopCache } from './scallopCache';
13
- import type {
14
- ScallopUtilsParams,
15
- CoinPrices,
16
- CoinWrappedType,
17
- ScallopUtilsInstanceParams,
18
- PoolAddress,
19
- } from '../types';
20
- import { queryKeys } from 'src/constants';
21
- import type { SuiObjectArg, SuiTxBlock } from '@scallop-io/sui-kit';
22
- import { newSuiKit } from './suiKit';
23
- import { ScallopConstants } from './scallopConstants';
24
-
25
- const PYTH_ENDPOINTS: {
26
- [k in 'mainnet']: string[];
27
- } = {
28
- mainnet: ['https://hermes.pyth.network', 'https://scallop.rpc.p2p.world'],
29
- };
30
-
31
- /**
32
- * @description
33
- * Integrates some helper functions frequently used in interactions with the Scallop contract.
34
- *
35
- * @example
36
- * ```typescript
37
- * const scallopUtils = new ScallopUtils(<parameters>);
38
- * await scallopUtils.init();
39
- * scallopUtils.<utils functions>();
40
- * await scallopUtils.<utils functions>();
41
- * ```
42
- */
43
- export class ScallopUtils {
44
- public readonly params: ScallopUtilsParams;
45
-
46
- public suiKit: SuiKit;
47
- public address: ScallopAddress;
48
- public cache: ScallopCache;
49
- public constants: ScallopConstants;
50
- public walletAddress: string;
51
-
52
- public constructor(
53
- params: ScallopUtilsParams,
54
- instance?: ScallopUtilsInstanceParams
55
- ) {
56
- this.params = {
57
- pythEndpoints: params.pythEndpoints ?? PYTH_ENDPOINTS['mainnet'],
58
- ...params,
59
- };
60
- this.walletAddress =
61
- params.walletAddress ?? instance?.suiKit?.currentAddress() ?? '';
62
-
63
- this.suiKit =
64
- instance?.suiKit ??
65
- instance?.constants?.cache.suiKit ??
66
- newSuiKit(params);
67
-
68
- this.cache =
69
- instance?.constants?.cache ??
70
- instance?.cache ??
71
- new ScallopCache(this.params, {
72
- suiKit: this.suiKit,
4
+ SUI_TYPE_ARG,
5
+ SuiObjectArg,
6
+ SuiTxBlock,
7
+ Transaction,
8
+ } from '@scallop-io/sui-kit';
9
+ import ScallopConstants, { ScallopConstantsParams } from './scallopConstants';
10
+ import { CoinPrices, CoinWrappedType, PoolAddress } from 'src/types';
11
+ import { findClosestUnlockRound } from 'src/utils';
12
+ import {
13
+ MAX_LOCK_DURATION,
14
+ queryKeys,
15
+ UNLOCK_ROUND_DURATION,
16
+ } from 'src/constants';
17
+ import { PriceFeed, SuiPriceServiceConnection } from '@pythnetwork/pyth-sui-js';
18
+ import ScallopSuiKit, { ScallopSuiKitParams } from './scallopSuiKit';
19
+ import { SuiObjectData } from '@mysten/sui/dist/cjs/client';
20
+ import { queryObligation } from 'src/queries';
21
+ import { ScallopUtilsInterface } from './interface';
22
+
23
+ export type ScallopUtilsParams = {
24
+ pythEndpoints?: string[];
25
+ scallopSuiKit?: ScallopSuiKit;
26
+ scallopConstants?: ScallopConstants;
27
+ } & ScallopSuiKitParams &
28
+ ScallopConstantsParams;
29
+
30
+ class ScallopUtils implements ScallopUtilsInterface {
31
+ public pythEndpoints: string[];
32
+ public readonly scallopSuiKit: ScallopSuiKit;
33
+ public readonly constants: ScallopConstants;
34
+
35
+ constructor(params: ScallopUtilsParams = {}) {
36
+ this.constants = params.scallopConstants ?? new ScallopConstants(params);
37
+ this.scallopSuiKit =
38
+ params.scallopSuiKit ??
39
+ new ScallopSuiKit({
40
+ queryClient: this.constants.queryClient,
41
+ ...params,
73
42
  });
74
43
 
75
- this.address =
76
- instance?.constants?.address ??
77
- new ScallopAddress(this.params, {
78
- cache: this.cache,
79
- });
44
+ this.pythEndpoints = params.pythEndpoints ?? [
45
+ 'https://hermes.pyth.network',
46
+ 'https://scallop.rpc.p2p.world',
47
+ ];
48
+ }
80
49
 
81
- this.constants =
82
- instance?.constants ??
83
- new ScallopConstants(this.params, {
84
- address: this.address,
85
- });
50
+ get walletAddress() {
51
+ return this.scallopSuiKit.walletAddress;
86
52
  }
87
53
 
88
- get whitelist() {
89
- return this.constants.whitelist;
54
+ get suiKit() {
55
+ return this.scallopSuiKit.suiKit;
56
+ }
57
+
58
+ get queryClient() {
59
+ return this.constants.queryClient;
60
+ }
61
+
62
+ // For backward compatibility with older sdk version
63
+ get address() {
64
+ return this.constants;
90
65
  }
91
66
 
92
67
  // -------------- TYPE GUARDS --------------
93
- public isSuiBridgeAsset(coinName: any) {
68
+ isSuiBridgeAsset(coinName: any) {
94
69
  return this.constants.whitelist.suiBridge.has(coinName);
95
70
  }
96
71
 
97
- public isWormholeAsset(coinName: any) {
72
+ isWormholeAsset(coinName: any) {
98
73
  return this.constants.whitelist.wormhole.has(coinName);
99
74
  }
100
75
 
101
- public isMarketCoin(coinName: string) {
76
+ isMarketCoin(coinName: string) {
102
77
  const assetCoinName = coinName.slice(1).toLowerCase() as string;
103
78
  return (
104
79
  coinName.charAt(0).toLowerCase() === 's' &&
105
- this.whitelist.lending.has(assetCoinName)
80
+ this.constants.whitelist.lending.has(assetCoinName)
106
81
  );
107
82
  }
108
83
 
84
+ async init({ force = false }: { force?: boolean } = {}) {
85
+ await this.constants.init({ force });
86
+ }
87
+
109
88
  /**
110
- * Request the scallop API to initialize data.
89
+ * Convert market coin name to coin name.
111
90
  *
112
- * @param force - Whether to force initialization.
113
- * @param address - ScallopAddress instance.
91
+ * @param marketCoinName - Specific support market coin name.
92
+ * @return Coin Name.
114
93
  */
115
- public async init(force: boolean = false) {
116
- if (force || !this.constants.isInitialized) {
117
- await this.constants.init();
118
- }
94
+ parseCoinName<T extends string>(marketCoinName: string) {
95
+ return marketCoinName.slice(1) as T;
119
96
  }
120
97
 
121
98
  /**
@@ -124,7 +101,7 @@ export class ScallopUtils {
124
101
  * @param coinName - Specific support coin name.
125
102
  * @return Symbol string.
126
103
  */
127
- public parseSymbol(coinName: string) {
104
+ parseSymbol(coinName: string) {
128
105
  return this.isMarketCoin(coinName)
129
106
  ? (this.constants.poolAddresses[this.parseCoinName(coinName)]
130
107
  ?.sCoinSymbol ?? '')
@@ -142,7 +119,7 @@ export class ScallopUtils {
142
119
  * @param coinName - Specific support coin name.
143
120
  * @return Coin type.
144
121
  */
145
- public parseCoinType(coinName: string, useOldMarketCoin: boolean = false) {
122
+ parseCoinType(coinName: string, useOldMarketCoin: boolean = false) {
146
123
  if (useOldMarketCoin) {
147
124
  return this.constants.coinNameToOldMarketCoinTypeMap[coinName] ?? '';
148
125
  }
@@ -155,13 +132,16 @@ export class ScallopUtils {
155
132
  * @param coinName - Specific support coin name.
156
133
  * @return sCoin name.
157
134
  */
158
- public parseSCoinName<T extends string>(coinName: string) {
135
+ parseSCoinName<T extends string>(coinName: string) {
159
136
  // need more check because swapt has no sCoin type
160
- if (this.isMarketCoin(coinName) && this.whitelist.scoin.has(coinName)) {
137
+ if (
138
+ this.isMarketCoin(coinName) &&
139
+ this.constants.whitelist.scoin.has(coinName)
140
+ ) {
161
141
  return coinName as T;
162
142
  } else {
163
143
  const marketCoinName = `s${coinName}`;
164
- if (this.whitelist.scoin.has(marketCoinName)) {
144
+ if (this.constants.whitelist.scoin.has(marketCoinName)) {
165
145
  return marketCoinName as T;
166
146
  }
167
147
  return undefined;
@@ -175,8 +155,8 @@ export class ScallopUtils {
175
155
  * if no `scallop_...` is encountered, return coinName
176
156
  * @return sCoin name
177
157
  */
178
- public parseSCoinTypeNameToMarketCoinName(coinName: string) {
179
- return this.constants.sCoinRawNameToScoinNameMap[coinName] ?? coinName;
158
+ parseSCoinTypeNameToMarketCoinName(coinName: string) {
159
+ return this.constants.scoinRawNameToSCoinNameMap[coinName] ?? coinName;
180
160
  }
181
161
 
182
162
  /**
@@ -184,7 +164,7 @@ export class ScallopUtils {
184
164
  * @param sCoinName
185
165
  * @returns sCoin type
186
166
  */
187
- public parseSCoinType(sCoinName: string) {
167
+ parseSCoinType(sCoinName: string) {
188
168
  return this.constants.sCoinTypes[sCoinName] ?? '';
189
169
  }
190
170
 
@@ -193,8 +173,8 @@ export class ScallopUtils {
193
173
  * @param sCoinType
194
174
  * @returns sCoin name
195
175
  */
196
- public parseSCoinNameFromType(sCoinType: string) {
197
- return this.constants.sCoinTypeToSCoinNameMap[sCoinType];
176
+ parseSCoinNameFromType(sCoinType: string) {
177
+ return this.constants.scoinTypeToSCoinNameMap[sCoinType];
198
178
  }
199
179
 
200
180
  /**
@@ -202,7 +182,7 @@ export class ScallopUtils {
202
182
  * @param sCoinName
203
183
  * @returns coin type
204
184
  */
205
- public parseUnderlyingSCoinType(sCoinName: string) {
185
+ parseUnderlyingSCoinType(sCoinName: string) {
206
186
  const coinName = this.parseCoinName(sCoinName);
207
187
  return this.parseCoinType(coinName);
208
188
  }
@@ -212,7 +192,7 @@ export class ScallopUtils {
212
192
  * @param sCoinName
213
193
  * @returns sCoin treasury id
214
194
  */
215
- public getSCoinTreasury(sCoinName: string) {
195
+ getSCoinTreasury(sCoinName: string) {
216
196
  return this.address.get(`scoin.coins.${sCoinName}.treasury`);
217
197
  }
218
198
 
@@ -223,7 +203,7 @@ export class ScallopUtils {
223
203
  * @param coinName - Specific support coin name.
224
204
  * @return Market coin type.
225
205
  */
226
- public parseMarketCoinType(coinName: string) {
206
+ parseMarketCoinType(coinName: string) {
227
207
  const coinType = this.parseCoinType(
228
208
  this.isMarketCoin(coinName) ? this.parseCoinName(coinName) : coinName,
229
209
  true
@@ -231,57 +211,13 @@ export class ScallopUtils {
231
211
  return coinType;
232
212
  }
233
213
 
234
- /**
235
- * Convert coin type to coin name.
236
- *
237
- * @description
238
- * The coin name cannot be obtained directly from the wormhole type. Here
239
- * the package id is used to determine and return a specific name.
240
- *
241
- * @param coinType - Specific support coin type.
242
- * @return Coin Name.
243
- */
244
- public parseCoinNameFromType(coinType: string) {
245
- coinType = normalizeStructTag(coinType);
246
- const { address, module, name, typeParams } = parseStructTag(coinType);
247
- const isMarketCoinType =
248
- address === this.constants.protocolObjectId &&
249
- module === 'reserve' &&
250
- name === 'MarketCoin';
251
-
252
- if (isMarketCoinType) {
253
- return this.parseMarketCoinName(
254
- (typeof typeParams[0] === 'string'
255
- ? parseStructTag(typeParams[0])
256
- : typeParams[0]
257
- ).name.toLowerCase()
258
- );
259
- }
260
- const assetCoinName =
261
- this.constants.coinTypeToCoinNameMap[coinType] ||
262
- this.constants.sCoinTypeToSCoinNameMap[coinType] ||
263
- parseStructTag(coinType).name.toLowerCase();
264
-
265
- return assetCoinName;
266
- }
267
-
268
- /**
269
- * Convert market coin name to coin name.
270
- *
271
- * @param marketCoinName - Specific support market coin name.
272
- * @return Coin Name.
273
- */
274
- public parseCoinName<T extends string>(marketCoinName: string) {
275
- return marketCoinName.slice(1) as T;
276
- }
277
-
278
214
  /**
279
215
  * Convert coin name to market coin name.
280
216
  *
281
217
  * @param coinName - Specific support coin name.
282
218
  * @return Market coin name.
283
219
  */
284
- public parseMarketCoinName<T extends string>(coinName: string) {
220
+ parseMarketCoinName<T extends string>(coinName: string) {
285
221
  return `s${coinName}` as T;
286
222
  }
287
223
 
@@ -291,7 +227,7 @@ export class ScallopUtils {
291
227
  * @param stakeMarketCoinName - Support stake market coin.
292
228
  * @return Spool reward coin name.
293
229
  */
294
- public getSpoolRewardCoinName = () => {
230
+ getSpoolRewardCoinName = () => {
295
231
  return 'sui'; // No further plan to incentivize other spools
296
232
  };
297
233
 
@@ -300,7 +236,7 @@ export class ScallopUtils {
300
236
  *
301
237
  * return Coin decimal.
302
238
  */
303
- public getCoinDecimal(coinName: string) {
239
+ getCoinDecimal(coinName: string) {
304
240
  return this.constants.coinDecimals[coinName] ?? 0;
305
241
  }
306
242
 
@@ -309,7 +245,7 @@ export class ScallopUtils {
309
245
  *
310
246
  * return Coin wrapped type.
311
247
  */
312
- public getCoinWrappedType(assetCoinName: string): CoinWrappedType {
248
+ getCoinWrappedType(assetCoinName: string): CoinWrappedType {
313
249
  if (this.isSuiBridgeAsset(assetCoinName)) {
314
250
  return {
315
251
  from: 'Sui Bridge',
@@ -325,6 +261,40 @@ export class ScallopUtils {
325
261
  return undefined;
326
262
  }
327
263
 
264
+ /**
265
+ * Convert coin type to coin name.
266
+ *
267
+ * @description
268
+ * The coin name cannot be obtained directly from the wormhole type. Here
269
+ * the package id is used to determine and return a specific name.
270
+ *
271
+ * @param coinType - Specific support coin type.
272
+ * @return Coin Name.
273
+ */
274
+ public parseCoinNameFromType(coinType: string) {
275
+ coinType = normalizeStructTag(coinType);
276
+ const { address, module, name, typeParams } = parseStructTag(coinType);
277
+ const isMarketCoinType =
278
+ address === this.constants.protocolObjectId &&
279
+ module === 'reserve' &&
280
+ name === 'MarketCoin';
281
+
282
+ if (isMarketCoinType) {
283
+ return this.parseMarketCoinName(
284
+ (typeof typeParams[0] === 'string'
285
+ ? parseStructTag(typeParams[0])
286
+ : typeParams[0]
287
+ ).name.toLowerCase()
288
+ );
289
+ }
290
+ const assetCoinName =
291
+ this.constants.coinTypeToCoinNameMap[coinType] ||
292
+ this.constants.scoinTypeToSCoinNameMap[coinType] ||
293
+ parseStructTag(coinType).name.toLowerCase();
294
+
295
+ return assetCoinName;
296
+ }
297
+
328
298
  /**
329
299
  * Select coin id that add up to the given amount as transaction arguments.
330
300
  *
@@ -333,7 +303,7 @@ export class ScallopUtils {
333
303
  * @param coinType - The coin type, default is 0x2::SUI::SUI.
334
304
  * @return The selected transaction coin arguments.
335
305
  */
336
- public async selectCoins(
306
+ async selectCoins(
337
307
  amount: number,
338
308
  coinType: string = SUI_TYPE_ARG,
339
309
  ownerAddress?: string
@@ -354,8 +324,8 @@ export class ScallopUtils {
354
324
  * @param coinType
355
325
  * @param sender
356
326
  */
357
- public async mergeSimilarCoins(
358
- txBlock: SuiTxBlock,
327
+ async mergeSimilarCoins(
328
+ txBlock: SuiTxBlock | Transaction,
359
329
  dest: SuiObjectArg,
360
330
  coinType: string,
361
331
  sender: string = this.walletAddress
@@ -369,7 +339,10 @@ export class ScallopUtils {
369
339
  );
370
340
 
371
341
  if (existingCoins.length > 0) {
372
- txBlock.mergeCoins(dest, existingCoins.slice(0, 500));
342
+ txBlock.mergeCoins(
343
+ dest as any,
344
+ existingCoins.slice(0, 500).map(txBlock.objectRef) as any
345
+ );
373
346
  }
374
347
  } catch (_e) {
375
348
  // ignore
@@ -386,7 +359,7 @@ export class ScallopUtils {
386
359
  * @param obligationId - The obligation id.
387
360
  * @return Asset coin Names.
388
361
  */
389
- public async getObligationCoinNames(obligationId: SuiObjectArg) {
362
+ async getObligationCoinNames(obligationId: SuiObjectArg) {
390
363
  const obligation = await queryObligation(this, obligationId);
391
364
  if (!obligation) return undefined;
392
365
 
@@ -405,6 +378,128 @@ export class ScallopUtils {
405
378
  return obligationCoinNames;
406
379
  }
407
380
 
381
+ private parseDataFromPythPriceFeed(feed: PriceFeed) {
382
+ const assetCoinNames = [...this.constants.whitelist.lending] as string[];
383
+ const assetCoinName = assetCoinNames.find((assetCoinName) => {
384
+ return (
385
+ this.address.get(`core.coins.${assetCoinName}.oracle.pyth.feed`) ===
386
+ feed.id
387
+ );
388
+ });
389
+
390
+ if (assetCoinName) {
391
+ const parsedPrice = feed.getEmaPriceUnchecked();
392
+
393
+ return {
394
+ coinName: assetCoinName,
395
+ price: parsedPrice.getPriceAsNumberUnchecked(),
396
+ publishTime: Number(parsedPrice.publishTime) * 10 ** 3,
397
+ };
398
+ } else {
399
+ throw new Error(`Invalid feed id: ${feed.id}`);
400
+ }
401
+ }
402
+
403
+ async getPythPrice(
404
+ assetCoinName: string,
405
+ priceFeedObject?: SuiObjectData | null
406
+ ) {
407
+ const pythFeedObjectId = this.address.get(
408
+ `core.coins.${assetCoinName}.oracle.pyth.feedObject`
409
+ );
410
+ priceFeedObject =
411
+ priceFeedObject ||
412
+ (await this.scallopSuiKit.queryGetObject(pythFeedObjectId))?.data;
413
+
414
+ if (priceFeedObject) {
415
+ const priceFeedPoolObject = priceFeedObject;
416
+ if (
417
+ priceFeedPoolObject.content &&
418
+ 'fields' in priceFeedPoolObject.content
419
+ ) {
420
+ const fields = priceFeedPoolObject.content.fields as any;
421
+ const expoMagnitude = Number(
422
+ fields.price_info.fields.price_feed.fields.price.fields.expo.fields
423
+ .magnitude
424
+ );
425
+ const expoNegative = Number(
426
+ fields.price_info.fields.price_feed.fields.price.fields.expo.fields
427
+ .negative
428
+ );
429
+ const priceMagnitude = Number(
430
+ fields.price_info.fields.price_feed.fields.price.fields.price.fields
431
+ .magnitude
432
+ );
433
+ const priceNegative = Number(
434
+ fields.price_info.fields.price_feed.fields.price.fields.price.fields
435
+ .negative
436
+ );
437
+
438
+ return (
439
+ priceMagnitude *
440
+ 10 ** ((expoNegative ? -1 : 1) * expoMagnitude) *
441
+ (priceNegative ? -1 : 1)
442
+ );
443
+ }
444
+ }
445
+
446
+ return 0;
447
+ }
448
+
449
+ async getPythPrices(assetCoinNames: string[]) {
450
+ const pythPriceFeedIds = assetCoinNames.reduce(
451
+ (prev, assetCoinName) => {
452
+ const pythPriceFeed = this.address.get(
453
+ `core.coins.${assetCoinName}.oracle.pyth.feedObject`
454
+ );
455
+ if (pythPriceFeed) {
456
+ if (!prev[pythPriceFeed]) {
457
+ prev[pythPriceFeed] = [assetCoinName];
458
+ } else {
459
+ prev[pythPriceFeed].push(assetCoinName);
460
+ }
461
+ }
462
+ return prev;
463
+ },
464
+ {} as Record<string, string[]>
465
+ );
466
+
467
+ // Fetch multiple objects at once to save rpc calls
468
+ const priceFeedObjects = await this.scallopSuiKit.queryGetObjects(
469
+ Object.keys(pythPriceFeedIds)
470
+ );
471
+
472
+ const assetToPriceFeedMapping = priceFeedObjects.reduce(
473
+ (prev, priceFeedObject) => {
474
+ pythPriceFeedIds[priceFeedObject.objectId].forEach((assetCoinName) => {
475
+ prev[assetCoinName] = priceFeedObject;
476
+ });
477
+ return prev;
478
+ },
479
+ {} as Record<string, SuiObjectData>
480
+ );
481
+
482
+ return (
483
+ await Promise.all(
484
+ Object.entries(assetToPriceFeedMapping).map(
485
+ async ([assetCoinName, priceFeedObject]) => ({
486
+ coinName: assetCoinName,
487
+ price: await this.getPythPrice(
488
+ assetCoinName as string,
489
+ priceFeedObject
490
+ ),
491
+ })
492
+ )
493
+ )
494
+ ).reduce(
495
+ (prev, curr) => {
496
+ prev[curr.coinName as string] = curr.price;
497
+ return prev;
498
+ },
499
+ {} as Record<string, number>
500
+ );
501
+ }
502
+
408
503
  /**
409
504
  * Get asset coin price.
410
505
  *
@@ -417,7 +512,7 @@ export class ScallopUtils {
417
512
  * @param assetCoinNames - Specific an array of support asset coin name.
418
513
  * @return Asset coin price.
419
514
  */
420
- public async getCoinPrices(
515
+ async getCoinPrices(
421
516
  coinNames: string[] = [
422
517
  ...new Set([
423
518
  ...this.constants.whitelist.lending,
@@ -427,8 +522,7 @@ export class ScallopUtils {
427
522
  ) {
428
523
  let coinPrices: CoinPrices = {};
429
524
 
430
- const endpoints = this.params.pythEndpoints ?? PYTH_ENDPOINTS['mainnet'];
431
-
525
+ const endpoints = this.pythEndpoints;
432
526
  const failedRequests: Set<string> = new Set(coinNames);
433
527
 
434
528
  for (const endpoint of endpoints) {
@@ -450,7 +544,7 @@ export class ScallopUtils {
450
544
  });
451
545
 
452
546
  try {
453
- const feeds = await this.cache.queryClient.fetchQuery({
547
+ const feeds = await this.queryClient.fetchQuery({
454
548
  queryKey: queryKeys.oracle.getPythLatestPriceFeeds(),
455
549
  queryFn: async () => {
456
550
  return await pythConnection.getLatestPriceFeeds(priceIds);
@@ -461,7 +555,7 @@ export class ScallopUtils {
461
555
  if (feeds) {
462
556
  feeds.forEach((feed, idx) => {
463
557
  const coinName = priceIdPairs[idx][0] as string;
464
- const data = parseDataFromPythPriceFeed(feed, this.constants);
558
+ const data = this.parseDataFromPythPriceFeed(feed);
465
559
  coinPrices[coinName as string] = data.price;
466
560
  failedRequests.delete(coinName as string); // remove success price feed to prevent duplicate request on the next endpoint
467
561
  });
@@ -475,7 +569,7 @@ export class ScallopUtils {
475
569
  if (failedRequests.size > 0) {
476
570
  coinPrices = {
477
571
  ...coinPrices,
478
- ...(await getPythPrices(this, Array.from(failedRequests.values()))),
572
+ ...(await this.getPythPrices(Array.from(failedRequests.values()))),
479
573
  };
480
574
  failedRequests.clear();
481
575
  }
@@ -561,3 +655,5 @@ export class ScallopUtils {
561
655
  : [];
562
656
  }
563
657
  }
658
+
659
+ export default ScallopUtils;