@scallop-io/sui-scallop-sdk 0.44.27 → 0.45.0

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 (39) hide show
  1. package/dist/builders/borrowIncentiveBuilder.d.ts +0 -7
  2. package/dist/constants/cache.d.ts +8 -0
  3. package/dist/index.js +538 -268
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.mjs +522 -253
  6. package/dist/index.mjs.map +1 -1
  7. package/dist/models/index.d.ts +1 -0
  8. package/dist/models/scallop.d.ts +7 -1
  9. package/dist/models/scallopAddress.d.ts +4 -2
  10. package/dist/models/scallopBuilder.d.ts +2 -0
  11. package/dist/models/scallopCache.d.ts +71 -0
  12. package/dist/models/scallopClient.d.ts +2 -0
  13. package/dist/models/scallopIndexer.d.ts +5 -3
  14. package/dist/models/scallopQuery.d.ts +14 -0
  15. package/dist/models/scallopUtils.d.ts +1 -0
  16. package/dist/queries/borrowIncentiveQuery.d.ts +8 -0
  17. package/dist/types/model.d.ts +2 -0
  18. package/dist/types/query/vesca.d.ts +2 -1
  19. package/package.json +2 -1
  20. package/src/builders/borrowIncentiveBuilder.ts +8 -57
  21. package/src/builders/vescaBuilder.ts +7 -3
  22. package/src/constants/cache.ts +15 -0
  23. package/src/models/index.ts +1 -0
  24. package/src/models/scallop.ts +26 -7
  25. package/src/models/scallopAddress.ts +24 -19
  26. package/src/models/scallopBuilder.ts +14 -4
  27. package/src/models/scallopCache.ts +245 -0
  28. package/src/models/scallopClient.ts +15 -4
  29. package/src/models/scallopIndexer.ts +58 -84
  30. package/src/models/scallopQuery.ts +34 -5
  31. package/src/models/scallopUtils.ts +53 -24
  32. package/src/queries/borrowIncentiveQuery.ts +99 -7
  33. package/src/queries/coreQuery.ts +62 -75
  34. package/src/queries/priceQuery.ts +4 -6
  35. package/src/queries/spoolQuery.ts +12 -15
  36. package/src/queries/vescaQuery.ts +20 -8
  37. package/src/types/model.ts +2 -0
  38. package/src/types/query/borrowIncentive.ts +0 -107
  39. package/src/types/query/vesca.ts +2 -1
@@ -37,6 +37,8 @@ import type {
37
37
  CoinWrappedType,
38
38
  } from '../types';
39
39
  import { PYTH_ENDPOINTS } from 'src/constants/pyth';
40
+ import { ScallopCache } from './scallopCache';
41
+ import { DEFAULT_CACHE_OPTIONS } from 'src/constants/cache';
40
42
 
41
43
  /**
42
44
  * @description
@@ -58,6 +60,7 @@ export class ScallopUtils {
58
60
  private _address: ScallopAddress;
59
61
  private _query: ScallopQuery;
60
62
  private _priceMap: PriceMap = new Map();
63
+ private _cache: ScallopCache;
61
64
 
62
65
  public constructor(
63
66
  params: ScallopUtilsParams,
@@ -65,17 +68,23 @@ export class ScallopUtils {
65
68
  ) {
66
69
  this.params = params;
67
70
  this._suiKit = instance?.suiKit ?? new SuiKit(params);
71
+ this._cache =
72
+ instance?.cache ?? new ScallopCache(DEFAULT_CACHE_OPTIONS, this._suiKit);
68
73
  this._address =
69
74
  instance?.address ??
70
- new ScallopAddress({
71
- id: params?.addressesId || ADDRESSES_ID,
72
- network: params?.networkType,
73
- });
75
+ new ScallopAddress(
76
+ {
77
+ id: params?.addressesId || ADDRESSES_ID,
78
+ network: params?.networkType,
79
+ },
80
+ this._cache
81
+ );
74
82
  this._query =
75
83
  instance?.query ??
76
84
  new ScallopQuery(params, {
77
85
  suiKit: this._suiKit,
78
86
  address: this._address,
87
+ cache: this._cache,
79
88
  });
80
89
  this.isTestnet = params.networkType
81
90
  ? params.networkType === 'testnet'
@@ -404,36 +413,56 @@ export class ScallopUtils {
404
413
  const endpoints =
405
414
  this.params.pythEndpoints ??
406
415
  PYTH_ENDPOINTS[this.isTestnet ? 'testnet' : 'mainnet'];
407
- try {
408
- for (const endpoint of endpoints) {
409
- try {
410
- const pythConnection = new SuiPriceServiceConnection(endpoint);
411
- const priceIds = lackPricesCoinNames.map((coinName) =>
412
- this._address.get(`core.coins.${coinName}.oracle.pyth.feed`)
416
+
417
+ const failedRequests: Set<SupportAssetCoins> = new Set(
418
+ lackPricesCoinNames
419
+ );
420
+
421
+ for (const endpoint of endpoints) {
422
+ let hasFailRequest = false;
423
+ const pythConnection = new SuiPriceServiceConnection(endpoint);
424
+
425
+ const priceIds = Array.from(failedRequests.values()).reduce(
426
+ (acc, coinName) => {
427
+ const priceId = this._address.get(
428
+ `core.coins.${coinName}.oracle.pyth.feed`
413
429
  );
414
- const priceFeeds =
415
- (await pythConnection.getLatestPriceFeeds(priceIds)) || [];
416
- for (const [index, feed] of priceFeeds.entries()) {
417
- const data = parseDataFromPythPriceFeed(feed, this._address);
418
- const coinName = lackPricesCoinNames[index];
419
- this._priceMap.set(coinName, {
430
+ acc[coinName] = priceId;
431
+ return acc;
432
+ },
433
+ {} as Record<SupportAssetCoins, string>
434
+ );
435
+
436
+ for (const [coinName, priceId] of Object.entries(priceIds)) {
437
+ try {
438
+ const feed = await this._cache.queryClient.fetchQuery({
439
+ queryKey: [priceId],
440
+ queryFn: async () => {
441
+ return await pythConnection.getLatestPriceFeeds([priceId]);
442
+ },
443
+ // staleTime: 15000,
444
+ });
445
+ if (feed) {
446
+ const data = parseDataFromPythPriceFeed(feed[0], this._address);
447
+ this._priceMap.set(coinName as SupportAssetCoins, {
420
448
  price: data.price,
421
449
  publishTime: data.publishTime,
422
450
  });
423
- coinPrices[coinName] = data.price;
451
+ coinPrices[coinName as SupportAssetCoins] = data.price;
424
452
  }
425
-
426
- break;
453
+ failedRequests.delete(coinName as SupportAssetCoins); // remove success price feed to prevent duplicate request on the next endpoint
427
454
  } catch (e) {
428
455
  console.warn(
429
- `Failed to update price feeds with endpoint ${endpoint}: ${e}`
456
+ `Failed to get price ${coinName} feeds with endpoint ${endpoint}: ${e}`
430
457
  );
458
+ hasFailRequest = true;
431
459
  }
432
-
433
- throw new Error('Failed to update price feeds with all endpoins');
434
460
  }
435
- } catch (_e) {
436
- for (const coinName of lackPricesCoinNames) {
461
+ if (!hasFailRequest) break;
462
+ }
463
+
464
+ if (failedRequests.size > 0) {
465
+ for (const coinName of failedRequests.values()) {
437
466
  const price = await this._query.getPriceFromPyth(coinName);
438
467
  this._priceMap.set(coinName, {
439
468
  price: price,
@@ -1,6 +1,6 @@
1
1
  import { normalizeStructTag } from '@mysten/sui.js/utils';
2
- import { SuiTxBlock as SuiKitTxBlock } from '@scallop-io/sui-kit';
3
2
  import {
3
+ IS_VE_SCA_TEST,
4
4
  SUPPORT_BORROW_INCENTIVE_POOLS,
5
5
  SUPPORT_BORROW_INCENTIVE_REWARDS,
6
6
  } from '../constants';
@@ -40,10 +40,9 @@ export const queryBorrowIncentivePools = async (
40
40
  const queryPkgId = query.address.get('borrowIncentive.query');
41
41
  const incentivePoolsId = query.address.get('borrowIncentive.incentivePools');
42
42
 
43
- const txBlock = new SuiKitTxBlock();
44
43
  const queryTarget = `${queryPkgId}::incentive_pools_query::incentive_pools_data`;
45
- txBlock.moveCall(queryTarget, [incentivePoolsId]);
46
- const queryResult = await query.suiKit.inspectTxn(txBlock);
44
+ const args = [incentivePoolsId];
45
+ const queryResult = await query.cache.queryInspectTxn({ queryTarget, args });
47
46
  const borrowIncentivePoolsQueryData = queryResult.events[0]
48
47
  .parsedJson as BorrowIncentivePoolsQueryInterface;
49
48
 
@@ -169,10 +168,9 @@ export const queryBorrowIncentiveAccounts = async (
169
168
  'borrowIncentive.incentiveAccounts'
170
169
  );
171
170
  const queryTarget = `${queryPkgId}::incentive_account_query::incentive_account_data`;
171
+ const args = [incentiveAccountsId, obligationId];
172
172
 
173
- const txBlock = new SuiKitTxBlock();
174
- txBlock.moveCall(queryTarget, [incentiveAccountsId, obligationId]);
175
- const queryResult = await query.suiKit.inspectTxn(txBlock);
173
+ const queryResult = await query.cache.queryInspectTxn({ queryTarget, args });
176
174
  const borrowIncentiveAccountsQueryData = queryResult.events[0]
177
175
  .parsedJson as BorrowIncentiveAccountsQueryInterface;
178
176
 
@@ -197,3 +195,97 @@ export const queryBorrowIncentiveAccounts = async (
197
195
 
198
196
  return borrowIncentiveAccounts;
199
197
  };
198
+
199
+ /**
200
+ * Check veSca bind status
201
+ * @param query
202
+ * @param veScaKey
203
+ * @returns
204
+ */
205
+ export const getBindedObligationId = async (
206
+ query: ScallopQuery,
207
+ veScaKeyId: string
208
+ ): Promise<string | null> => {
209
+ const borrowIncentiveObjectId = query.address.get('borrowIncentive.object');
210
+ const incentivePoolsId = query.address.get('borrowIncentive.incentivePools');
211
+ const veScaPkgId = IS_VE_SCA_TEST
212
+ ? '0xb220d034bdf335d77ae5bfbf6daf059c2cc7a1f719b12bfed75d1736fac038c8'
213
+ : query.address.get('vesca.id');
214
+
215
+ const client = query.suiKit.client();
216
+
217
+ // get incentive pools
218
+ const incentivePoolsResponse = await client.getObject({
219
+ id: incentivePoolsId,
220
+ options: {
221
+ showContent: true,
222
+ },
223
+ });
224
+
225
+ if (incentivePoolsResponse.data?.content?.dataType !== 'moveObject')
226
+ return null;
227
+ const incentivePoolFields = incentivePoolsResponse.data.content.fields as any;
228
+ const veScaBindTableId = incentivePoolFields.ve_sca_bind.fields.id
229
+ .id as string;
230
+
231
+ // check if veSca is inside the bind table
232
+ const keyType = `${borrowIncentiveObjectId}::typed_id::TypedID<${veScaPkgId}::ve_sca::VeScaKey>`;
233
+ const veScaBindTableResponse = await client.getDynamicFieldObject({
234
+ parentId: veScaBindTableId,
235
+ name: {
236
+ type: keyType,
237
+ value: veScaKeyId,
238
+ },
239
+ });
240
+
241
+ if (veScaBindTableResponse.data?.content?.dataType !== 'moveObject')
242
+ return null;
243
+ const veScaBindTableFields = veScaBindTableResponse.data.content
244
+ .fields as any;
245
+ // get obligationId pair
246
+ const obligationId = veScaBindTableFields.value.fields.id as string;
247
+
248
+ return obligationId;
249
+ };
250
+
251
+ export const getBindedVeScaKey = async (
252
+ query: ScallopQuery,
253
+ obliationId: string
254
+ ): Promise<string | null> => {
255
+ const borrowIncentiveObjectId = query.address.get('borrowIncentive.object');
256
+ const incentiveAccountsId = query.address.get(
257
+ 'borrowIncentive.incentiveAccounts'
258
+ );
259
+ const corePkg = query.address.get('core.object');
260
+ const client = query.suiKit.client();
261
+
262
+ // get IncentiveAccounts object
263
+ const incentiveAccountsObject = await client.getObject({
264
+ id: incentiveAccountsId,
265
+ options: {
266
+ showContent: true,
267
+ },
268
+ });
269
+ if (incentiveAccountsObject.data?.content?.dataType !== 'moveObject')
270
+ return null;
271
+ const incentiveAccountsTableId = (
272
+ incentiveAccountsObject.data.content.fields as any
273
+ ).accounts.fields.id.id;
274
+
275
+ // Search in the table
276
+ const bindedIncentiveAcc = await client.getDynamicFieldObject({
277
+ parentId: incentiveAccountsTableId,
278
+ name: {
279
+ type: `${borrowIncentiveObjectId}::typed_id::TypedID<${corePkg}::obligation::Obligation>`,
280
+ value: obliationId,
281
+ },
282
+ });
283
+
284
+ if (bindedIncentiveAcc.data?.content?.dataType !== 'moveObject') return null;
285
+ const bindedIncentiveAccFields = bindedIncentiveAcc.data.content
286
+ .fields as any;
287
+
288
+ return (
289
+ bindedIncentiveAccFields.value.fields.binded_ve_sca_key?.fields.id ?? null
290
+ );
291
+ };
@@ -1,5 +1,4 @@
1
1
  import { normalizeStructTag } from '@mysten/sui.js/utils';
2
- import { SuiTxBlock as SuiKitTxBlock } from '@scallop-io/sui-kit';
3
2
  import BigNumber from 'bignumber.js';
4
3
  import {
5
4
  SUPPORT_POOLS,
@@ -54,10 +53,15 @@ export const queryMarket = async (
54
53
  ) => {
55
54
  const packageId = query.address.get('core.packages.query.id');
56
55
  const marketId = query.address.get('core.market');
57
- const txBlock = new SuiKitTxBlock();
58
56
  const queryTarget = `${packageId}::market_query::market_data`;
59
- txBlock.moveCall(queryTarget, [marketId]);
60
- const queryResult = await query.suiKit.inspectTxn(txBlock);
57
+ const args = [marketId];
58
+
59
+ // const txBlock = new SuiKitTxBlock();
60
+ // txBlock.moveCall(queryTarget, args);
61
+ const queryResult = await query.cache.queryInspectTxn(
62
+ { queryTarget, args }
63
+ // txBlock
64
+ );
61
65
  const marketData = queryResult.events[0].parsedJson as MarketQueryInterface;
62
66
  const coinPrices = await query.utils.getCoinPrices();
63
67
 
@@ -211,11 +215,8 @@ export const getMarketPools = async (
211
215
  ) => {
212
216
  poolCoinNames = poolCoinNames || [...SUPPORT_POOLS];
213
217
  const marketId = query.address.get('core.market');
214
- const marketObjectResponse = await query.suiKit.client().getObject({
215
- id: marketId,
216
- options: {
217
- showContent: true,
218
- },
218
+ const marketObjectResponse = await query.cache.queryGetObject(marketId, {
219
+ showContent: true,
219
220
  });
220
221
  const coinPrices = await query.utils.getCoinPrices(poolCoinNames ?? []);
221
222
 
@@ -274,11 +275,8 @@ export const getMarketPool = async (
274
275
  marketObject =
275
276
  marketObject ||
276
277
  (
277
- await query.suiKit.client().getObject({
278
- id: marketId,
279
- options: {
280
- showContent: true,
281
- },
278
+ await query.cache.queryGetObject(marketId, {
279
+ showContent: true,
282
280
  })
283
281
  ).data;
284
282
 
@@ -310,9 +308,8 @@ export const getMarketPool = async (
310
308
  // Get balance sheet.
311
309
  const balanceSheetParentId =
312
310
  fields.vault.fields.balance_sheets.fields.table.fields.id.id;
313
- const balanceSheetDynamicFieldObjectResponse = await query.suiKit
314
- .client()
315
- .getDynamicFieldObject({
311
+ const balanceSheetDynamicFieldObjectResponse =
312
+ await query.cache.queryGetDynamicFieldObject({
316
313
  parentId: balanceSheetParentId,
317
314
  name: {
318
315
  type: '0x1::type_name::TypeName',
@@ -336,9 +333,8 @@ export const getMarketPool = async (
336
333
  // Get borrow index.
337
334
  const borrowIndexParentId =
338
335
  fields.borrow_dynamics.fields.table.fields.id.id;
339
- const borrowIndexDynamicFieldObjectResponse = await query.suiKit
340
- .client()
341
- .getDynamicFieldObject({
336
+ const borrowIndexDynamicFieldObjectResponse =
337
+ await query.cache.queryGetDynamicFieldObject({
342
338
  parentId: borrowIndexParentId,
343
339
  name: {
344
340
  type: '0x1::type_name::TypeName',
@@ -362,9 +358,8 @@ export const getMarketPool = async (
362
358
  // Get interest models.
363
359
  const interestModelParentId =
364
360
  fields.interest_models.fields.table.fields.id.id;
365
- const interestModelDynamicFieldObjectResponse = await query.suiKit
366
- .client()
367
- .getDynamicFieldObject({
361
+ const interestModelDynamicFieldObjectResponse =
362
+ await query.cache.queryGetDynamicFieldObject({
368
363
  parentId: interestModelParentId,
369
364
  name: {
370
365
  type: '0x1::type_name::TypeName',
@@ -386,9 +381,8 @@ export const getMarketPool = async (
386
381
  }
387
382
 
388
383
  // Get borrow fee.
389
- const borrowFeeDynamicFieldObjectResponse = await query.suiKit
390
- .client()
391
- .getDynamicFieldObject({
384
+ const borrowFeeDynamicFieldObjectResponse =
385
+ await query.cache.queryGetDynamicFieldObject({
392
386
  parentId: marketId,
393
387
  name: {
394
388
  type: `${BORROW_FEE_PROTOCOL_ID}::market_dynamic_keys::BorrowFeeKey`,
@@ -482,11 +476,8 @@ export const getMarketCollaterals = async (
482
476
  ) => {
483
477
  collateralCoinNames = collateralCoinNames || [...SUPPORT_COLLATERALS];
484
478
  const marketId = query.address.get('core.market');
485
- const marketObjectResponse = await query.suiKit.client().getObject({
486
- id: marketId,
487
- options: {
488
- showContent: true,
489
- },
479
+ const marketObjectResponse = await query.cache.queryGetObject(marketId, {
480
+ showContent: true,
490
481
  });
491
482
  const coinPrices = await query.utils.getCoinPrices(collateralCoinNames ?? []);
492
483
 
@@ -544,11 +535,8 @@ export const getMarketCollateral = async (
544
535
  marketObject =
545
536
  marketObject ||
546
537
  (
547
- await query.suiKit.client().getObject({
548
- id: marketId,
549
- options: {
550
- showContent: true,
551
- },
538
+ await query.cache.queryGetObject(marketId, {
539
+ showContent: true,
552
540
  })
553
541
  ).data;
554
542
 
@@ -581,9 +569,8 @@ export const getMarketCollateral = async (
581
569
 
582
570
  // Get risk model.
583
571
  const riskModelParentId = fields.risk_models.fields.table.fields.id.id;
584
- const riskModelDynamicFieldObjectResponse = await query.suiKit
585
- .client()
586
- .getDynamicFieldObject({
572
+ const riskModelDynamicFieldObjectResponse =
573
+ await query.cache.queryGetDynamicFieldObject({
587
574
  parentId: riskModelParentId,
588
575
  name: {
589
576
  type: '0x1::type_name::TypeName',
@@ -606,9 +593,8 @@ export const getMarketCollateral = async (
606
593
  // Get collateral stat.
607
594
  const collateralStatParentId =
608
595
  fields.collateral_stats.fields.table.fields.id.id;
609
- const collateralStatDynamicFieldObjectResponse = await query.suiKit
610
- .client()
611
- .getDynamicFieldObject({
596
+ const collateralStatDynamicFieldObjectResponse =
597
+ await query.cache.queryGetDynamicFieldObject({
612
598
  parentId: collateralStatParentId,
613
599
  name: {
614
600
  type: '0x1::type_name::TypeName',
@@ -687,15 +673,14 @@ export const getObligations = async (
687
673
  let hasNextPage = false;
688
674
  let nextCursor: string | null | undefined = null;
689
675
  do {
690
- const paginatedKeyObjectsResponse = await query.suiKit
691
- .client()
692
- .getOwnedObjects({
693
- owner,
694
- filter: {
695
- StructType: `${protocolObjectId}::obligation::ObligationKey`,
696
- },
697
- cursor: nextCursor,
698
- });
676
+ const paginatedKeyObjectsResponse = await query.cache.queryGetOwnedObjects({
677
+ owner,
678
+ filter: {
679
+ StructType: `${protocolObjectId}::obligation::ObligationKey`,
680
+ },
681
+ cursor: nextCursor,
682
+ });
683
+
699
684
  keyObjectsResponse.push(...paginatedKeyObjectsResponse.data);
700
685
  if (
701
686
  paginatedKeyObjectsResponse.hasNextPage &&
@@ -711,7 +696,8 @@ export const getObligations = async (
711
696
  const keyObjectIds: string[] = keyObjectsResponse
712
697
  .map((ref: any) => ref?.data?.objectId)
713
698
  .filter((id: any) => id !== undefined);
714
- const keyObjects = await query.suiKit.getObjects(keyObjectIds);
699
+ const keyObjects = await query.cache.queryGetObjects(keyObjectIds);
700
+
715
701
  const obligations: Obligation[] = [];
716
702
  for (const keyObject of keyObjects) {
717
703
  const keyId = keyObject.objectId;
@@ -736,12 +722,10 @@ export const getObligationLocked = async (
736
722
  query: ScallopQuery,
737
723
  obligationId: string
738
724
  ) => {
739
- const obligationObjectResponse = await query.suiKit.client().getObject({
740
- id: obligationId,
741
- options: {
742
- showContent: true,
743
- },
744
- });
725
+ const obligationObjectResponse = await query.cache.queryGetObject(
726
+ obligationId,
727
+ { showContent: true }
728
+ );
745
729
  let obligationLocked = false;
746
730
  if (
747
731
  obligationObjectResponse.data &&
@@ -772,9 +756,14 @@ export const queryObligation = async (
772
756
  ) => {
773
757
  const packageId = query.address.get('core.packages.query.id');
774
758
  const queryTarget = `${packageId}::obligation_query::obligation_data`;
775
- const txBlock = new SuiKitTxBlock();
776
- txBlock.moveCall(queryTarget, [obligationId]);
777
- const queryResult = await query.suiKit.inspectTxn(txBlock);
759
+ const args = [obligationId];
760
+
761
+ // const txBlock = new SuiKitTxBlock();
762
+ // txBlock.moveCall(queryTarget, args);
763
+ const queryResult = await query.cache.queryInspectTxn(
764
+ { queryTarget, args }
765
+ // txBlock
766
+ );
778
767
  return queryResult.events[0].parsedJson as ObligationQueryInterface;
779
768
  };
780
769
 
@@ -797,9 +786,8 @@ export const getCoinAmounts = async (
797
786
  let hasNextPage = false;
798
787
  let nextCursor: string | null | undefined = null;
799
788
  do {
800
- const paginatedCoinObjectsResponse = await query.suiKit
801
- .client()
802
- .getOwnedObjects({
789
+ const paginatedCoinObjectsResponse = await query.cache.queryGetOwnedObjects(
790
+ {
803
791
  owner,
804
792
  filter: {
805
793
  MatchAny: assetCoinNames.map((assetCoinName) => {
@@ -812,7 +800,8 @@ export const getCoinAmounts = async (
812
800
  showContent: true,
813
801
  },
814
802
  cursor: nextCursor,
815
- });
803
+ }
804
+ );
816
805
 
817
806
  coinObjectsResponse.push(...paginatedCoinObjectsResponse.data);
818
807
  if (
@@ -869,16 +858,16 @@ export const getCoinAmount = async (
869
858
  let hasNextPage = false;
870
859
  let nextCursor: string | null | undefined = null;
871
860
  do {
872
- const paginatedCoinObjectsResponse = await query.suiKit
873
- .client()
874
- .getOwnedObjects({
861
+ const paginatedCoinObjectsResponse = await query.cache.queryGetOwnedObjects(
862
+ {
875
863
  owner,
876
864
  filter: { StructType: `0x2::coin::Coin<${coinType}>` },
877
865
  options: {
878
866
  showContent: true,
879
867
  },
880
868
  cursor: nextCursor,
881
- });
869
+ }
870
+ );
882
871
 
883
872
  coinObjectsResponse.push(...paginatedCoinObjectsResponse.data);
884
873
  if (
@@ -932,9 +921,8 @@ export const getMarketCoinAmounts = async (
932
921
  let hasNextPage = false;
933
922
  let nextCursor: string | null | undefined = null;
934
923
  do {
935
- const paginatedMarketCoinObjectsResponse = await query.suiKit
936
- .client()
937
- .getOwnedObjects({
924
+ const paginatedMarketCoinObjectsResponse =
925
+ await query.cache.queryGetOwnedObjects({
938
926
  owner,
939
927
  filter: {
940
928
  MatchAny: marketCoinNames.map((marketCoinName) => {
@@ -1007,9 +995,8 @@ export const getMarketCoinAmount = async (
1007
995
  let hasNextPage = false;
1008
996
  let nextCursor: string | null | undefined = null;
1009
997
  do {
1010
- const paginatedMarketCoinObjectsResponse = await query.suiKit
1011
- .client()
1012
- .getOwnedObjects({
998
+ const paginatedMarketCoinObjectsResponse =
999
+ await query.cache.queryGetOwnedObjects({
1013
1000
  owner,
1014
1001
  filter: { StructType: `0x2::coin::Coin<${marketCoinType}>` },
1015
1002
  options: {
@@ -15,12 +15,10 @@ export const getPythPrice = async (
15
15
  const pythFeedObjectId = query.address.get(
16
16
  `core.coins.${assetCoinName}.oracle.pyth.feedObject`
17
17
  );
18
- const priceFeedObjectResponse = await query.suiKit.client().getObject({
19
- id: pythFeedObjectId,
20
- options: {
21
- showContent: true,
22
- },
23
- });
18
+ const priceFeedObjectResponse = await query.cache.queryGetObject(
19
+ pythFeedObjectId,
20
+ { showContent: true }
21
+ );
24
22
 
25
23
  if (priceFeedObjectResponse.data) {
26
24
  const priceFeedPoolObject = priceFeedObjectResponse.data;
@@ -245,9 +245,8 @@ export const getStakeAccounts = async (
245
245
  let hasNextPage = false;
246
246
  let nextCursor: string | null | undefined = null;
247
247
  do {
248
- const paginatedStakeObjectsResponse = await query.suiKit
249
- .client()
250
- .getOwnedObjects({
248
+ const paginatedStakeObjectsResponse =
249
+ await query.cache.queryGetOwnedObjects({
251
250
  owner,
252
251
  filter: { StructType: stakeAccountType },
253
252
  options: {
@@ -298,7 +297,7 @@ export const getStakeAccounts = async (
298
297
  const stakeObjectIds: string[] = stakeObjectsResponse
299
298
  .map((ref: any) => ref?.data?.objectId)
300
299
  .filter((id: any) => id !== undefined);
301
- const stakeObjects = await query.suiKit.getObjects(stakeObjectIds);
300
+ const stakeObjects = await query.cache.queryGetObjects(stakeObjectIds);
302
301
  for (const stakeObject of stakeObjects) {
303
302
  const id = stakeObject.objectId;
304
303
  const type = stakeObject.type!;
@@ -421,12 +420,9 @@ export const getStakePool = async (
421
420
  ) => {
422
421
  const poolId = query.address.get(`spool.pools.${marketCoinName}.id`);
423
422
  let stakePool: StakePool | undefined = undefined;
424
- const stakePoolObjectResponse = await query.suiKit.client().getObject({
425
- id: poolId,
426
- options: {
427
- showContent: true,
428
- showType: true,
429
- },
423
+ const stakePoolObjectResponse = await query.cache.queryGetObject(poolId, {
424
+ showContent: true,
425
+ showType: true,
430
426
  });
431
427
  if (stakePoolObjectResponse.data) {
432
428
  const stakePoolObject = stakePoolObjectResponse.data;
@@ -482,13 +478,14 @@ export const getStakeRewardPool = async (
482
478
  `spool.pools.${marketCoinName}.rewardPoolId`
483
479
  );
484
480
  let stakeRewardPool: StakeRewardPool | undefined = undefined;
485
- const stakeRewardPoolObjectResponse = await query.suiKit.client().getObject({
486
- id: poolId,
487
- options: {
481
+ const stakeRewardPoolObjectResponse = await query.cache.queryGetObject(
482
+ poolId,
483
+ {
488
484
  showContent: true,
489
485
  showType: true,
490
- },
491
- });
486
+ }
487
+ );
488
+
492
489
  if (stakeRewardPoolObjectResponse.data) {
493
490
  const stakeRewardPoolObject = stakeRewardPoolObjectResponse.data;
494
491
  const id = stakeRewardPoolObject.objectId;
@@ -2,7 +2,7 @@ import BigNumber from 'bignumber.js';
2
2
  import { Vesca } from '../types';
3
3
  import type { SuiObjectResponse, SuiObjectData } from '@mysten/sui.js/client';
4
4
  import type { ScallopQuery } from '../models';
5
- import { IS_VE_SCA_TEST } from 'src/constants';
5
+ import { IS_VE_SCA_TEST, MAX_LOCK_DURATION } from 'src/constants';
6
6
  /**
7
7
  * Query all owned veSca key.
8
8
  *
@@ -91,9 +91,8 @@ export const getVeSca = async (
91
91
 
92
92
  let vesca: Vesca | undefined = undefined;
93
93
 
94
- const veScaDynamicFieldObjectResponse = await query.suiKit
95
- .client()
96
- .getDynamicFieldObject({
94
+ const veScaDynamicFieldObjectResponse =
95
+ await query.cache.queryGetDynamicFieldObject({
97
96
  parentId: tableId,
98
97
  name: {
99
98
  type: '0x2::object::ID',
@@ -109,13 +108,26 @@ export const getVeSca = async (
109
108
  ) {
110
109
  const dynamicFields = (veScaDynamicFieldObject.content.fields as any).value
111
110
  .fields;
111
+
112
+ const remainingLockPeriodInMilliseconds = Math.max(
113
+ +dynamicFields.unlock_at * 1000 - Date.now(),
114
+ 0
115
+ );
116
+ const lockedScaAmount = String(dynamicFields.locked_sca_amount);
117
+ const lockedScaCoin = BigNumber(dynamicFields.locked_sca_amount)
118
+ .shiftedBy(-9)
119
+ .toNumber();
120
+ const currentVeScaBalance =
121
+ lockedScaCoin *
122
+ (Math.floor(remainingLockPeriodInMilliseconds / 1000) /
123
+ MAX_LOCK_DURATION);
124
+
112
125
  vesca = {
113
126
  id: veScaDynamicFieldObject.objectId,
114
127
  keyId: veScaKeyId,
115
- lockedScaAmount: BigNumber(dynamicFields.locked_sca_amount).toNumber(),
116
- lockedScaCoin: BigNumber(dynamicFields.locked_sca_amount)
117
- .shiftedBy(-9)
118
- .toNumber(),
128
+ lockedScaAmount,
129
+ lockedScaCoin,
130
+ currentVeScaBalance,
119
131
  unlockAt: BigNumber(dynamicFields.unlock_at).toNumber(),
120
132
  } as Vesca;
121
133
  }
@@ -7,6 +7,7 @@ import type {
7
7
  ScallopUtils,
8
8
  ScallopBuilder,
9
9
  } from '../models';
10
+ import { ScallopCache } from 'src/models/scallopCache';
10
11
 
11
12
  export type ScallopClientFnReturnType<T extends boolean> = T extends true
12
13
  ? SuiTransactionBlockResponse
@@ -18,6 +19,7 @@ export type ScallopInstanceParams = {
18
19
  query?: ScallopQuery;
19
20
  utils?: ScallopUtils;
20
21
  builder?: ScallopBuilder;
22
+ cache: ScallopCache;
21
23
  };
22
24
 
23
25
  export type ScallopAddressParams = {