@subwallet/extension-base 1.3.46-0 → 1.3.47-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 (68) hide show
  1. package/cjs/koni/api/nft/ordinal_nft/index.js +3 -2
  2. package/cjs/koni/background/handlers/State.js +3 -0
  3. package/cjs/packageInfo.js +1 -1
  4. package/cjs/services/balance-service/helpers/subscribe/substrate/index.js +8 -14
  5. package/cjs/services/buy-service/index.js +2 -0
  6. package/cjs/services/chain-service/utils/index.js +3 -0
  7. package/cjs/services/chain-service/utils/patch.js +1 -1
  8. package/cjs/services/earning-service/handlers/native-staking/amplitude.js +32 -0
  9. package/cjs/services/earning-service/handlers/native-staking/astar.js +18 -0
  10. package/cjs/services/earning-service/handlers/native-staking/base.js +37 -29
  11. package/cjs/services/earning-service/handlers/native-staking/dtao.js +5 -0
  12. package/cjs/services/earning-service/handlers/native-staking/mythos.js +28 -0
  13. package/cjs/services/earning-service/handlers/native-staking/para-chain.js +17 -0
  14. package/cjs/services/earning-service/handlers/native-staking/relay-chain.js +16 -0
  15. package/cjs/services/earning-service/handlers/native-staking/tao.js +5 -0
  16. package/cjs/services/earning-service/service.js +26 -5
  17. package/cjs/services/history-service/index.js +12 -7
  18. package/cjs/services/subscan-service/index.js +35 -104
  19. package/cjs/services/transaction-service/utils.js +10 -1
  20. package/cjs/strategy/api-request-strategy/index.js +1 -0
  21. package/cjs/strategy/api-request-strategy/utils/index.js +2 -2
  22. package/cjs/strategy/api-request-strategy-v2/index.js +138 -0
  23. package/cjs/strategy/api-request-strategy-v2/types.js +1 -0
  24. package/cjs/utils/gear/combine.js +4 -3
  25. package/cjs/utils/gear/vft.js +104 -135
  26. package/koni/api/nft/ordinal_nft/index.js +3 -2
  27. package/koni/background/handlers/State.js +4 -1
  28. package/package.json +20 -9
  29. package/packageInfo.js +1 -1
  30. package/services/balance-service/helpers/subscribe/substrate/index.js +8 -14
  31. package/services/buy-service/index.js +2 -0
  32. package/services/chain-service/utils/index.js +3 -0
  33. package/services/chain-service/utils/patch.js +1 -1
  34. package/services/earning-service/handlers/native-staking/amplitude.d.ts +1 -0
  35. package/services/earning-service/handlers/native-staking/amplitude.js +32 -0
  36. package/services/earning-service/handlers/native-staking/astar.d.ts +1 -0
  37. package/services/earning-service/handlers/native-staking/astar.js +18 -0
  38. package/services/earning-service/handlers/native-staking/base.d.ts +1 -0
  39. package/services/earning-service/handlers/native-staking/base.js +37 -29
  40. package/services/earning-service/handlers/native-staking/dtao.d.ts +1 -0
  41. package/services/earning-service/handlers/native-staking/dtao.js +5 -0
  42. package/services/earning-service/handlers/native-staking/mythos.d.ts +1 -0
  43. package/services/earning-service/handlers/native-staking/mythos.js +28 -0
  44. package/services/earning-service/handlers/native-staking/para-chain.d.ts +1 -0
  45. package/services/earning-service/handlers/native-staking/para-chain.js +17 -0
  46. package/services/earning-service/handlers/native-staking/relay-chain.d.ts +1 -0
  47. package/services/earning-service/handlers/native-staking/relay-chain.js +16 -0
  48. package/services/earning-service/handlers/native-staking/tao.d.ts +1 -0
  49. package/services/earning-service/handlers/native-staking/tao.js +5 -0
  50. package/services/earning-service/service.d.ts +1 -0
  51. package/services/earning-service/service.js +26 -5
  52. package/services/history-service/index.js +12 -7
  53. package/services/subscan-service/index.d.ts +13 -27
  54. package/services/subscan-service/index.js +26 -95
  55. package/services/transaction-service/utils.js +11 -2
  56. package/strategy/api-request-strategy/context/base.d.ts +2 -6
  57. package/strategy/api-request-strategy/index.js +1 -0
  58. package/strategy/api-request-strategy/types.d.ts +4 -2
  59. package/strategy/api-request-strategy/utils/index.js +2 -2
  60. package/strategy/api-request-strategy-v2/index.d.ts +22 -0
  61. package/strategy/api-request-strategy-v2/index.js +128 -0
  62. package/strategy/api-request-strategy-v2/types.d.ts +11 -0
  63. package/strategy/api-request-strategy-v2/types.js +1 -0
  64. package/types/buy.d.ts +1 -1
  65. package/utils/gear/combine.d.ts +2 -1
  66. package/utils/gear/combine.js +4 -4
  67. package/utils/gear/vft.d.ts +20 -9
  68. package/utils/gear/vft.js +104 -135
@@ -119,6 +119,34 @@ export default class MythosNativeStakingPoolHandler extends BaseParaStakingPoolH
119
119
 
120
120
  /* Subscribe pool position */
121
121
 
122
+ async checkAccountHaveStake(useAddresses) {
123
+ var _substrateApi$api$que, _substrateApi$api$que2, _substrateApi$api$que3;
124
+ const result = [];
125
+ const substrateApi = await this.substrateApi.isReady;
126
+ const ledgers = await ((_substrateApi$api$que = substrateApi.api.query.collatorStaking) === null || _substrateApi$api$que === void 0 ? void 0 : (_substrateApi$api$que2 = _substrateApi$api$que.userStake) === null || _substrateApi$api$que2 === void 0 ? void 0 : (_substrateApi$api$que3 = _substrateApi$api$que2.multi) === null || _substrateApi$api$que3 === void 0 ? void 0 : _substrateApi$api$que3.call(_substrateApi$api$que2, useAddresses));
127
+ const _unstakings = await Promise.all(useAddresses.map(stakerAddress => {
128
+ var _substrateApi$api$que4, _substrateApi$api$que5;
129
+ return (_substrateApi$api$que4 = substrateApi.api.query.collatorStaking) === null || _substrateApi$api$que4 === void 0 ? void 0 : (_substrateApi$api$que5 = _substrateApi$api$que4.releaseQueues) === null || _substrateApi$api$que5 === void 0 ? void 0 : _substrateApi$api$que5.call(_substrateApi$api$que4, stakerAddress);
130
+ }));
131
+ if (!ledgers || !_unstakings) {
132
+ return [];
133
+ }
134
+ for (let i = 0; i < useAddresses.length; i++) {
135
+ const owner = useAddresses[i];
136
+ const _userStake = ledgers[i];
137
+ const userStake = _userStake.toPrimitive();
138
+ const _unstaking = _unstakings[i];
139
+ const unstakings = _unstaking.toPrimitive();
140
+
141
+ // TODO: Need to improve, check if can only load stake info
142
+ if (userStake && userStake.stake !== '0') {
143
+ result.push(owner);
144
+ } else if (unstakings && unstakings.some(unstake => unstake.amount !== '0')) {
145
+ result.push(owner);
146
+ }
147
+ }
148
+ return result;
149
+ }
122
150
  async subscribePoolPosition(useAddresses, resultCallback) {
123
151
  let cancel = false;
124
152
  const substrateApi = await this.substrateApi.isReady;
@@ -7,6 +7,7 @@ export default class ParaNativeStakingPoolHandler extends BaseParaNativeStakingP
7
7
  subscribePoolInfo(callback: (data: YieldPoolInfo) => void): Promise<VoidFunction>;
8
8
  parseNominatorMetadata(chainInfo: _ChainInfo, address: string, substrateApi: _SubstrateApi, delegatorState: PalletParachainStakingDelegator): Promise<Omit<YieldPositionInfo, keyof BaseYieldPositionInfo>>;
9
9
  subscribePoolPosition(useAddresses: string[], resultCallback: (rs: YieldPositionInfo) => void): Promise<VoidFunction>;
10
+ checkAccountHaveStake(useAddresses: string[]): Promise<string[]>;
10
11
  getMantaPoolTargets(): Promise<ValidatorInfo[]>;
11
12
  getParachainPoolTargets(): Promise<ValidatorInfo[]>;
12
13
  getPoolTargets(): Promise<ValidatorInfo[]>;
@@ -268,6 +268,23 @@ export default class ParaNativeStakingPoolHandler extends BaseParaNativeStakingP
268
268
  unsub();
269
269
  };
270
270
  }
271
+ async checkAccountHaveStake(useAddresses) {
272
+ var _substrateApi$api$que, _substrateApi$api$que2, _substrateApi$api$que3;
273
+ const result = [];
274
+ const substrateApi = await this.substrateApi.isReady;
275
+ const ledgers = await ((_substrateApi$api$que = substrateApi.api.query.parachainStaking) === null || _substrateApi$api$que === void 0 ? void 0 : (_substrateApi$api$que2 = _substrateApi$api$que.delegatorState) === null || _substrateApi$api$que2 === void 0 ? void 0 : (_substrateApi$api$que3 = _substrateApi$api$que2.multi) === null || _substrateApi$api$que3 === void 0 ? void 0 : _substrateApi$api$que3.call(_substrateApi$api$que2, useAddresses));
276
+ if (!ledgers) {
277
+ return [];
278
+ }
279
+ for (let i = 0; i < useAddresses.length; i++) {
280
+ const owner = useAddresses[i];
281
+ const delegatorState = ledgers[i].toPrimitive();
282
+ if (delegatorState && delegatorState.total > 0) {
283
+ result.push(owner);
284
+ }
285
+ }
286
+ return result;
287
+ }
271
288
 
272
289
  /* Subscribe pool position */
273
290
 
@@ -12,6 +12,7 @@ export default class RelayNativeStakingPoolHandler extends BaseNativeStakingPool
12
12
  parseNominatorMetadata(chainInfo: _ChainInfo, address: string, substrateApi: _SubstrateApi, ledger: PalletStakingStakingLedger, currentEra: string, minStake: BN, _deriveSessionProgress: DeriveSessionProgress): Promise<Omit<YieldPositionInfo, keyof BaseYieldPositionInfo>>;
13
13
  handleNominationsList(substrateApi: _SubstrateApi, chain: string, nominations: PalletStakingNominations, currentEra: string, address: string, maxNominatorRewardedPerValidator: number | undefined): Promise<NominationInfo[]>;
14
14
  subscribePoolPosition(useAddresses: string[], resultCallback: (rs: YieldPositionInfo) => void): Promise<VoidFunction>;
15
+ checkAccountHaveStake(useAddresses: string[]): Promise<string[]>;
15
16
  getPoolTargets(): Promise<ValidatorInfo[]>;
16
17
  private getValidatorExpectedReturn;
17
18
  private parseEraStakerData;
@@ -283,6 +283,22 @@ export default class RelayNativeStakingPoolHandler extends BaseNativeStakingPool
283
283
  unsub === null || unsub === void 0 ? void 0 : unsub();
284
284
  };
285
285
  }
286
+ async checkAccountHaveStake(useAddresses) {
287
+ var _substrateApi$api$que22, _substrateApi$api$que23, _substrateApi$api$que24;
288
+ const result = [];
289
+ const substrateApi = await this.substrateApi.isReady;
290
+ const ledgers = await ((_substrateApi$api$que22 = substrateApi.api.query.staking) === null || _substrateApi$api$que22 === void 0 ? void 0 : (_substrateApi$api$que23 = _substrateApi$api$que22.ledger) === null || _substrateApi$api$que23 === void 0 ? void 0 : (_substrateApi$api$que24 = _substrateApi$api$que23.multi) === null || _substrateApi$api$que24 === void 0 ? void 0 : _substrateApi$api$que24.call(_substrateApi$api$que23, useAddresses));
291
+ if (ledgers) {
292
+ for (let i = 0; i < useAddresses.length; i++) {
293
+ const address = useAddresses[i];
294
+ const _ledger = ledgers[i].toPrimitive();
295
+ if (_ledger.total > 0) {
296
+ result.push(address);
297
+ }
298
+ }
299
+ }
300
+ return result;
301
+ }
286
302
 
287
303
  /* Subscribe pool position */
288
304
 
@@ -57,6 +57,7 @@ export default class TaoNativeStakingPoolHandler extends BaseParaStakingPoolHand
57
57
  subscribePoolInfo(callback: (data: YieldPoolInfo) => void): Promise<VoidFunction>;
58
58
  parseNominatorMetadata(chainInfo: _ChainInfo, address: string, delegatorState: TaoStakingStakeOption[]): Promise<Omit<YieldPositionInfo, keyof BaseYieldPositionInfo>>;
59
59
  subscribePoolPosition(useAddresses: string[], rsCallback: (rs: YieldPositionInfo) => void): Promise<VoidFunction>;
60
+ checkAccountHaveStake(useAddresses: string[]): Promise<string[]>;
60
61
  private getDevnetPoolTargets;
61
62
  private getMainnetPoolTargets;
62
63
  getPoolTargets(): Promise<ValidatorInfo[]>;
@@ -380,6 +380,11 @@ export default class TaoNativeStakingPoolHandler extends BaseParaStakingPoolHand
380
380
  };
381
381
  }
382
382
 
383
+ // Because not have subscan api
384
+ async checkAccountHaveStake(useAddresses) {
385
+ return Promise.resolve([]);
386
+ }
387
+
383
388
  /* Subscribe pool position */
384
389
 
385
390
  /* Get pool targets */
@@ -83,6 +83,7 @@ export default class EarningService implements StoppableServiceInterface, Persis
83
83
  subscribeEarningRewardHistory(): BehaviorSubject<Record<string, EarningRewardHistoryItem>>;
84
84
  getEarningRewardHistory(): Record<string, EarningRewardHistoryItem>;
85
85
  earningsRewardHistoryInterval: NodeJS.Timer | undefined;
86
+ private unSubFetchEarningRewardHistory;
86
87
  runSubscribeEarningRewardHistoryInterval(): void;
87
88
  runUnsubscribeEarningRewardHistoryInterval(): void;
88
89
  /**
@@ -660,18 +660,39 @@ export default class EarningService {
660
660
  if (!addresses.length) {
661
661
  return;
662
662
  }
663
- this.fetchPoolRewardHistory(addresses, result => {
664
- this.updateEarningRewardHistory(result);
665
- }).catch(console.error);
666
- this.earningsRewardHistoryInterval = setInterval(() => {
663
+ let cancel = false;
664
+ let unsub;
665
+ this.unSubFetchEarningRewardHistory = () => {
666
+ if (!cancel) {
667
+ var _unsub2;
668
+ (_unsub2 = unsub) === null || _unsub2 === void 0 ? void 0 : _unsub2();
669
+ cancel = true;
670
+ }
671
+ };
672
+ const fetchData = () => {
667
673
  this.fetchPoolRewardHistory(addresses, result => {
674
+ if (cancel) {
675
+ return;
676
+ }
668
677
  this.updateEarningRewardHistory(result);
678
+ }).then(_unsub => {
679
+ if (!cancel) {
680
+ var _unsub3;
681
+ (_unsub3 = unsub) === null || _unsub3 === void 0 ? void 0 : _unsub3();
682
+ unsub = _unsub;
683
+ }
669
684
  }).catch(console.error);
670
- }, CRON_REFRESH_EARNING_REWARD_HISTORY_INTERVAL);
685
+ };
686
+ if (!cancel) {
687
+ fetchData();
688
+ }
689
+ this.earningsRewardHistoryInterval = setInterval(fetchData, CRON_REFRESH_EARNING_REWARD_HISTORY_INTERVAL);
671
690
  }
672
691
  runUnsubscribeEarningRewardHistoryInterval() {
692
+ var _this$unSubFetchEarni;
673
693
  removeLazy('updateEarningRewardHistory');
674
694
  this.earningRewardHistoryQueue = [];
695
+ (_this$unSubFetchEarni = this.unSubFetchEarningRewardHistory) === null || _this$unSubFetchEarni === void 0 ? void 0 : _this$unSubFetchEarni.call(this);
675
696
  this.earningsRewardHistoryInterval && clearInterval(this.earningsRewardHistoryInterval);
676
697
  }
677
698
 
@@ -74,7 +74,7 @@ export class HistoryService {
74
74
  /**
75
75
  * @todo: Must improve performance of this function
76
76
  * */
77
- fetchSubscanTransactionHistory(chain, addresses) {
77
+ fetchSubscanTransactionHistory(chain, addresses, groupId) {
78
78
  if (!this.subscanService.checkSupportedSubscanChain(chain) || !addresses.length) {
79
79
  return;
80
80
  }
@@ -87,7 +87,7 @@ export class HistoryService {
87
87
  // However, fetchAllPossibleTransferItems-sent must run after fetchAllPossibleExtrinsicItems,
88
88
  // to avoid "duplicate Extrinsic Hash between items" problem
89
89
 
90
- this.subscanService.fetchAllPossibleExtrinsicItems(chain, address, extrinsicItems => {
90
+ this.subscanService.fetchAllPossibleExtrinsicItems(groupId, chain, address, extrinsicItems => {
91
91
  const result = [];
92
92
  extrinsicItems.forEach(x => {
93
93
  const item = parseSubscanExtrinsicData(address, x, chainInfo);
@@ -105,7 +105,7 @@ export class HistoryService {
105
105
  excludeTransferExtrinsicHash.push(x.extrinsic_hash);
106
106
  }
107
107
  });
108
- this.subscanService.fetchAllPossibleTransferItems(chain, address, 'sent').then(rsMap => {
108
+ this.subscanService.fetchAllPossibleTransferItems(groupId, chain, address, 'sent').then(rsMap => {
109
109
  const result = [];
110
110
  Object.keys(rsMap).forEach(hash => {
111
111
  // only push item that does not have same hash with another item
@@ -125,7 +125,7 @@ export class HistoryService {
125
125
  }).catch(e => {
126
126
  console.log('fetchAllPossibleExtrinsicItems error', e);
127
127
  });
128
- this.subscanService.fetchAllPossibleTransferItems(chain, address, 'received').then(rsMap => {
128
+ this.subscanService.fetchAllPossibleTransferItems(groupId, chain, address, 'received').then(rsMap => {
129
129
  const result = [];
130
130
  Object.keys(rsMap).forEach(hash => {
131
131
  // only push item that does not have same hash with another item
@@ -172,14 +172,19 @@ export class HistoryService {
172
172
  const evmAddresses = getAddressesByChainType(addresses, [ChainType.EVM]);
173
173
  const substrateAddresses = getAddressesByChainType(addresses, [ChainType.SUBSTRATE]);
174
174
  const bitcoinAddresses = getAddressesByChainType(addresses, [ChainType.BITCOIN], chainInfo);
175
+ const groupId = this.subscanService.getGroupId();
175
176
  const subscription = this.historySubject.subscribe(items => {
176
177
  cb(items.filter(filterHistoryItemByAddressAndChain(chain, addresses)));
177
178
  });
179
+ const unsubscribe = () => {
180
+ subscription.unsubscribe();
181
+ this.subscanService.cancelGroupRequest(groupId);
182
+ };
178
183
  if (_isChainSubstrateCompatible(chainInfo)) {
179
184
  if (_isChainEvmCompatible(chainInfo)) {
180
- this.fetchSubscanTransactionHistory(chain, evmAddresses);
185
+ this.fetchSubscanTransactionHistory(chain, evmAddresses, groupId);
181
186
  } else {
182
- this.fetchSubscanTransactionHistory(chain, substrateAddresses);
187
+ this.fetchSubscanTransactionHistory(chain, substrateAddresses, groupId);
183
188
  }
184
189
  } else if (_isChainBitcoinCompatible(chainInfo)) {
185
190
  this.fetchBitcoinTransactionHistory(chain, bitcoinAddresses).catch(e => {
@@ -187,7 +192,7 @@ export class HistoryService {
187
192
  });
188
193
  }
189
194
  return {
190
- unsubscribe: subscription.unsubscribe,
195
+ unsubscribe,
191
196
  value: this.historySubject.getValue().filter(filterHistoryItemByAddressAndChain(chain, addresses))
192
197
  };
193
198
  }
@@ -1,44 +1,30 @@
1
- import { CrowdloanContributionsResponse, ExtrinsicItem, ExtrinsicsListResponse, IMultiChainBalance, RequestBlockRange, RewardHistoryListResponse, SubscanRequest, TransferItem, TransfersListResponse } from '@subwallet/extension-base/services/subscan-service/types';
1
+ import { CrowdloanContributionsResponse, ExtrinsicItem, ExtrinsicsListResponse, IMultiChainBalance, RequestBlockRange, RewardHistoryListResponse, TransferItem, TransfersListResponse } from '@subwallet/extension-base/services/subscan-service/types';
2
+ import { ApiRequestContextProps } from '@subwallet/extension-base/strategy/api-request-strategy/types';
3
+ import { BaseApiRequestStrategyV2 } from '@subwallet/extension-base/strategy/api-request-strategy-v2';
2
4
  import { SubscanEventBaseItemData, SubscanExtrinsicParam } from '@subwallet/extension-base/types';
3
- export declare class SubscanService {
5
+ export declare class SubscanService extends BaseApiRequestStrategyV2 {
4
6
  private subscanChainMap;
5
- private callRate;
6
- private limitRate;
7
- private intervalCheck;
8
- private maxRetry;
9
- private rollbackRateTime;
10
- private timeoutRollbackRate;
11
- private requestMap;
12
- private nextId;
13
- private isRunning;
14
- private getId;
15
- constructor(subscanChainMap: Record<string, string>, options?: {
16
- limitRate?: number;
17
- intervalCheck?: number;
18
- maxRetry?: number;
19
- });
20
- private reduceLimitRate;
7
+ constructor(subscanChainMap: Record<string, string>, options?: Partial<ApiRequestContextProps>);
21
8
  private getApiUrl;
22
9
  private postRequest;
23
- addRequest<T>(run: SubscanRequest<T>['run'], ordinal: number): Promise<T>;
24
- private process;
10
+ isRateLimited(e: Error): boolean;
25
11
  checkSupportedSubscanChain(chain: string): boolean;
26
12
  setSubscanChainMap(subscanChainMap: Record<string, string>): void;
27
13
  getMultiChainBalance(address: string): Promise<IMultiChainBalance[]>;
28
14
  getCrowdloanContributions(relayChain: string, address: string, page?: number): Promise<CrowdloanContributionsResponse>;
29
- getExtrinsicsList(chain: string, address: string, page?: number, blockRange?: RequestBlockRange): Promise<ExtrinsicsListResponse>;
30
- fetchAllPossibleExtrinsicItems(chain: string, address: string, cbAfterEachRequest?: (items: ExtrinsicItem[]) => void, limit?: {
15
+ getExtrinsicsList(groupId: number, chain: string, address: string, page?: number, blockRange?: RequestBlockRange): Promise<ExtrinsicsListResponse>;
16
+ fetchAllPossibleExtrinsicItems(groupId: number, chain: string, address: string, cbAfterEachRequest?: (items: ExtrinsicItem[]) => void, limit?: {
31
17
  page: number;
32
18
  record: number;
33
19
  }): Promise<ExtrinsicItem[]>;
34
- getTransfersList(chain: string, address: string, page?: number, direction?: 'sent' | 'received', blockRange?: RequestBlockRange): Promise<TransfersListResponse>;
35
- fetchAllPossibleTransferItems(chain: string, address: string, direction?: 'sent' | 'received', cbAfterEachRequest?: (items: TransferItem[]) => void, limit?: {
20
+ getTransfersList(groupId: number, chain: string, address: string, page?: number, direction?: 'sent' | 'received', blockRange?: RequestBlockRange): Promise<TransfersListResponse>;
21
+ fetchAllPossibleTransferItems(groupId: number, chain: string, address: string, direction?: 'sent' | 'received', cbAfterEachRequest?: (items: TransferItem[]) => void, limit?: {
36
22
  page: number;
37
23
  record: number;
38
24
  }): Promise<Record<string, TransferItem[]>>;
39
- getRewardHistoryList(chain: string, address: string, page?: number): Promise<RewardHistoryListResponse>;
40
- getAccountRemarkEvents(chain: string, address: string): Promise<SubscanEventBaseItemData[]>;
41
- getExtrinsicParams(chain: string, extrinsicIndexes: string[], ordinal?: number): Promise<SubscanExtrinsicParam[]>;
25
+ getRewardHistoryList(groupId: number, chain: string, address: string, page?: number): Promise<RewardHistoryListResponse>;
26
+ getAccountRemarkEvents(groupId: number, chain: string, address: string): Promise<SubscanEventBaseItemData[]>;
27
+ getExtrinsicParams(groupId: number, chain: string, extrinsicIndexes: string[], ordinal?: number): Promise<SubscanExtrinsicParam[]>;
42
28
  private static _instance;
43
29
  static getInstance(): SubscanService;
44
30
  }
@@ -4,34 +4,15 @@
4
4
  import { SWError } from '@subwallet/extension-base/background/errors/SWError';
5
5
  import { BASE_FETCH_ORDINAL_EVENT_DATA } from '@subwallet/extension-base/koni/api/nft/ordinal_nft/constants';
6
6
  import { SUBSCAN_API_CHAIN_MAP } from '@subwallet/extension-base/services/subscan-service/subscan-chain-map';
7
+ import { BaseApiRequestContext } from '@subwallet/extension-base/strategy/api-request-strategy/context/base';
8
+ import { BaseApiRequestStrategyV2 } from '@subwallet/extension-base/strategy/api-request-strategy-v2';
7
9
  import { wait } from '@subwallet/extension-base/utils';
8
10
  const QUERY_ROW = 100;
9
- export class SubscanService {
10
- callRate = 2; // limit per interval check
11
- limitRate = 2; // max rate per interval check
12
- intervalCheck = 1000; // interval check in ms
13
- maxRetry = 9; // interval check in ms
14
- rollbackRateTime = 30 * 1000; // rollback rate time in ms
15
- timeoutRollbackRate = undefined;
16
- requestMap = {};
17
- nextId = 0;
18
- isRunning = false;
19
- getId() {
20
- return this.nextId++;
21
- }
11
+ export class SubscanService extends BaseApiRequestStrategyV2 {
22
12
  constructor(subscanChainMap, options) {
13
+ const context = new BaseApiRequestContext(options);
14
+ super(context);
23
15
  this.subscanChainMap = subscanChainMap;
24
- this.callRate = (options === null || options === void 0 ? void 0 : options.limitRate) || this.callRate;
25
- this.limitRate = (options === null || options === void 0 ? void 0 : options.limitRate) || this.limitRate;
26
- this.intervalCheck = (options === null || options === void 0 ? void 0 : options.intervalCheck) || this.intervalCheck;
27
- this.maxRetry = (options === null || options === void 0 ? void 0 : options.maxRetry) || this.maxRetry;
28
- }
29
- reduceLimitRate() {
30
- clearTimeout(this.timeoutRollbackRate);
31
- this.callRate = Math.ceil(this.limitRate / 2);
32
- this.timeoutRollbackRate = setTimeout(() => {
33
- this.callRate = this.limitRate;
34
- }, this.rollbackRateTime);
35
16
  }
36
17
  getApiUrl(chain, path) {
37
18
  const subscanChain = this.subscanChainMap[chain];
@@ -49,61 +30,9 @@ export class SubscanService {
49
30
  body: JSON.stringify(body)
50
31
  });
51
32
  }
52
- addRequest(run, ordinal) {
53
- const newId = this.getId();
54
- return new Promise((resolve, reject) => {
55
- this.requestMap[newId] = {
56
- id: newId,
57
- status: 'pending',
58
- retry: -1,
59
- ordinal,
60
- run,
61
- resolve,
62
- reject
63
- };
64
- if (!this.isRunning) {
65
- this.process();
66
- }
67
- });
68
- }
69
- process() {
70
- this.isRunning = true;
71
- const maxRetry = this.maxRetry;
72
- const interval = setInterval(() => {
73
- const remainingRequests = Object.values(this.requestMap);
74
- if (remainingRequests.length === 0) {
75
- this.isRunning = false;
76
- clearInterval(interval);
77
- return;
78
- }
79
-
80
- // Get first this.limit requests base on id
81
- const requests = remainingRequests.filter(request => request.status !== 'running').sort((a, b) => a.id - b.id).sort((a, b) => a.ordinal - b.ordinal).slice(0, this.callRate);
82
-
83
- // Start requests
84
- requests.forEach(request => {
85
- request.status = 'running';
86
- request.run().then(rs => {
87
- request.resolve(rs);
88
- }).catch(e => {
89
- const error = JSON.parse(e.message);
90
-
91
- // Limit rate
92
- if (error.code === 20008) {
93
- if (request.retry < maxRetry) {
94
- request.status = 'pending';
95
- request.retry++;
96
- this.reduceLimitRate();
97
- } else {
98
- // Reject request
99
- request.reject(new SWError('MAX_RETRY', String(e)));
100
- }
101
- } else {
102
- request.reject(new SWError('UNKNOWN', String(e)));
103
- }
104
- });
105
- });
106
- }, this.intervalCheck);
33
+ isRateLimited(e) {
34
+ const error = JSON.parse(e.message);
35
+ return error.code === 20008;
107
36
  }
108
37
  checkSupportedSubscanChain(chain) {
109
38
  return !!this.subscanChainMap[chain];
@@ -114,6 +43,7 @@ export class SubscanService {
114
43
 
115
44
  // Implement Subscan API
116
45
  getMultiChainBalance(address) {
46
+ const hashKey = this.createKeyHash(['multi_chain_balance', address]);
117
47
  return this.addRequest(async () => {
118
48
  const rs = await this.postRequest(this.getApiUrl('polkadot', 'api/scan/multiChain/account'), {
119
49
  address
@@ -123,7 +53,7 @@ export class SubscanService {
123
53
  }
124
54
  const jsonData = await rs.json();
125
55
  return jsonData.data;
126
- }, 1);
56
+ }, 1, undefined, hashKey);
127
57
  }
128
58
  getCrowdloanContributions(relayChain, address, page = 0) {
129
59
  return this.addRequest(async () => {
@@ -140,7 +70,7 @@ export class SubscanService {
140
70
  return jsonData.data;
141
71
  }, 2);
142
72
  }
143
- getExtrinsicsList(chain, address, page = 0, blockRange) {
73
+ getExtrinsicsList(groupId, chain, address, page = 0, blockRange) {
144
74
  const _blockRange = (() => {
145
75
  if (!blockRange || !blockRange.to) {
146
76
  return null;
@@ -159,9 +89,9 @@ export class SubscanService {
159
89
  }
160
90
  const jsonData = await rs.json();
161
91
  return jsonData.data;
162
- }, 0);
92
+ }, 0, groupId);
163
93
  }
164
- async fetchAllPossibleExtrinsicItems(chain, address, cbAfterEachRequest, limit = {
94
+ async fetchAllPossibleExtrinsicItems(groupId, chain, address, cbAfterEachRequest, limit = {
165
95
  page: 10,
166
96
  record: 1000
167
97
  }) {
@@ -173,7 +103,7 @@ export class SubscanService {
173
103
  };
174
104
  const resultMap = {};
175
105
  const _getExtrinsicItems = async page => {
176
- const res = await this.getExtrinsicsList(chain, address, page, blockRange);
106
+ const res = await this.getExtrinsicsList(groupId, chain, address, page, blockRange);
177
107
  if (!res || !res.count || !res.extrinsics || !res.extrinsics.length) {
178
108
  return;
179
109
  }
@@ -182,7 +112,7 @@ export class SubscanService {
182
112
  }
183
113
  const extrinsics = res.extrinsics;
184
114
  const extrinsicIndexes = extrinsics.map(item => item.extrinsic_index);
185
- const extrinsicParams = await this.getExtrinsicParams(chain, extrinsicIndexes, 0);
115
+ const extrinsicParams = await this.getExtrinsicParams(groupId, chain, extrinsicIndexes, 0);
186
116
  for (const data of extrinsicParams) {
187
117
  const {
188
118
  extrinsic_index: extrinsicIndex,
@@ -214,7 +144,7 @@ export class SubscanService {
214
144
  await _getExtrinsicItems(0);
215
145
  return Object.values(resultMap);
216
146
  }
217
- getTransfersList(chain, address, page = 0, direction, blockRange) {
147
+ getTransfersList(groupId, chain, address, page = 0, direction, blockRange) {
218
148
  return this.addRequest(async () => {
219
149
  const rs = await this.postRequest(this.getApiUrl(chain, 'api/v2/scan/transfers'), {
220
150
  page,
@@ -229,9 +159,9 @@ export class SubscanService {
229
159
  }
230
160
  const jsonData = await rs.json();
231
161
  return jsonData.data;
232
- }, 0);
162
+ }, 0, groupId);
233
163
  }
234
- async fetchAllPossibleTransferItems(chain, address, direction, cbAfterEachRequest, limit = {
164
+ async fetchAllPossibleTransferItems(groupId, chain, address, direction, cbAfterEachRequest, limit = {
235
165
  page: 10,
236
166
  record: 1000
237
167
  }) {
@@ -243,7 +173,7 @@ export class SubscanService {
243
173
  };
244
174
  const resultMap = {};
245
175
  const _getTransferItems = async page => {
246
- const res = await this.getTransfersList(chain, address, page, direction, blockRange);
176
+ const res = await this.getTransfersList(groupId, chain, address, page, direction, blockRange);
247
177
  if (!res || !res.count || !res.transfers || !res.transfers.length) {
248
178
  return;
249
179
  }
@@ -273,7 +203,8 @@ export class SubscanService {
273
203
  await _getTransferItems(0);
274
204
  return resultMap;
275
205
  }
276
- getRewardHistoryList(chain, address, page = 0) {
206
+ getRewardHistoryList(groupId, chain, address, page = 0) {
207
+ const hashKey = this.createKeyHash([chain, 'reward_slash', address, page]);
277
208
  return this.addRequest(async () => {
278
209
  const rs = await this.postRequest(this.getApiUrl(chain, 'api/scan/account/reward_slash'), {
279
210
  page,
@@ -293,9 +224,9 @@ export class SubscanService {
293
224
  };
294
225
  }
295
226
  return jsonData.data;
296
- }, 2);
227
+ }, 2, groupId, hashKey);
297
228
  }
298
- getAccountRemarkEvents(chain, address) {
229
+ getAccountRemarkEvents(groupId, chain, address) {
299
230
  return this.addRequest(async () => {
300
231
  const rs = await this.postRequest(this.getApiUrl(chain, 'api/v2/scan/events'), {
301
232
  ...BASE_FETCH_ORDINAL_EVENT_DATA,
@@ -306,9 +237,9 @@ export class SubscanService {
306
237
  }
307
238
  const jsonData = await rs.json();
308
239
  return jsonData.data.events;
309
- }, 3);
240
+ }, 3, groupId);
310
241
  }
311
- getExtrinsicParams(chain, extrinsicIndexes, ordinal = 3) {
242
+ getExtrinsicParams(groupId, chain, extrinsicIndexes, ordinal = 3) {
312
243
  return this.addRequest(async () => {
313
244
  const rs = await this.postRequest(this.getApiUrl(chain, 'api/scan/extrinsic/params'), {
314
245
  extrinsic_index: extrinsicIndexes
@@ -318,7 +249,7 @@ export class SubscanService {
318
249
  }
319
250
  const jsonData = await rs.json();
320
251
  return jsonData.data;
321
- }, ordinal);
252
+ }, ordinal, groupId);
322
253
  }
323
254
 
324
255
  // Singleton
@@ -1,7 +1,7 @@
1
1
  // Copyright 2019-2022 @subwallet/extension-base authors & contributors
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
- import { _getBlockExplorerFromChain, _isChainTestNet, _isPureBitcoinChain, _isPureCardanoChain, _isPureEvmChain } from '@subwallet/extension-base/services/chain-service/utils';
4
+ import { _getBlockExplorerFromChain, _isChainTestNet, _isPureBitcoinChain, _isPureCardanoChain, _isPureEvmChain, _isPureTonChain } from '@subwallet/extension-base/services/chain-service/utils';
5
5
  import { CHAIN_FLIP_MAINNET_EXPLORER, CHAIN_FLIP_TESTNET_EXPLORER, SIMPLE_SWAP_EXPLORER } from '@subwallet/extension-base/services/swap-service/utils';
6
6
  import { hexAddPrefix, isHex, u8aToHex } from '@polkadot/util';
7
7
  import { decodeAddress } from '@polkadot/util-crypto';
@@ -37,13 +37,19 @@ function getBlockExplorerAccountRoute(explorerLink) {
37
37
  if (explorerLink.includes('taostats.io')) {
38
38
  return 'account';
39
39
  }
40
+ if (explorerLink.includes('tonviewer.com')) {
41
+ return '';
42
+ }
43
+ if (explorerLink.includes('devnet-explorer.mosaicchain.io')) {
44
+ return 'accounts';
45
+ }
40
46
  return 'address';
41
47
  }
42
48
  function getBlockExplorerTxRoute(chainInfo) {
43
49
  if (_isPureEvmChain(chainInfo) || _isPureBitcoinChain(chainInfo)) {
44
50
  return 'tx';
45
51
  }
46
- if (_isPureCardanoChain(chainInfo)) {
52
+ if (_isPureCardanoChain(chainInfo) || _isPureTonChain(chainInfo)) {
47
53
  return 'transaction';
48
54
  }
49
55
  if (['aventus', 'deeper_network'].includes(chainInfo.slug)) {
@@ -52,6 +58,9 @@ function getBlockExplorerTxRoute(chainInfo) {
52
58
  if (['gen6_public'].includes(chainInfo.slug)) {
53
59
  return '#/extrinsics';
54
60
  }
61
+ if (['mosaicTest'].includes(chainInfo.slug)) {
62
+ return 'transactions';
63
+ }
55
64
  const explorerLink = _getBlockExplorerFromChain(chainInfo);
56
65
  if (explorerLink && explorerLink.includes('statescan.io')) {
57
66
  return '#/extrinsics';
@@ -1,4 +1,4 @@
1
- import { ApiRequestContext } from '@subwallet/extension-base/strategy/api-request-strategy/types';
1
+ import { ApiRequestContext, ApiRequestContextProps } from '@subwallet/extension-base/strategy/api-request-strategy/types';
2
2
  export declare class BaseApiRequestContext implements ApiRequestContext {
3
3
  callRate: number;
4
4
  limitRate: number;
@@ -6,10 +6,6 @@ export declare class BaseApiRequestContext implements ApiRequestContext {
6
6
  maxRetry: number;
7
7
  private rollbackRateTime;
8
8
  private timeoutRollbackRate;
9
- constructor(options?: {
10
- limitRate?: number;
11
- intervalCheck?: number;
12
- maxRetry?: number;
13
- });
9
+ constructor(options?: Partial<ApiRequestContextProps>);
14
10
  reduceLimitRate(): void;
15
11
  }
@@ -50,6 +50,7 @@ export class BaseApiRequestStrategy {
50
50
  request.status = 'running';
51
51
  request.run().then(rs => {
52
52
  request.resolve(rs);
53
+ delete this.requestMap[request.id];
53
54
  }).catch(e => {
54
55
  const isRateLimited = this.isRateLimited(e);
55
56
 
@@ -1,8 +1,10 @@
1
- export interface ApiRequestContext {
2
- callRate: number;
1
+ export interface ApiRequestContextProps {
3
2
  limitRate: number;
4
3
  intervalCheck: number;
5
4
  maxRetry: number;
5
+ }
6
+ export interface ApiRequestContext extends ApiRequestContextProps {
7
+ callRate: number;
6
8
  reduceLimitRate: () => void;
7
9
  }
8
10
  export interface ApiRequestStrategy {
@@ -12,8 +12,8 @@ export const postRequest = (url, body, headers, jsonBody = true) => {
12
12
  });
13
13
  };
14
14
  export const getRequest = (url, params, headers) => {
15
- const queryString = params ? Object.keys(params).map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`).join('&') : '';
16
- const _url = `${url}?${queryString}`;
15
+ const q = new URLSearchParams(params);
16
+ const _url = `${url}?${q.toString()}`;
17
17
  return fetch(_url, {
18
18
  method: 'GET',
19
19
  headers: headers || {
@@ -0,0 +1,22 @@
1
+ import { ApiRequestContext } from '../api-request-strategy/types';
2
+ import { ApiRequestStrategyV2, ApiRequestV2 } from './types';
3
+ export declare abstract class BaseApiRequestStrategyV2 implements ApiRequestStrategyV2 {
4
+ private nextId;
5
+ private groupId;
6
+ private isRunning;
7
+ private requestMap;
8
+ private context;
9
+ private processInterval;
10
+ private canceledGroupIds;
11
+ private cacheMap;
12
+ private getId;
13
+ protected constructor(context: ApiRequestContext);
14
+ getGroupId(): number;
15
+ createKeyHash(keys: Array<string | number>): string;
16
+ addRequest<T>(run: ApiRequestV2<T>['run'], ordinal: number, _groupId?: number, keyHash?: string): Promise<T>;
17
+ abstract isRateLimited(error: Error): boolean;
18
+ private process;
19
+ stop(): void;
20
+ cancelGroupRequest(groupId: number): void;
21
+ setContext(context: ApiRequestContext): void;
22
+ }