@scallop-io/sui-scallop-sdk 0.46.39 → 0.46.41

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/builders/loyaltyProgramBuilder.d.ts +1 -1
  2. package/dist/builders/sCoinBuilder.d.ts +4 -0
  3. package/dist/constants/common.d.ts +3 -1
  4. package/dist/constants/enum.d.ts +12 -10
  5. package/dist/constants/flashloan.d.ts +2 -0
  6. package/dist/constants/index.d.ts +1 -0
  7. package/dist/index.js +742 -97
  8. package/dist/index.js.map +1 -1
  9. package/dist/index.mjs +739 -92
  10. package/dist/index.mjs.map +1 -1
  11. package/dist/models/scallopBuilder.d.ts +16 -1
  12. package/dist/models/scallopClient.d.ts +5 -0
  13. package/dist/models/scallopQuery.d.ts +24 -1
  14. package/dist/models/scallopUtils.d.ts +32 -2
  15. package/dist/queries/coreQuery.d.ts +7 -0
  16. package/dist/queries/portfolioQuery.d.ts +1 -1
  17. package/dist/queries/sCoinQuery.d.ts +27 -0
  18. package/dist/test.d.ts +1 -0
  19. package/dist/types/address.d.ts +8 -1
  20. package/dist/types/builder/core.d.ts +12 -4
  21. package/dist/types/builder/index.d.ts +6 -1
  22. package/dist/types/builder/sCoin.d.ts +37 -0
  23. package/dist/types/builder/spool.d.ts +2 -1
  24. package/dist/types/constant/common.d.ts +3 -2
  25. package/dist/types/constant/enum.d.ts +13 -1
  26. package/dist/types/query/core.d.ts +2 -1
  27. package/dist/types/query/index.d.ts +1 -0
  28. package/dist/types/query/portfolio.d.ts +1 -0
  29. package/dist/types/query/sCoin.d.ts +1 -0
  30. package/package.json +3 -3
  31. package/src/builders/coreBuilder.ts +72 -17
  32. package/src/builders/index.ts +5 -1
  33. package/src/builders/loyaltyProgramBuilder.ts +1 -1
  34. package/src/builders/referralBuilder.ts +1 -1
  35. package/src/builders/sCoinBuilder.ts +119 -0
  36. package/src/builders/spoolBuilder.ts +1 -1
  37. package/src/builders/vescaBuilder.ts +3 -3
  38. package/src/constants/common.ts +19 -5
  39. package/src/constants/enum.ts +98 -20
  40. package/src/constants/flashloan.ts +18 -0
  41. package/src/constants/index.ts +1 -0
  42. package/src/constants/testAddress.ts +115 -21
  43. package/src/models/scallopAddress.ts +44 -3
  44. package/src/models/scallopBuilder.ts +43 -7
  45. package/src/models/scallopCache.ts +32 -4
  46. package/src/models/scallopClient.ts +121 -0
  47. package/src/models/scallopQuery.ts +57 -1
  48. package/src/models/scallopUtils.ts +56 -2
  49. package/src/queries/coreQuery.ts +102 -4
  50. package/src/queries/portfolioQuery.ts +25 -3
  51. package/src/queries/sCoinQuery.ts +94 -0
  52. package/src/queries/vescaQuery.ts +0 -1
  53. package/src/test.ts +19 -0
  54. package/src/types/address.ts +13 -0
  55. package/src/types/builder/core.ts +19 -4
  56. package/src/types/builder/index.ts +11 -3
  57. package/src/types/builder/sCoin.ts +61 -0
  58. package/src/types/builder/spool.ts +2 -0
  59. package/src/types/constant/common.ts +4 -1
  60. package/src/types/constant/enum.ts +17 -0
  61. package/src/types/query/core.ts +3 -0
  62. package/src/types/query/index.ts +1 -0
  63. package/src/types/query/portfolio.ts +1 -0
  64. package/src/types/query/sCoin.ts +1 -0
  65. package/src/utils/builder.ts +1 -1
  66. package/src/utils/util.ts +13 -17
@@ -1,4 +1,4 @@
1
- import { API_BASE_URL, IS_VE_SCA_TEST } from '../constants';
1
+ import { API_BASE_URL, USE_TEST_ADDRESS } from '../constants';
2
2
  import type { NetworkType } from '@scallop-io/sui-kit';
3
3
  import type {
4
4
  ScallopAddressParams,
@@ -309,6 +309,47 @@ const EMPTY_ADDRESSES: AddressesInterface = {
309
309
  rewardPool: '',
310
310
  userRewardTableId: '',
311
311
  },
312
+ scoin: {
313
+ id: '',
314
+ coins: {
315
+ ssui: {
316
+ coinType: '',
317
+ treasury: '',
318
+ },
319
+ scetus: {
320
+ coinType: '',
321
+ treasury: '',
322
+ },
323
+ ssca: {
324
+ coinType: '',
325
+ treasury: '',
326
+ },
327
+ susdc: {
328
+ coinType: '',
329
+ treasury: '',
330
+ },
331
+ susdt: {
332
+ coinType: '',
333
+ treasury: '',
334
+ },
335
+ seth: {
336
+ coinType: '',
337
+ treasury: '',
338
+ },
339
+ safsui: {
340
+ coinType: '',
341
+ treasury: '',
342
+ },
343
+ shasui: {
344
+ coinType: '',
345
+ treasury: '',
346
+ },
347
+ svsui: {
348
+ coinType: '',
349
+ treasury: '',
350
+ },
351
+ },
352
+ },
312
353
  };
313
354
  /**
314
355
  * @description
@@ -345,10 +386,10 @@ export class ScallopAddress {
345
386
  if (auth) this._auth = auth;
346
387
  this._id = id;
347
388
  this._network = network || 'mainnet';
348
- this._addressesMap = IS_VE_SCA_TEST
389
+ this._addressesMap = USE_TEST_ADDRESS
349
390
  ? new Map([['mainnet', TEST_ADDRESSES]])
350
391
  : new Map();
351
- if (IS_VE_SCA_TEST) this._currentAddresses = TEST_ADDRESSES;
392
+ if (USE_TEST_ADDRESS) this._currentAddresses = TEST_ADDRESSES;
352
393
  }
353
394
 
354
395
  /**
@@ -14,6 +14,7 @@ import type {
14
14
  ScallopTxBlock,
15
15
  SupportMarketCoins,
16
16
  SupportAssetCoins,
17
+ SupportSCoin,
17
18
  } from '../types';
18
19
  import { ScallopCache } from './scallopCache';
19
20
  import { DEFAULT_CACHE_OPTIONS } from 'src/constants/cache';
@@ -124,7 +125,7 @@ export class ScallopBuilder {
124
125
  sender: string
125
126
  ) {
126
127
  const coinType = this.utils.parseCoinType(assetCoinName);
127
- const coins = await this.utils.selectCoinIds(amount, coinType, sender);
128
+ const coins = await this.utils.selectCoins(amount, coinType, sender);
128
129
  const [takeCoin, leftCoin] = txBlock.takeAmountFromCoins(coins, amount);
129
130
  return { takeCoin, leftCoin };
130
131
  }
@@ -145,13 +146,48 @@ export class ScallopBuilder {
145
146
  sender: string
146
147
  ) {
147
148
  const marketCoinType = this.utils.parseMarketCoinType(marketCoinName);
148
- const coins = await this.utils.selectCoinIds(
149
- amount,
150
- marketCoinType,
151
- sender
149
+ const coins = await this.utils.selectCoins(amount, marketCoinType, sender);
150
+ const totalAmount = coins.reduce((prev, coin) => {
151
+ prev += Number(coin.balance);
152
+ return prev;
153
+ }, 0);
154
+ const [takeCoin, leftCoin] = txBlock.takeAmountFromCoins(
155
+ coins,
156
+ Math.min(amount, totalAmount)
152
157
  );
153
- const [takeCoin, leftCoin] = txBlock.takeAmountFromCoins(coins, amount);
154
- return { takeCoin, leftCoin };
158
+ return { takeCoin, leftCoin, totalAmount };
159
+ }
160
+
161
+ /**
162
+ * Specifying the sender's amount of sCoins to get coins args from transaction result.
163
+ *
164
+ * @param txBlock - Scallop txBlock or txBlock created by SuiKit .
165
+ * @param marketCoinName - Specific support sCoin name.
166
+ * @param amount - Amount of coins to be selected.
167
+ * @param sender - Sender address.
168
+ * @return Take coin and left coin.
169
+ */
170
+ public async selectSCoin(
171
+ txBlock: ScallopTxBlock | SuiKitTxBlock,
172
+ sCoinName: SupportSCoin,
173
+ amount: number,
174
+ sender: string
175
+ ) {
176
+ const sCoinType = this.utils.parseSCoinType(sCoinName);
177
+ const coins = await this.utils.selectCoins(amount, sCoinType, sender);
178
+ const totalAmount = coins.reduce((prev, coin) => {
179
+ prev += Number(coin.balance);
180
+ return prev;
181
+ }, 0);
182
+ const [takeCoin, leftCoin] = txBlock.takeAmountFromCoins(
183
+ coins,
184
+ Math.min(totalAmount, amount)
185
+ );
186
+ return {
187
+ takeCoin,
188
+ leftCoin,
189
+ totalAmount,
190
+ };
155
191
  }
156
192
 
157
193
  /**
@@ -1,5 +1,10 @@
1
1
  import { QueryClient, QueryClientConfig } from '@tanstack/query-core';
2
- import { SuiTxArg, SuiTxBlock, normalizeStructTag } from '@scallop-io/sui-kit';
2
+ import {
3
+ SuiTxArg,
4
+ SuiTxBlock,
5
+ normalizeStructTag,
6
+ normalizeSuiAddress,
7
+ } from '@scallop-io/sui-kit';
3
8
  import { SuiKit } from '@scallop-io/sui-kit';
4
9
  import type {
5
10
  SuiObjectResponse,
@@ -162,6 +167,7 @@ export class ScallopCache {
162
167
  objectIds: string[],
163
168
  options?: SuiObjectDataOptions
164
169
  ): Promise<SuiObjectData[]> {
170
+ if (objectIds.length === 0) return [];
165
171
  const queryKey = [
166
172
  'getObjects',
167
173
  JSON.stringify(objectIds),
@@ -171,7 +177,7 @@ export class ScallopCache {
171
177
  queryKey.push(JSON.stringify(options));
172
178
  }
173
179
  return this.queryClient.fetchQuery({
174
- queryKey,
180
+ queryKey: queryKey,
175
181
  queryFn: async () => {
176
182
  return await this.suiKit.getObjects(objectIds, options);
177
183
  },
@@ -254,7 +260,7 @@ export class ScallopCache {
254
260
  const allBalances = await this.suiKit
255
261
  .client()
256
262
  .getAllBalances({ owner });
257
- return allBalances.reduce(
263
+ const balances = allBalances.reduce(
258
264
  (acc, coinBalance) => {
259
265
  if (coinBalance.totalBalance !== '0') {
260
266
  acc[normalizeStructTag(coinBalance.coinType)] =
@@ -264,12 +270,34 @@ export class ScallopCache {
264
270
  },
265
271
  {} as { [k: string]: string }
266
272
  );
273
+
274
+ // Set query data for each coin balance
275
+ for (const coinType in balances) {
276
+ const coinBalanceQueryKey = [
277
+ 'getCoinBalance',
278
+ normalizeSuiAddress(owner),
279
+ normalizeStructTag(coinType),
280
+ ];
281
+ this.queryClient.setQueryData(
282
+ coinBalanceQueryKey,
283
+ balances[coinType]
284
+ );
285
+ }
286
+
287
+ return balances;
267
288
  },
289
+ staleTime: 5000,
268
290
  });
269
291
  }
270
292
 
271
293
  public async queryGetCoinBalance(input: GetBalanceParams): Promise<string> {
272
- const queryKey = ['getCoinBalance', input.owner, input.coinType];
294
+ if (!input.coinType) return '0';
295
+
296
+ const queryKey = [
297
+ 'getCoinBalance',
298
+ normalizeSuiAddress(input.owner),
299
+ normalizeStructTag(input.coinType),
300
+ ];
273
301
  return this.queryClient.fetchQuery({
274
302
  queryKey,
275
303
  queryFn: async () => {
@@ -4,6 +4,8 @@ import {
4
4
  ADDRESSES_ID,
5
5
  SUPPORT_BORROW_INCENTIVE_POOLS,
6
6
  SUPPORT_BORROW_INCENTIVE_REWARDS,
7
+ SUPPORT_SCOIN,
8
+ SUPPORT_SPOOLS,
7
9
  } from '../constants';
8
10
  import { ScallopAddress } from './scallopAddress';
9
11
  import { ScallopUtils } from './scallopUtils';
@@ -23,6 +25,7 @@ import type {
23
25
  SupportStakeMarketCoins,
24
26
  SupportBorrowIncentiveCoins,
25
27
  ScallopTxBlock,
28
+ SupportSCoin,
26
29
  } from '../types';
27
30
  import { ScallopCache } from './scallopCache';
28
31
  import { DEFAULT_CACHE_OPTIONS } from 'src/constants/cache';
@@ -959,6 +962,124 @@ export class ScallopClient {
959
962
  }
960
963
  }
961
964
 
965
+ /* ==================== Migrate market coin to sCoin method ==================== */
966
+ /**
967
+ * Function to migrate all market coin in user wallet into sCoin
968
+ * @returns Transaction response
969
+ */
970
+ public async migrateAllMarketCoin<S extends boolean>(
971
+ sign: S = true as S
972
+ ): Promise<ScallopClientFnReturnType<S>> {
973
+ const txBlock = this.builder.createTxBlock();
974
+ txBlock.setSender(this.walletAddress);
975
+
976
+ const toTransfer: SuiObjectArg[] = [];
977
+ await Promise.all(
978
+ SUPPORT_SCOIN.map(async (sCoinName) => {
979
+ /**
980
+ * First check marketCoin inside mini wallet
981
+ * Then check stakedMarketCoin inside spool
982
+ */
983
+ let toDestroyMarketCoin: SuiObjectArg | undefined;
984
+
985
+ // check market coin in mini wallet
986
+ try {
987
+ const marketCoins = await this.utils.selectCoins(
988
+ Number.MAX_SAFE_INTEGER,
989
+ this.utils.parseMarketCoinType(sCoinName as SupportSCoin),
990
+ this.walletAddress
991
+ ); // throw error no coins found
992
+
993
+ const mergedMarketCoin = marketCoins[0];
994
+ if (marketCoins.length > 1) {
995
+ txBlock.mergeCoins(mergedMarketCoin, marketCoins.slice(1));
996
+ }
997
+
998
+ toDestroyMarketCoin = mergedMarketCoin;
999
+ } catch (e: any) {
1000
+ // Ignore
1001
+ const errMsg = e.toString() as String;
1002
+ if (!errMsg.includes('No valid coins found for the transaction'))
1003
+ throw e;
1004
+ }
1005
+
1006
+ // check for staked market coin in spool
1007
+ if (SUPPORT_SPOOLS.includes(sCoinName as SupportStakeMarketCoins)) {
1008
+ try {
1009
+ const stakedMarketCoins = await txBlock.unstakeQuick(
1010
+ Number.MAX_SAFE_INTEGER,
1011
+ sCoinName as SupportStakeMarketCoins
1012
+ );
1013
+ if (stakedMarketCoins.length > 0) {
1014
+ const mergedStakedMarketCoin = stakedMarketCoins[0];
1015
+ if (stakedMarketCoins.length > 1) {
1016
+ txBlock.mergeCoins(
1017
+ mergedStakedMarketCoin,
1018
+ stakedMarketCoins.slice(1)
1019
+ );
1020
+ }
1021
+ // merge with takeMarketCoin
1022
+ if (toDestroyMarketCoin) {
1023
+ txBlock.mergeCoins(toDestroyMarketCoin, [
1024
+ mergedStakedMarketCoin,
1025
+ ]);
1026
+ } else {
1027
+ toDestroyMarketCoin = mergedStakedMarketCoin;
1028
+ }
1029
+ }
1030
+ } catch (e: any) {
1031
+ // ignore
1032
+ const errMsg = e.toString();
1033
+ if (!errMsg.includes('No stake account found')) throw e;
1034
+ }
1035
+ }
1036
+
1037
+ // if market coin found, mint sCoin
1038
+ if (toDestroyMarketCoin) {
1039
+ // mint new sCoin
1040
+ const sCoin = txBlock.mintSCoin(
1041
+ sCoinName as SupportSCoin,
1042
+ toDestroyMarketCoin
1043
+ );
1044
+
1045
+ // check if current sCoin exist
1046
+ try {
1047
+ const existSCoins = await this.utils.selectCoins(
1048
+ Number.MAX_SAFE_INTEGER,
1049
+ this.utils.parseSCoinType(sCoinName as SupportSCoin),
1050
+ this.walletAddress
1051
+ ); // throw error on no coins found
1052
+ const mergedSCoin = existSCoins[0];
1053
+ if (existSCoins.length > 1) {
1054
+ txBlock.mergeCoins(mergedSCoin, existSCoins.slice(1));
1055
+ }
1056
+
1057
+ // merge existing sCoin to new sCoin
1058
+ txBlock.mergeCoins(sCoin, [mergedSCoin]);
1059
+ } catch (e: any) {
1060
+ // ignore
1061
+ const errMsg = e.toString() as String;
1062
+ if (!errMsg.includes('No valid coins found for the transaction'))
1063
+ throw e;
1064
+ }
1065
+ toTransfer.push(sCoin);
1066
+ }
1067
+ })
1068
+ );
1069
+
1070
+ if (toTransfer.length > 0) {
1071
+ txBlock.transferObjects(toTransfer, this.walletAddress);
1072
+ }
1073
+
1074
+ if (sign) {
1075
+ return (await this.suiKit.signAndSendTxn(
1076
+ txBlock
1077
+ )) as ScallopClientFnReturnType<S>;
1078
+ } else {
1079
+ return txBlock.txBlock as ScallopClientFnReturnType<S>;
1080
+ }
1081
+ }
1082
+
962
1083
  /* ==================== Other Method ==================== */
963
1084
 
964
1085
  /**
@@ -1,5 +1,5 @@
1
1
  import { SuiKit } from '@scallop-io/sui-kit';
2
- import { ADDRESSES_ID, SUPPORT_SPOOLS } from '../constants';
2
+ import { ADDRESSES_ID, SUPPORT_POOLS, SUPPORT_SPOOLS } from '../constants';
3
3
  import {
4
4
  queryMarket,
5
5
  getObligations,
@@ -32,6 +32,7 @@ import {
32
32
  getPythPrices,
33
33
  getVeScaTreasuryInfo,
34
34
  getLoyaltyProgramInformations,
35
+ getFlashLoanFees,
35
36
  } from '../queries';
36
37
  import {
37
38
  ScallopQueryParams,
@@ -44,6 +45,7 @@ import {
44
45
  StakePools,
45
46
  StakeRewardPools,
46
47
  SupportBorrowIncentiveCoins,
48
+ SupportSCoin,
47
49
  } from '../types';
48
50
  import { ScallopAddress } from './scallopAddress';
49
51
  import { ScallopUtils } from './scallopUtils';
@@ -51,6 +53,11 @@ import { ScallopIndexer } from './scallopIndexer';
51
53
  import { ScallopCache } from './scallopCache';
52
54
  import { DEFAULT_CACHE_OPTIONS } from 'src/constants/cache';
53
55
  import { SuiObjectData } from '@mysten/sui.js/src/client';
56
+ import {
57
+ getSCoinAmount,
58
+ getSCoinAmounts,
59
+ getSCoinTotalSupply,
60
+ } from 'src/queries/sCoinQuery';
54
61
 
55
62
  /**
56
63
  * @description
@@ -593,4 +600,53 @@ export class ScallopQuery {
593
600
  public async getLoyaltyProgramInfos(veScaKey?: string | SuiObjectData) {
594
601
  return await getLoyaltyProgramInformations(this, veScaKey);
595
602
  }
603
+
604
+ /**
605
+ * Get total supply of sCoin
606
+ * @param sCoinName - Supported sCoin name
607
+ * @returns Total Supply
608
+ */
609
+ public async getSCoinTotalSupply(sCoinName: SupportSCoin) {
610
+ return await getSCoinTotalSupply(this, sCoinName);
611
+ }
612
+
613
+ /**
614
+ * Get all sCoin amounts.
615
+ *
616
+ * @param sCoinNames - Specific an array of support sCoin name.
617
+ * @param ownerAddress - The owner address.
618
+ * @return All market sCoin amounts.
619
+ */
620
+ public async getSCoinAmounts(
621
+ sCoinNames?: SupportSCoin[],
622
+ ownerAddress?: string
623
+ ) {
624
+ return await getSCoinAmounts(this, sCoinNames, ownerAddress);
625
+ }
626
+
627
+ /**
628
+ * Get sCoin amount.
629
+ *
630
+ * @param coinNames - Specific support sCoin name.
631
+ * @param ownerAddress - The owner address.
632
+ * @return sCoin amount.
633
+ */
634
+ public async getSCoinAmount(
635
+ sCoinName: SupportSCoin | SupportMarketCoins,
636
+ ownerAddress?: string
637
+ ) {
638
+ const parsedSCoinName = this.utils.parseSCoinName(sCoinName);
639
+ return parsedSCoinName
640
+ ? await getSCoinAmount(this, parsedSCoinName, ownerAddress)
641
+ : 0;
642
+ }
643
+
644
+ /*
645
+ * Get flashloan fee for specified assets
646
+ */
647
+ public async getFlashLoanFees(
648
+ assetCoinNames: SupportAssetCoins[] = [...SUPPORT_POOLS]
649
+ ) {
650
+ return await getFlashLoanFees(this, assetCoinNames);
651
+ }
596
652
  }
@@ -16,6 +16,8 @@ import {
16
16
  coinIds,
17
17
  UNLOCK_ROUND_DURATION,
18
18
  MAX_LOCK_DURATION,
19
+ SUPPORT_SCOIN,
20
+ sCoinIds,
19
21
  } from '../constants';
20
22
  import { queryObligation } from '../queries';
21
23
  import {
@@ -35,6 +37,7 @@ import type {
35
37
  CoinPrices,
36
38
  PriceMap,
37
39
  CoinWrappedType,
40
+ SupportSCoin,
38
41
  } from '../types';
39
42
  import { PYTH_ENDPOINTS } from 'src/constants/pyth';
40
43
  import { ScallopCache } from './scallopCache';
@@ -170,6 +173,57 @@ export class ScallopUtils {
170
173
  }
171
174
  }
172
175
 
176
+ /**
177
+ * Convert coin name to sCoin name.
178
+ *
179
+ * @param coinName - Specific support coin name.
180
+ * @return sCoin name.
181
+ */
182
+ public parseSCoinName<T extends SupportSCoin>(
183
+ coinName: SupportCoins | SupportMarketCoins
184
+ ) {
185
+ // need more check because sbtc, ssol and sapt has no sCoin type
186
+ if (
187
+ isMarketCoin(coinName) &&
188
+ SUPPORT_SCOIN.includes(coinName as SupportSCoin)
189
+ ) {
190
+ return coinName as T;
191
+ } else {
192
+ const marketCoinName = `s${coinName}`;
193
+ if (SUPPORT_SCOIN.includes(marketCoinName as SupportSCoin)) {
194
+ return marketCoinName as T;
195
+ }
196
+ return undefined;
197
+ }
198
+ }
199
+ /**
200
+ * Convert sCoin name into sCoin type
201
+ * @param sCoinName
202
+ * @returns sCoin type
203
+ */
204
+ public parseSCoinType(sCoinName: SupportSCoin) {
205
+ return sCoinIds[sCoinName];
206
+ }
207
+
208
+ /**
209
+ * Convert sCoin name into its underlying coin type
210
+ * @param sCoinName
211
+ * @returns coin type
212
+ */
213
+ public parseUnderlyingSCoinType(sCoinName: SupportSCoin) {
214
+ const coinName = this.parseCoinName(sCoinName);
215
+ return this.parseCoinType(coinName);
216
+ }
217
+
218
+ /**
219
+ * Get sCoin treasury id from sCoin name
220
+ * @param sCoinName
221
+ * @returns sCoin treasury id
222
+ */
223
+ public getSCoinTreasury(sCoinName: SupportSCoin) {
224
+ return this._address.get(`scoin.coins.${sCoinName}.treasury`);
225
+ }
226
+
173
227
  /**
174
228
  * Convert coin name to market coin type.
175
229
  *
@@ -328,7 +382,7 @@ export class ScallopUtils {
328
382
  * @param coinType - The coin type, default is 0x2::SUI::SUI.
329
383
  * @return The selected transaction coin arguments.
330
384
  */
331
- public async selectCoinIds(
385
+ public async selectCoins(
332
386
  amount: number,
333
387
  coinType: string = SUI_TYPE_ARG,
334
388
  ownerAddress?: string
@@ -339,7 +393,7 @@ export class ScallopUtils {
339
393
  amount,
340
394
  coinType
341
395
  );
342
- return coins.map((c) => c.objectId);
396
+ return coins;
343
397
  }
344
398
 
345
399
  /**
@@ -4,6 +4,8 @@ import {
4
4
  PROTOCOL_OBJECT_ID,
5
5
  SUPPORT_COLLATERALS,
6
6
  BORROW_FEE_PROTOCOL_ID,
7
+ USE_TEST_ADDRESS,
8
+ FlashLoanFeeObjectMap,
7
9
  } from '../constants';
8
10
  import {
9
11
  parseOriginMarketPoolData,
@@ -404,7 +406,12 @@ export const getMarketPool = async (
404
406
  }
405
407
  }
406
408
 
407
- if (balanceSheet && borrowIndex && interestModel && borrowFeeRate) {
409
+ if (
410
+ balanceSheet &&
411
+ borrowIndex &&
412
+ interestModel &&
413
+ (USE_TEST_ADDRESS || borrowFeeRate)
414
+ ) {
408
415
  const parsedMarketPoolData = parseOriginMarketPoolData({
409
416
  type: interestModel.type.fields,
410
417
  maxBorrowRate: interestModel.max_borrow_rate.fields,
@@ -418,7 +425,7 @@ export const getMarketPool = async (
418
425
  reserve: balanceSheet.revenue,
419
426
  reserveFactor: interestModel.revenue_factor.fields,
420
427
  borrowWeight: interestModel.borrow_weight.fields,
421
- borrowFeeRate: borrowFeeRate,
428
+ borrowFeeRate: borrowFeeRate || { value: '0' },
422
429
  baseBorrowRatePerSec: interestModel.base_borrow_rate_per_sec.fields,
423
430
  borrowRateOnHighKink: interestModel.borrow_rate_on_high_kink.fields,
424
431
  borrowRateOnMidKink: interestModel.borrow_rate_on_mid_kink.fields,
@@ -474,10 +481,10 @@ export const getMarketCollaterals = async (
474
481
  collateralCoinNames = collateralCoinNames || [...SUPPORT_COLLATERALS];
475
482
  const marketId = query.address.get('core.market');
476
483
  const [marketObjectResponse, coinPrices] = await Promise.all([
477
- query.cache.queryGetObject(marketId, {
484
+ await query.cache.queryGetObject(marketId, {
478
485
  showContent: true,
479
486
  }),
480
- query.utils.getCoinPrices(collateralCoinNames ?? []),
487
+ await query.utils.getCoinPrices(collateralCoinNames ?? []),
481
488
  ]);
482
489
  const marketCollaterals: MarketCollaterals = {};
483
490
 
@@ -875,3 +882,94 @@ export const getMarketCoinAmount = async (
875
882
  });
876
883
  return BigNumber(amount).toNumber();
877
884
  };
885
+
886
+ /**
887
+ * Get flashloan fee for specific asset
888
+ * @param query - The Scallop query instance.
889
+ * @param assetNames - Specific an array of support pool coin name.
890
+ * @returns Record of asset name to flashloan fee in decimal
891
+ */
892
+
893
+ export const getFlashLoanFees = async (
894
+ query: ScallopQuery,
895
+ assetNames: SupportPoolCoins[]
896
+ ): Promise<Record<SupportPoolCoins, number>> => {
897
+ const FEE_RATE = 1e4;
898
+ const missingAssets: SupportPoolCoins[] = [];
899
+
900
+ // create mapping from asset type to asset name
901
+ const assetTypeMap = assetNames.reduce(
902
+ (prev, curr) => {
903
+ const assetType = query.utils.parseCoinType(curr).slice(2);
904
+ prev[assetType] = curr;
905
+ return prev;
906
+ },
907
+ {} as Record<string, SupportPoolCoins>
908
+ );
909
+
910
+ // use the mapped object first
911
+ const objIds = assetNames
912
+ .map((assetName) => {
913
+ if (!FlashLoanFeeObjectMap[assetName]) {
914
+ missingAssets.push(assetName);
915
+ return null;
916
+ } else {
917
+ return FlashLoanFeeObjectMap[assetName];
918
+ }
919
+ })
920
+ .filter((t) => !!t) as string[];
921
+
922
+ const flashloanFeeObjects = await query.cache.queryGetObjects(objIds, {
923
+ showContent: true,
924
+ });
925
+
926
+ if (missingAssets.length > 0) {
927
+ // get market object
928
+ const marketObjectId = query.address.get('core.market');
929
+ const marketObjectRes = await query.cache.queryGetObject(marketObjectId, {
930
+ showContent: true,
931
+ });
932
+ if (marketObjectRes.data?.content?.dataType !== 'moveObject')
933
+ throw new Error('Failed to get market object');
934
+
935
+ // get vault
936
+ const vault = (marketObjectRes.data.content.fields as any).vault;
937
+
938
+ // get vault balance sheet object id
939
+ const flashloanFeesTableId = vault.fields.flash_loan_fees.fields.table
940
+ .fields.id.id as string;
941
+
942
+ // the balance sheet is a VecSet<0x1::type_name::TypeName
943
+ const balanceSheetDynamicFields = await query.cache.queryGetDynamicFields({
944
+ parentId: flashloanFeesTableId,
945
+ limit: 50,
946
+ });
947
+
948
+ // get the dynamic object ids
949
+ const dynamicFieldObjectIds = balanceSheetDynamicFields.data
950
+ .filter((field) => {
951
+ const assetType = (field.name.value as any).name as string;
952
+ return !!assetTypeMap[assetType];
953
+ })
954
+ .map((field) => field.objectId);
955
+
956
+ flashloanFeeObjects.push(
957
+ ...(await query.cache.queryGetObjects(dynamicFieldObjectIds, {
958
+ showContent: true,
959
+ }))
960
+ );
961
+ }
962
+
963
+ return flashloanFeeObjects.reduce(
964
+ (prev, curr) => {
965
+ if (curr.content?.dataType === 'moveObject') {
966
+ const objectFields = curr.content.fields as any;
967
+ const assetType = (curr.content.fields as any).name.fields.name;
968
+ const feeNumerator = +objectFields.value;
969
+ prev[assetTypeMap[assetType]] = feeNumerator / FEE_RATE;
970
+ }
971
+ return prev;
972
+ },
973
+ {} as Record<SupportPoolCoins, number>
974
+ );
975
+ };