@scallop-io/sui-scallop-sdk 1.5.3 → 2.0.0-alpha.10

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 (66) hide show
  1. package/dist/index.d.mts +509 -602
  2. package/dist/index.d.ts +509 -602
  3. package/dist/index.js +28 -59
  4. package/dist/index.mjs +7 -7
  5. package/package.json +1 -1
  6. package/src/builders/coreBuilder.ts +33 -33
  7. package/src/builders/loyaltyProgramBuilder.ts +5 -3
  8. package/src/builders/{oracle.ts → oracles/index.ts} +48 -60
  9. package/src/builders/oracles/pyth.ts +44 -0
  10. package/src/builders/oracles/switchboard.ts +270 -0
  11. package/src/builders/referralBuilder.ts +5 -9
  12. package/src/builders/sCoinBuilder.ts +9 -8
  13. package/src/builders/spoolBuilder.ts +4 -6
  14. package/src/constants/common.ts +114 -126
  15. package/src/constants/index.ts +0 -5
  16. package/src/constants/pyth.ts +25 -34
  17. package/src/constants/queryKeys.ts +2 -0
  18. package/src/constants/testAddress.ts +36 -487
  19. package/src/models/index.ts +1 -0
  20. package/src/models/scallop.ts +23 -19
  21. package/src/models/scallopAddress.ts +17 -5
  22. package/src/models/scallopBuilder.ts +36 -41
  23. package/src/models/scallopCache.ts +3 -3
  24. package/src/models/scallopClient.ts +93 -98
  25. package/src/models/scallopConstants.ts +399 -0
  26. package/src/models/scallopIndexer.ts +11 -24
  27. package/src/models/scallopQuery.ts +76 -79
  28. package/src/models/scallopUtils.ts +126 -250
  29. package/src/queries/borrowIncentiveQuery.ts +25 -58
  30. package/src/queries/borrowLimitQuery.ts +3 -6
  31. package/src/queries/coreQuery.ts +98 -114
  32. package/src/queries/flashloanFeeQuery.ts +86 -0
  33. package/src/queries/index.ts +1 -0
  34. package/src/queries/isolatedAssetQuery.ts +12 -11
  35. package/src/queries/poolAddressesQuery.ts +211 -117
  36. package/src/queries/portfolioQuery.ts +60 -70
  37. package/src/queries/priceQuery.ts +16 -22
  38. package/src/queries/sCoinQuery.ts +15 -16
  39. package/src/queries/spoolQuery.ts +49 -59
  40. package/src/queries/supplyLimitQuery.ts +2 -6
  41. package/src/queries/switchboardQuery.ts +64 -0
  42. package/src/queries/xOracleQuery.ts +29 -32
  43. package/src/types/address.ts +21 -19
  44. package/src/types/builder/borrowIncentive.ts +2 -3
  45. package/src/types/builder/core.ts +20 -27
  46. package/src/types/builder/index.ts +1 -2
  47. package/src/types/builder/referral.ts +4 -8
  48. package/src/types/builder/sCoin.ts +4 -8
  49. package/src/types/builder/spool.ts +7 -10
  50. package/src/types/constant/common.ts +44 -49
  51. package/src/types/constant/enum.ts +15 -27
  52. package/src/types/constant/xOracle.ts +3 -2
  53. package/src/types/model.ts +49 -28
  54. package/src/types/query/borrowIncentive.ts +7 -24
  55. package/src/types/query/core.ts +8 -18
  56. package/src/types/query/portfolio.ts +9 -17
  57. package/src/types/query/spool.ts +5 -11
  58. package/src/types/utils.ts +1 -21
  59. package/src/utils/core.ts +1 -1
  60. package/src/utils/query.ts +15 -23
  61. package/src/utils/util.ts +6 -84
  62. package/src/constants/coinGecko.ts +0 -34
  63. package/src/constants/enum.ts +0 -268
  64. package/src/constants/flashloan.ts +0 -18
  65. package/src/constants/poolAddress.ts +0 -898
  66. package/src/models/scallopPrice.ts +0 -0
@@ -0,0 +1,399 @@
1
+ import axios, { AxiosInstance } from 'axios';
2
+ import { queryKeys } from 'src/constants';
3
+ import {
4
+ PoolAddress,
5
+ ScallopConstantsInstanceParams,
6
+ ScallopConstantsParams,
7
+ Whitelist,
8
+ } from 'src/types';
9
+ import { ScallopCache } from './scallopCache';
10
+ import { QueryKey } from '@tanstack/query-core';
11
+ import { newSuiKit } from './suiKit';
12
+ import { ScallopAddress } from './scallopAddress';
13
+ import { parseStructTag } from '@scallop-io/sui-kit';
14
+
15
+ /**
16
+ * @description
17
+ * It provides methods to construct constants for Scallop SDK instances.
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * const scallopConstants = new ScallopConstants();
22
+ * await scallopConstants.init();
23
+ * ```
24
+ */
25
+ export class ScallopConstants {
26
+ private readonly _requestClient: AxiosInstance;
27
+ public address: ScallopAddress;
28
+ public cache: ScallopCache;
29
+
30
+ private _poolAddresses: Record<string, PoolAddress | undefined> = {};
31
+ private _whitelist: Whitelist = {
32
+ lending: new Set(),
33
+ borrowing: new Set(),
34
+ collateral: new Set(),
35
+ packages: new Set(),
36
+ scoin: new Set(),
37
+ spool: new Set(),
38
+ borrowIncentiveRewards: new Set(),
39
+ rewardsAsPoint: new Set(),
40
+ suiBridge: new Set(),
41
+ wormhole: new Set(),
42
+ oracles: new Set(),
43
+ pythEndpoints: new Set(),
44
+ deprecated: new Set(),
45
+ };
46
+
47
+ private _coinDecimals: Record<string, number | undefined> = {};
48
+ private _coinNameToOldMarketCoinTypeMap: Record<string, string | undefined> =
49
+ {};
50
+ private _scoinRawNameToSCoinNameMap: Record<string, string | undefined> = {};
51
+ private _scoinTypeToSCoinNameMap: Record<string, string | undefined> = {};
52
+ private _wormholeCoinTypeToCoinNameMap: Record<string, string | undefined> =
53
+ {};
54
+ private _voloCoinTypeToCoinNameMap: Record<string, string | undefined> = {};
55
+ private _suiBridgeCoinTypeToCoinNameMap: Record<string, string | undefined> =
56
+ {};
57
+ private _coinTypes: Record<string, string | undefined> = {};
58
+ private _sCoinTypes: Record<string, string | undefined> = {};
59
+ private _coinTypeToCoinNameMap: Record<string, string | undefined> = {};
60
+ private _supportedBorrowIncentiveRewards: Set<string> = new Set();
61
+
62
+ constructor(
63
+ public readonly params: ScallopConstantsParams,
64
+ instance?: ScallopConstantsInstanceParams
65
+ ) {
66
+ this.params = params;
67
+ this._requestClient = axios.create({
68
+ headers: {
69
+ 'Content-Type': 'application/json',
70
+ Accept: 'application/json',
71
+ },
72
+ timeout: 8000,
73
+ });
74
+
75
+ this.cache =
76
+ instance?.address?.cache ??
77
+ instance?.cache ??
78
+ new ScallopCache(this.params, {
79
+ suiKit: newSuiKit(this.params),
80
+ });
81
+
82
+ this.address =
83
+ instance?.address ??
84
+ new ScallopAddress(this.params, {
85
+ cache: this.cache,
86
+ });
87
+
88
+ if (params.forcePoolAddressInterface) {
89
+ this._poolAddresses = params.forcePoolAddressInterface;
90
+ }
91
+
92
+ if (params.forceWhitelistInterface) {
93
+ this._whitelist = params.forceWhitelistInterface;
94
+ }
95
+ }
96
+
97
+ get isAddressInitialized() {
98
+ return !this.isEmptyObject(this.address.getAllAddresses());
99
+ }
100
+
101
+ get isInitialized() {
102
+ return (
103
+ !this.isEmptyObject(this._poolAddresses) &&
104
+ Object.values(this._whitelist).every((t) => t.size > 0) &&
105
+ this.isAddressInitialized
106
+ );
107
+ }
108
+
109
+ get queryClient() {
110
+ return this.cache.queryClient;
111
+ }
112
+
113
+ get poolAddresses() {
114
+ return this._poolAddresses;
115
+ }
116
+
117
+ get whitelist() {
118
+ return this._whitelist;
119
+ }
120
+
121
+ parseToOldMarketCoin(coinType: string) {
122
+ return `${this.protocolObjectId}::reserve::MarketCoin<${coinType}>`;
123
+ }
124
+
125
+ get protocolObjectId() {
126
+ return (
127
+ (this.address.get('core.object') as string | undefined) ??
128
+ ('0xefe8b36d5b2e43728cc323298626b83177803521d195cfb11e15b910e892fddf' as const)
129
+ );
130
+ }
131
+
132
+ /**
133
+ * @description
134
+ * Return maps of coin names to coin decimals.
135
+ */
136
+ get coinDecimals() {
137
+ if (this.isEmptyObject(this._coinDecimals)) {
138
+ this._coinDecimals = Object.fromEntries([
139
+ ...Object.entries(this.poolAddresses)
140
+ .filter(([_, value]) => !!value)
141
+ .map(([key, value]) => [key, value!.decimals]),
142
+ ...Object.entries(this.poolAddresses)
143
+ .filter(([_, value]) => !!value?.sCoinName)
144
+ .map(([_, value]) => [value!.sCoinName, value!.decimals]),
145
+ ]);
146
+ }
147
+ return this._coinDecimals;
148
+ }
149
+
150
+ /**
151
+ * @description
152
+ * Return maps of coin names to coin types.
153
+ */
154
+ get coinTypes() {
155
+ if (this.isEmptyObject(this._coinTypes))
156
+ this._coinTypes = Object.fromEntries([
157
+ ...Object.entries(this.poolAddresses)
158
+ .filter(([_, value]) => !!value)
159
+ .map(([key, value]) => [key, value?.coinType]),
160
+ ...Object.entries(this.poolAddresses)
161
+ .filter(([_, value]) => !!value && value.sCoinName && value.sCoinType)
162
+ .map(([_, value]) => [value!.sCoinName, value!.sCoinType]),
163
+ ]);
164
+ return this._coinTypes;
165
+ }
166
+
167
+ /**
168
+ * @description
169
+ * Return maps of coin types to its coin name
170
+ */
171
+ get coinTypeToCoinNameMap() {
172
+ if (this.isEmptyObject(this._coinTypeToCoinNameMap))
173
+ this._coinTypeToCoinNameMap = Object.fromEntries(
174
+ Object.entries(this.coinTypes).map(([key, val]) => [val, key])
175
+ );
176
+ return this._coinTypeToCoinNameMap;
177
+ }
178
+
179
+ /**
180
+ * @description
181
+ * Return maps of wormhole coin types to its coin name.
182
+ */
183
+ get wormholeCoinTypeToCoinName() {
184
+ if (this.isEmptyObject(this._wormholeCoinTypeToCoinNameMap))
185
+ this._wormholeCoinTypeToCoinNameMap = Object.fromEntries(
186
+ Object.entries(this.poolAddresses)
187
+ .filter(([key, value]) => !!value && this.whitelist.wormhole.has(key))
188
+ .map(([_, value]) => [value!.coinType, value!.coinName])
189
+ );
190
+ return this._wormholeCoinTypeToCoinNameMap;
191
+ }
192
+
193
+ /**
194
+ * @description
195
+ * Return maps of coin name to its old market coin type (...::reserve::MarketCoin<coinType>)
196
+ */
197
+ get coinNameToOldMarketCoinTypeMap() {
198
+ if (this.isEmptyObject(this._coinNameToOldMarketCoinTypeMap))
199
+ this._coinNameToOldMarketCoinTypeMap = Object.fromEntries(
200
+ Object.entries(this.poolAddresses)
201
+ .filter(([_, value]) => !!value && value.spool)
202
+ .map(([_, value]) => [
203
+ value!.coinName,
204
+ this.parseToOldMarketCoin(value!.coinType),
205
+ ])
206
+ );
207
+ return this._coinNameToOldMarketCoinTypeMap;
208
+ }
209
+
210
+ /**
211
+ * @description
212
+ * Return maps of sCoin raw name from its type to its sCoin name. (e.g. 'scallop_sui' -> 'ssui')
213
+ */
214
+ get sCoinRawNameToScoinNameMap() {
215
+ if (this.isEmptyObject(this._scoinRawNameToSCoinNameMap))
216
+ this._scoinRawNameToSCoinNameMap = Object.fromEntries(
217
+ Object.entries(this.poolAddresses)
218
+ .filter(([_, value]) => !!value && value.sCoinType && value.sCoinName)
219
+ .map(([_, value]) => {
220
+ const scoinRawName = parseStructTag(value!.sCoinType!).name;
221
+ return [scoinRawName, value!.sCoinName!];
222
+ })
223
+ );
224
+
225
+ return this._scoinRawNameToSCoinNameMap;
226
+ }
227
+
228
+ /**
229
+ * @description
230
+ * Return maps of scoin type to its sCoin name
231
+ */
232
+ get sCoinTypeToSCoinNameMap() {
233
+ if (this.isEmptyObject(this._scoinTypeToSCoinNameMap))
234
+ this._scoinTypeToSCoinNameMap = Object.fromEntries(
235
+ Object.entries(this.poolAddresses)
236
+ .filter(([_, value]) => !!value && value.sCoinType && value.sCoinName)
237
+ .map(([_, value]) => [value!.sCoinType!, value!.sCoinName!])
238
+ );
239
+
240
+ return this._scoinTypeToSCoinNameMap;
241
+ }
242
+
243
+ /**
244
+ * @description
245
+ * Return maps of volo coin type to its coin name
246
+ */
247
+ get voloCoinTypeToCoinNameMap() {
248
+ if (this.isEmptyObject(this._voloCoinTypeToCoinNameMap))
249
+ this._voloCoinTypeToCoinNameMap = {
250
+ [this.poolAddresses['vsui']!.coinType]: 'vsui',
251
+ };
252
+ return this._voloCoinTypeToCoinNameMap;
253
+ }
254
+
255
+ /**
256
+ * @description
257
+ * Return maps of sui bridge coin type to its coin name
258
+ */
259
+ get suiBridgeCoinTypeToCoinNameMap() {
260
+ if (this.isEmptyObject(this._suiBridgeCoinTypeToCoinNameMap))
261
+ this._suiBridgeCoinTypeToCoinNameMap = Object.fromEntries(
262
+ Object.entries(this.poolAddresses)
263
+ .filter(
264
+ ([_, value]) =>
265
+ !!value && this.whitelist.suiBridge.has(value.coinName)
266
+ )
267
+ .map(([_, value]) => [value!.coinType, value!.coinName])
268
+ );
269
+ return this._suiBridgeCoinTypeToCoinNameMap;
270
+ }
271
+
272
+ /**
273
+ * @description
274
+ * Return maps of sCoin coin name to its coin type
275
+ */
276
+ get sCoinTypes() {
277
+ if (this.isEmptyObject(this._sCoinTypes))
278
+ this._sCoinTypes = Object.fromEntries(
279
+ Object.entries(this.poolAddresses)
280
+ .filter(([_, value]) => !!value && value.sCoinName && value.sCoinType)
281
+ .map(([_, value]) => [value!.sCoinName, value!.sCoinType!])
282
+ );
283
+
284
+ return this._sCoinTypes;
285
+ }
286
+
287
+ /**
288
+ * @description
289
+ * Return set of supported coin types for borrow incentive rewards
290
+ * (all supported pools + sCoins + custom coins from `whitelist.borrowIncentiveRewards`)
291
+ */
292
+ get supportedBorrowIncentiveRewards() {
293
+ if (!this._supportedBorrowIncentiveRewards.size)
294
+ this._supportedBorrowIncentiveRewards = new Set([
295
+ ...Object.values(this.poolAddresses)
296
+ .filter((t) => !!t)
297
+ .map((t) => (t.sCoinName ? [t.coinName, t.sCoinName] : [t.coinName]))
298
+ .flat(),
299
+ ]);
300
+ return this._supportedBorrowIncentiveRewards;
301
+ }
302
+
303
+ private isEmptyObject(obj: Record<string, unknown>) {
304
+ return Object.keys(obj).length === 0;
305
+ }
306
+
307
+ private async readApi<T>({
308
+ url,
309
+ queryKey,
310
+ }: {
311
+ url: string;
312
+ queryKey: QueryKey;
313
+ }) {
314
+ const resp = await this.queryClient.fetchQuery({
315
+ queryKey,
316
+ queryFn: async () => {
317
+ return await this._requestClient.get(url);
318
+ },
319
+ });
320
+
321
+ if (resp.status === 200) {
322
+ return resp.data as T;
323
+ } else {
324
+ throw Error(
325
+ `Error: ${resp.status}; Failed to read ${url} ${resp.statusText}`
326
+ );
327
+ }
328
+ }
329
+
330
+ async readWhiteList() {
331
+ const response = await this.readApi<Record<keyof Whitelist, string[]>>({
332
+ url:
333
+ this.params.whitelistApiUrl ??
334
+ `https://sui.apis.scallop.io/pool/whitelist`,
335
+ queryKey: queryKeys.api.getWhiteList(),
336
+ });
337
+
338
+ return Object.fromEntries(
339
+ Object.entries(response)
340
+ .filter(([_, value]) => Array.isArray(value))
341
+ .map(([key, value]) => [key, new Set(value)])
342
+ ) as Whitelist;
343
+ }
344
+
345
+ async readPoolAddresses() {
346
+ return await this.readApi<Record<string, PoolAddress>>({
347
+ url:
348
+ this.params.poolAddressesApiUrl ??
349
+ `https://sui.apis.scallop.io/pool/addresses`,
350
+ queryKey: queryKeys.api.getPoolAddresses(),
351
+ });
352
+ }
353
+
354
+ async init(params?: Partial<ScallopConstantsParams>) {
355
+ if (!this.isAddressInitialized) {
356
+ await this.address.read();
357
+ }
358
+
359
+ if (params?.forcePoolAddressInterface) {
360
+ this._poolAddresses = params?.forcePoolAddressInterface;
361
+ }
362
+
363
+ if (params?.forceWhitelistInterface) {
364
+ this._whitelist = params?.forceWhitelistInterface;
365
+ }
366
+
367
+ if (this.isInitialized) return;
368
+
369
+ const [whitelistResponse, poolAddressesResponse] = await Promise.all([
370
+ this.readWhiteList(),
371
+ this.readPoolAddresses(),
372
+ ]);
373
+
374
+ if (!this.params.forceWhitelistInterface) {
375
+ this._whitelist = Object.fromEntries(
376
+ Object.entries(whitelistResponse)
377
+ .filter(([_, value]) => Array.isArray(value) || value instanceof Set)
378
+ .map(([key, value]) => [
379
+ key as keyof Whitelist,
380
+ value instanceof Set ? value : new Set(value),
381
+ ])
382
+ ) as Whitelist;
383
+ }
384
+ if (!this.params.forcePoolAddressInterface) {
385
+ this._poolAddresses = Object.fromEntries(
386
+ Object.entries(poolAddressesResponse)
387
+ .filter(([key]) =>
388
+ Object.values(this.whitelist).some((set) => set.has(key))
389
+ )
390
+ .map(([key, value]) => {
391
+ const parsedValue = Object.fromEntries(
392
+ Object.entries(value).map(([k, v]) => [k, v || undefined])
393
+ );
394
+ return [key, parsedValue as PoolAddress];
395
+ })
396
+ );
397
+ }
398
+ }
399
+ }
@@ -10,16 +10,10 @@ import type {
10
10
  Spool,
11
11
  BorrowIncentivePools,
12
12
  BorrowIncentivePool,
13
- SupportPoolCoins,
14
- SupportCollateralCoins,
15
- SupportStakeMarketCoins,
16
- SupportBorrowIncentiveCoins,
17
13
  TotalValueLocked,
18
- ScallopQueryParams,
19
- ScallopParams,
20
14
  BorrowIncentivePoolPoints,
21
- SupportBorrowIncentiveRewardCoins,
22
15
  ScallopIndexerInstanceParams,
16
+ ScallopIndexerParams,
23
17
  } from '../types';
24
18
  import { ScallopCache } from './scallopCache';
25
19
  import { queryKeys } from 'src/constants';
@@ -38,17 +32,17 @@ import { queryKeys } from 'src/constants';
38
32
  */
39
33
  export class ScallopIndexer {
40
34
  private readonly cache: ScallopCache;
41
- public readonly params: ScallopQueryParams;
35
+ public readonly params: ScallopIndexerParams;
42
36
  private readonly _requestClient: AxiosInstance;
43
37
 
44
38
  public constructor(
45
- params: ScallopParams = {},
39
+ params: ScallopIndexerParams,
46
40
  instance?: ScallopIndexerInstanceParams
47
41
  ) {
48
42
  this.params = params;
49
43
  this.cache = instance?.cache ?? new ScallopCache(this.params);
50
44
  this._requestClient = axios.create({
51
- baseURL: SDK_API_BASE_URL,
45
+ baseURL: params.indexerApiUrl ?? SDK_API_BASE_URL,
52
46
  headers: {
53
47
  'Content-Type': 'application/json',
54
48
  Accept: 'application/json',
@@ -107,9 +101,7 @@ export class ScallopIndexer {
107
101
  *
108
102
  * @return Market pool data.
109
103
  */
110
- public async getMarketPool(
111
- poolCoinName: SupportPoolCoins
112
- ): Promise<MarketPool> {
104
+ public async getMarketPool(poolCoinName: string): Promise<MarketPool> {
113
105
  return (await this.getMarketPools())[poolCoinName] as MarketPool;
114
106
  }
115
107
 
@@ -128,7 +120,7 @@ export class ScallopIndexer {
128
120
  * @return Market collateral data.
129
121
  */
130
122
  public async getMarketCollateral(
131
- collateralCoinName: SupportCollateralCoins
123
+ collateralCoinName: string
132
124
  ): Promise<MarketCollateral> {
133
125
  return (await this.getMarketCollaterals())[
134
126
  collateralCoinName
@@ -165,9 +157,7 @@ export class ScallopIndexer {
165
157
  *
166
158
  * @return Spool data.
167
159
  */
168
- public async getSpool(
169
- marketCoinName: SupportStakeMarketCoins
170
- ): Promise<Spool> {
160
+ public async getSpool(marketCoinName: string): Promise<Spool> {
171
161
  return (await this.getSpools())[marketCoinName] as Spool;
172
162
  }
173
163
 
@@ -199,10 +189,7 @@ export class ScallopIndexer {
199
189
  prev[curr.coinName] = curr;
200
190
  return prev;
201
191
  },
202
- {} as Record<
203
- SupportBorrowIncentiveRewardCoins,
204
- BorrowIncentivePoolPoints
205
- >
192
+ {} as Record<string, BorrowIncentivePoolPoints>
206
193
  );
207
194
  }
208
195
  borrowIncentivePools[borrowIncentivePool.coinName] =
@@ -222,7 +209,7 @@ export class ScallopIndexer {
222
209
  * @return Borrow incentive pool data.
223
210
  */
224
211
  public async getBorrowIncentivePool(
225
- borrowIncentiveCoinName: SupportBorrowIncentiveCoins
212
+ borrowIncentiveCoinName: string
226
213
  ): Promise<BorrowIncentivePool> {
227
214
  return (await this.getBorrowIncentivePools())[
228
215
  borrowIncentiveCoinName
@@ -266,7 +253,7 @@ export class ScallopIndexer {
266
253
  *
267
254
  * @return price data.
268
255
  */
269
- public async getCoinPrice(poolCoinName: SupportPoolCoins): Promise<number> {
256
+ public async getCoinPrice(poolCoinName: string): Promise<number> {
270
257
  return (await this.getMarketPool(poolCoinName))?.coinPrice ?? 0;
271
258
  }
272
259
 
@@ -274,7 +261,7 @@ export class ScallopIndexer {
274
261
  const marketPools = await this.getMarketPools();
275
262
  return Object.entries(marketPools).reduce(
276
263
  (prev, [coinName, market]) => {
277
- prev[coinName] = market.coinPrice;
264
+ if (market) prev[coinName] = market.coinPrice;
278
265
  return prev;
279
266
  },
280
267
  {} as Record<string, number>