@scallop-io/sui-scallop-sdk 2.0.13-merge-split-ve-sca-alpha.4 → 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.
- package/dist/index.d.mts +622 -697
- package/dist/index.d.ts +622 -697
- package/dist/index.js +32 -33
- package/dist/index.mjs +10 -10
- package/package.json +8 -18
- package/src/builders/borrowIncentiveBuilder.ts +8 -18
- package/src/builders/coreBuilder.ts +2 -2
- package/src/builders/index.ts +2 -2
- package/src/builders/oracles/index.ts +2 -3
- package/src/builders/oracles/pyth.ts +2 -2
- package/src/builders/spoolBuilder.ts +2 -2
- package/src/builders/vescaBuilder.ts +14 -132
- package/src/constants/queryKeys.ts +29 -54
- package/src/constants/testAddress.ts +6 -12
- package/src/index.ts +11 -1
- package/src/models/index.ts +11 -9
- package/src/models/interface.ts +36 -0
- package/src/models/scallop.ts +38 -133
- package/src/models/scallopAddress.ts +127 -142
- package/src/models/scallopAxios.ts +185 -0
- package/src/models/scallopBuilder.ts +45 -75
- package/src/models/scallopClient.ts +124 -154
- package/src/models/scallopConstants.ts +248 -323
- package/src/models/scallopIndexer.ts +54 -98
- package/src/models/scallopQuery.ts +145 -190
- package/src/models/scallopQueryClient.ts +29 -0
- package/src/models/scallopSuiKit.ts +432 -0
- package/src/models/scallopUtils.ts +260 -164
- package/src/queries/borrowIncentiveQuery.ts +28 -16
- package/src/queries/borrowLimitQuery.ts +1 -1
- package/src/queries/coreQuery.ts +148 -107
- package/src/queries/flashloanFeeQuery.ts +12 -6
- package/src/queries/index.ts +0 -1
- package/src/queries/isolatedAssetQuery.ts +3 -3
- package/src/queries/loyaltyProgramQuery.ts +10 -8
- package/src/queries/ownerQuery.ts +32 -0
- package/src/queries/portfolioQuery.ts +4 -5
- package/src/queries/priceQuery.ts +14 -8
- package/src/queries/referralQuery.ts +9 -3
- package/src/queries/sCoinQuery.ts +4 -4
- package/src/queries/spoolQuery.ts +11 -11
- package/src/queries/supplyLimitQuery.ts +1 -1
- package/src/queries/switchboardQuery.ts +1 -1
- package/src/queries/vescaQuery.ts +31 -27
- package/src/queries/xOracleQuery.ts +13 -8
- package/src/types/address.ts +0 -3
- package/src/types/builder/core.ts +1 -1
- package/src/types/builder/vesca.ts +10 -20
- package/src/types/constant/queryKeys.ts +48 -0
- package/src/types/index.ts +0 -1
- package/src/utils/builder.ts +1 -1
- package/src/utils/util.ts +1 -33
- package/src/models/scallopCache.ts +0 -428
- package/src/queries/objectsQuery.ts +0 -18
- 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
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
} from '
|
|
20
|
-
import {
|
|
21
|
-
import
|
|
22
|
-
import {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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.
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
44
|
+
this.pythEndpoints = params.pythEndpoints ?? [
|
|
45
|
+
'https://hermes.pyth.network',
|
|
46
|
+
'https://scallop.rpc.p2p.world',
|
|
47
|
+
];
|
|
48
|
+
}
|
|
80
49
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
new ScallopConstants(this.params, {
|
|
84
|
-
address: this.address,
|
|
85
|
-
});
|
|
50
|
+
get walletAddress() {
|
|
51
|
+
return this.scallopSuiKit.walletAddress;
|
|
86
52
|
}
|
|
87
53
|
|
|
88
|
-
get
|
|
89
|
-
return this.
|
|
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
|
-
|
|
68
|
+
isSuiBridgeAsset(coinName: any) {
|
|
94
69
|
return this.constants.whitelist.suiBridge.has(coinName);
|
|
95
70
|
}
|
|
96
71
|
|
|
97
|
-
|
|
72
|
+
isWormholeAsset(coinName: any) {
|
|
98
73
|
return this.constants.whitelist.wormhole.has(coinName);
|
|
99
74
|
}
|
|
100
75
|
|
|
101
|
-
|
|
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
|
-
*
|
|
89
|
+
* Convert market coin name to coin name.
|
|
111
90
|
*
|
|
112
|
-
* @param
|
|
113
|
-
* @
|
|
91
|
+
* @param marketCoinName - Specific support market coin name.
|
|
92
|
+
* @return Coin Name.
|
|
114
93
|
*/
|
|
115
|
-
|
|
116
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
135
|
+
parseSCoinName<T extends string>(coinName: string) {
|
|
159
136
|
// need more check because swapt has no sCoin type
|
|
160
|
-
if (
|
|
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
|
-
|
|
179
|
-
return this.constants.
|
|
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
|
-
|
|
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
|
-
|
|
197
|
-
return this.constants.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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
|
|
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(
|
|
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;
|