@subwallet/extension-base 1.0.5-1 → 1.0.5-3

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 (40) hide show
  1. package/background/KoniTypes.d.ts +2 -1
  2. package/cjs/koni/api/staking/bonding/relayChain.js +2 -7
  3. package/cjs/koni/api/staking/bonding/utils.js +9 -0
  4. package/cjs/koni/background/handlers/Extension.js +4 -2
  5. package/cjs/koni/background/handlers/Tabs.js +24 -23
  6. package/cjs/packageInfo.js +1 -1
  7. package/cjs/services/chain-service/constants.js +2 -2
  8. package/cjs/services/chain-service/index.js +5 -0
  9. package/cjs/services/chain-service/utils.js +15 -10
  10. package/cjs/services/history-service/helpers/recoverHistoryStatus.js +157 -38
  11. package/cjs/services/history-service/index.js +26 -19
  12. package/cjs/services/history-service/subsquid-multi-chain-history.js +2 -2
  13. package/cjs/services/notification-service/NotificationService.js +1 -1
  14. package/cjs/services/storage-service/DatabaseService.js +1 -1
  15. package/cjs/services/transaction-service/index.js +41 -14
  16. package/cjs/services/transaction-service/utils.js +3 -0
  17. package/cjs/utils/index.js +3 -0
  18. package/koni/api/staking/bonding/relayChain.js +3 -8
  19. package/koni/api/staking/bonding/utils.d.ts +1 -0
  20. package/koni/api/staking/bonding/utils.js +8 -0
  21. package/koni/background/handlers/Extension.js +5 -3
  22. package/koni/background/handlers/Tabs.js +24 -23
  23. package/package.json +6 -6
  24. package/packageInfo.js +1 -1
  25. package/services/chain-service/constants.js +2 -2
  26. package/services/chain-service/index.js +5 -0
  27. package/services/chain-service/utils.d.ts +1 -0
  28. package/services/chain-service/utils.js +14 -10
  29. package/services/history-service/helpers/recoverHistoryStatus.d.ts +7 -1
  30. package/services/history-service/helpers/recoverHistoryStatus.js +151 -35
  31. package/services/history-service/index.d.ts +1 -1
  32. package/services/history-service/index.js +26 -19
  33. package/services/history-service/subsquid-multi-chain-history.js +2 -2
  34. package/services/notification-service/NotificationService.js +1 -1
  35. package/services/storage-service/DatabaseService.d.ts +1 -1
  36. package/services/storage-service/DatabaseService.js +1 -1
  37. package/services/transaction-service/index.js +41 -14
  38. package/services/transaction-service/types.d.ts +5 -3
  39. package/services/transaction-service/utils.js +3 -0
  40. package/utils/index.js +3 -0
@@ -419,6 +419,8 @@ export interface TransactionHistoryItem<ET extends ExtrinsicType = ExtrinsicType
419
419
  fee?: AmountData;
420
420
  explorerUrl?: string;
421
421
  additionalInfo?: TransactionAdditionalInfo<ET>;
422
+ startBlock?: number;
423
+ nonce?: number;
422
424
  }
423
425
  export interface SWError extends Error {
424
426
  code?: number;
@@ -1073,7 +1075,6 @@ export interface ChainStakingMetadata {
1073
1075
  minJoinNominationPool?: string;
1074
1076
  minStake: string;
1075
1077
  nominatorCount?: number;
1076
- minPoolBonding?: string;
1077
1078
  maxValidatorPerNominator: number;
1078
1079
  maxWithdrawalRequestPerValidator: number;
1079
1080
  allowCancelUnstaking: boolean;
@@ -34,7 +34,7 @@ function validateRelayUnbondingCondition(amount, chainStakingMetadata, nominator
34
34
  const errors = [];
35
35
  const bnActiveStake = new _util.BN(nominatorMetadata.activeStake);
36
36
  const bnRemainingStake = bnActiveStake.sub(new _util.BN(amount));
37
- const minStake = new _util.BN(chainStakingMetadata.minPoolBonding || '0');
37
+ const minStake = new _util.BN(chainStakingMetadata.minJoinNominationPool || '0');
38
38
  if (!(bnRemainingStake.isZero() || bnRemainingStake.gte(minStake))) {
39
39
  errors.push(new _TransactionError.TransactionError(_KoniTypes.StakingTxErrorType.INVALID_ACTIVE_STAKE));
40
40
  }
@@ -48,7 +48,7 @@ function validatePoolBondingCondition(chainInfo, amount, selectedPool, address,
48
48
  // amount >= min stake
49
49
  const errors = [];
50
50
  let bnTotalStake = new _util.BN(amount);
51
- const bnMinStake = new _util.BN(chainStakingMetadata.minPoolBonding || '0');
51
+ const bnMinStake = new _util.BN(chainStakingMetadata.minJoinNominationPool || '0');
52
52
  if (selectedPool.state !== 'Open') {
53
53
  errors.push(new _TransactionError.TransactionError(_KoniTypes.StakingTxErrorType.INACTIVE_NOMINATION_POOL));
54
54
  }
@@ -90,9 +90,6 @@ function validateRelayBondingCondition(chainInfo, amount, selectedValidators, ad
90
90
  async function getRelayChainStakingMetadata(chainInfo, substrateApi) {
91
91
  var _chainApi$api$query$a, _chainApi$api$query, _chainApi$api$query$s, _chainApi$api$query2, _chainApi$api$query2$, _chainApi$api$query3, _chainApi$api$query3$;
92
92
  const chain = chainInfo.slug;
93
- const {
94
- decimals
95
- } = (0, _utils2._getChainNativeTokenBasicInfo)(chainInfo);
96
93
  const chainApi = await substrateApi.isReady;
97
94
  const _era = await chainApi.api.query.staking.currentEra();
98
95
  const currentEra = _era.toString();
@@ -136,8 +133,6 @@ async function getRelayChainStakingMetadata(chainInfo, substrateApi) {
136
133
  // in %, annually
137
134
  inflation,
138
135
  minStake: minStake.toString(),
139
- minPoolBonding: (10 ** decimals).toString(),
140
- // default is 1
141
136
  maxValidatorPerNominator: parseInt(maxNominations),
142
137
  maxWithdrawalRequestPerValidator: parseInt(maxUnlockingChunks),
143
138
  allowCancelUnstaking: true,
@@ -17,6 +17,7 @@ exports.getParaCurrentInflation = getParaCurrentInflation;
17
17
  exports.getStakingAvailableActionsByChain = getStakingAvailableActionsByChain;
18
18
  exports.getStakingAvailableActionsByNominator = getStakingAvailableActionsByNominator;
19
19
  exports.getStakingStatusByNominations = getStakingStatusByNominations;
20
+ exports.getValidatorLabel = getValidatorLabel;
20
21
  exports.getWithdrawalInfo = getWithdrawalInfo;
21
22
  exports.isActionFromValidator = isActionFromValidator;
22
23
  exports.isShowNominationByValidator = isShowNominationByValidator;
@@ -268,4 +269,12 @@ function getStakingStatusByNominations(bnTotalActiveStake, nominationList) {
268
269
  }
269
270
  }
270
271
  return stakingStatus;
272
+ }
273
+ function getValidatorLabel(chain) {
274
+ if (_constants._STAKING_CHAIN_GROUP.astar.includes(chain)) {
275
+ return 'dApp';
276
+ } else if (_constants._STAKING_CHAIN_GROUP.relay.includes(chain)) {
277
+ return 'Validator';
278
+ }
279
+ return 'Collator';
271
280
  }
@@ -1299,7 +1299,7 @@ class KoniExtension {
1299
1299
  const isPasswordValidated = this.validatedAccountsPassword(file, password);
1300
1300
  if (isPasswordValidated) {
1301
1301
  try {
1302
- this._saveCurrentAccountAddress(addressList[0], () => {
1302
+ this._saveCurrentAccountAddress(_constants.ALL_ACCOUNT_KEY, () => {
1303
1303
  _uiKeyring.keyring.restoreAccounts(file, password);
1304
1304
  this._addAddressesToAuthList(addressList, isAllowed);
1305
1305
  });
@@ -1446,7 +1446,8 @@ class KoniExtension {
1446
1446
 
1447
1447
  // Get native token amount
1448
1448
  const freeBalance = await this.#koniState.balanceService.getTokenFreeBalance(from, networkKey, tokenSlug);
1449
- if ((0, _utilCrypto.isEthereumAddress)(from) && (0, _utilCrypto.isEthereumAddress)(to)) {
1449
+ if ((0, _utilCrypto.isEthereumAddress)(from) && (0, _utilCrypto.isEthereumAddress)(to) && (0, _utils._isTokenTransferredByEvm)(tokenInfo)) {
1450
+ // TODO: review this
1450
1451
  chainType = _KoniTypes.ChainType.EVM;
1451
1452
  const txVal = transferAll ? freeBalance.value : value || '0';
1452
1453
 
@@ -2917,6 +2918,7 @@ class KoniExtension {
2917
2918
  return Object.fromEntries(Object.entries(rs).map(_ref70 => {
2918
2919
  let [key, value] = _ref70;
2919
2920
  const {
2921
+ additionalValidator,
2920
2922
  transaction,
2921
2923
  ...transactionResult
2922
2924
  } = value;
@@ -445,35 +445,36 @@ class KoniTabs {
445
445
  const chainIdNum = parseInt(chainId, 16);
446
446
  const [existedNetworkSlug, existedChainInfo] = this.#koniState.findNetworkKeyByChainId(chainIdNum);
447
447
  if (existedNetworkSlug && existedChainInfo && existedChainInfo !== null && existedChainInfo !== void 0 && existedChainInfo.evmInfo) {
448
- const evmInfo = existedChainInfo.evmInfo;
449
- const substrateInfo = existedChainInfo.substrateInfo;
450
- const chainState = this.#koniState.getChainStateByKey(existedNetworkSlug);
451
- await this.#koniState.addNetworkConfirm(id, url, {
452
- mode: 'update',
453
- chainSpec: {
454
- evmChainId: evmInfo.evmChainId,
455
- decimals: evmInfo.decimals,
456
- existentialDeposit: evmInfo.existentialDeposit,
457
- genesisHash: (substrateInfo === null || substrateInfo === void 0 ? void 0 : substrateInfo.genesisHash) || '',
458
- paraId: (substrateInfo === null || substrateInfo === void 0 ? void 0 : substrateInfo.paraId) || null,
459
- addressPrefix: (substrateInfo === null || substrateInfo === void 0 ? void 0 : substrateInfo.addressPrefix) || 0
460
- },
461
- chainEditInfo: {
462
- blockExplorer: blockExplorerUrls === null || blockExplorerUrls === void 0 ? void 0 : blockExplorerUrls[0],
463
- slug: existedNetworkSlug,
464
- currentProvider: chainState.currentProvider,
465
- providers: existedChainInfo.providers,
466
- symbol: evmInfo.symbol,
467
- chainType: 'EVM',
468
- name: existedChainInfo.name
469
- }
470
- });
471
448
  return await this.switchEvmChain(id, url, {
472
449
  method: 'wallet_switchEthereumChain',
473
450
  params: [{
474
451
  chainId
475
452
  }]
476
453
  });
454
+ // const evmInfo = existedChainInfo.evmInfo;
455
+ // const substrateInfo = existedChainInfo.substrateInfo;
456
+ // const chainState = this.#koniState.getChainStateByKey(existedNetworkSlug);
457
+ //
458
+ // return await this.#koniState.addNetworkConfirm(id, url, {
459
+ // mode: 'update',
460
+ // chainSpec: {
461
+ // evmChainId: evmInfo.evmChainId,
462
+ // decimals: evmInfo.decimals,
463
+ // existentialDeposit: evmInfo.existentialDeposit,
464
+ // genesisHash: substrateInfo?.genesisHash || '',
465
+ // paraId: substrateInfo?.paraId || null,
466
+ // addressPrefix: substrateInfo?.addressPrefix || 0
467
+ // },
468
+ // chainEditInfo: {
469
+ // blockExplorer: blockExplorerUrls?.[0],
470
+ // slug: existedNetworkSlug,
471
+ // currentProvider: chainState.currentProvider,
472
+ // providers: existedChainInfo.providers,
473
+ // symbol: evmInfo.symbol,
474
+ // chainType: 'EVM',
475
+ // name: existedChainInfo.name
476
+ // }
477
+ // });
477
478
  } else if (rpcUrls && chainName) {
478
479
  const filteredUrls = rpcUrls.filter(targetString => {
479
480
  let url;
@@ -13,6 +13,6 @@ const packageInfo = {
13
13
  name: '@subwallet/extension-base',
14
14
  path: typeof __dirname === 'string' ? __dirname : 'auto',
15
15
  type: 'cjs',
16
- version: '1.0.5-1'
16
+ version: '1.0.5-3'
17
17
  };
18
18
  exports.packageInfo = packageInfo;
@@ -36,7 +36,7 @@ const _BALANCE_CHAIN_GROUP = {
36
36
  genshiro: ['genshiro_testnet', 'genshiro'],
37
37
  equilibrium_parachain: ['equilibrium_parachain'],
38
38
  bifrost: ['bifrost', 'acala', 'karura', 'acala_testnet', 'pioneer', 'bitcountry'],
39
- statemine: ['statemine', 'astar', 'shiden', 'statemint', 'moonbeam', 'moonbase', 'moonriver', 'crabParachain'],
39
+ statemine: ['statemine', 'astar', 'shiden', 'statemint', 'moonbeam', 'moonbase', 'moonriver', 'crabParachain', 'darwinia2'],
40
40
  kusama: ['kusama', 'kintsugi', 'kintsugi_test', 'interlay', 'acala', 'statemint', 'karura', 'bifrost'] // perhaps there are some runtime updates
41
41
  };
42
42
  exports._BALANCE_CHAIN_GROUP = _BALANCE_CHAIN_GROUP;
@@ -194,7 +194,7 @@ const _TRANSFER_CHAIN_GROUP = {
194
194
  genshiro: ['genshiro_testnet', 'genshiro', 'equilibrium_parachain'],
195
195
  crab: ['crab', 'pangolin'],
196
196
  bitcountry: ['pioneer', 'bitcountry'],
197
- statemine: ['statemint', 'statemine']
197
+ statemine: ['statemint', 'statemine', 'darwinia2']
198
198
  };
199
199
  exports._TRANSFER_CHAIN_GROUP = _TRANSFER_CHAIN_GROUP;
200
200
  const _BALANCE_PARSING_CHAIN_GROUP = {
@@ -720,6 +720,11 @@ class ChainService {
720
720
  targetChainInfo.substrateInfo.crowdloanUrl = params.chainEditInfo.crowdloanUrl;
721
721
  }
722
722
  }
723
+ if (targetChainInfo.evmInfo) {
724
+ if (params.chainEditInfo.blockExplorer !== undefined) {
725
+ targetChainInfo.evmInfo.blockExplorer = params.chainEditInfo.blockExplorer;
726
+ }
727
+ }
723
728
  this.updateChainInfoMapSubscription();
724
729
  this.dbService.updateChainStore({
725
730
  ...targetChainInfo,
@@ -61,6 +61,7 @@ exports._isSubstrateParaChain = _isSubstrateParaChain;
61
61
  exports._isSubstrateParachain = _isSubstrateParachain;
62
62
  exports._isSubstrateRelayChain = _isSubstrateRelayChain;
63
63
  exports._isTokenEvmSmartContract = _isTokenEvmSmartContract;
64
+ exports._isTokenTransferredByEvm = _isTokenTransferredByEvm;
64
65
  exports._isTokenWasmSmartContract = _isTokenWasmSmartContract;
65
66
  exports._isXcmPathSupported = _isXcmPathSupported;
66
67
  exports._parseAssetRefKey = _parseAssetRefKey;
@@ -128,6 +129,10 @@ function _getContractAddressOfToken(tokenInfo) {
128
129
  var _tokenInfo$metadata;
129
130
  return ((_tokenInfo$metadata = tokenInfo.metadata) === null || _tokenInfo$metadata === void 0 ? void 0 : _tokenInfo$metadata.contractAddress) || '';
130
131
  }
132
+ function _isTokenTransferredByEvm(tokenInfo) {
133
+ var _tokenInfo$metadata2;
134
+ return !!((_tokenInfo$metadata2 = tokenInfo.metadata) !== null && _tokenInfo$metadata2 !== void 0 && _tokenInfo$metadata2.contractAddress) || _isNativeToken(tokenInfo);
135
+ }
131
136
  function _checkSmartContractSupportByChain(chainInfo, contractType) {
132
137
  // EVM chains support smart contract by default so just checking Substrate chains
133
138
  if (chainInfo.substrateInfo === null || chainInfo.substrateInfo && chainInfo.substrateInfo.supportSmartContract === null) {
@@ -138,12 +143,12 @@ function _checkSmartContractSupportByChain(chainInfo, contractType) {
138
143
 
139
144
  // Utils for balance functions
140
145
  function _getTokenOnChainAssetId(tokenInfo) {
141
- var _tokenInfo$metadata2;
142
- return ((_tokenInfo$metadata2 = tokenInfo.metadata) === null || _tokenInfo$metadata2 === void 0 ? void 0 : _tokenInfo$metadata2.assetId) || '-1';
146
+ var _tokenInfo$metadata3;
147
+ return ((_tokenInfo$metadata3 = tokenInfo.metadata) === null || _tokenInfo$metadata3 === void 0 ? void 0 : _tokenInfo$metadata3.assetId) || '-1';
143
148
  }
144
149
  function _getTokenOnChainInfo(tokenInfo) {
145
- var _tokenInfo$metadata3;
146
- return (_tokenInfo$metadata3 = tokenInfo.metadata) === null || _tokenInfo$metadata3 === void 0 ? void 0 : _tokenInfo$metadata3.onChainInfo;
150
+ var _tokenInfo$metadata4;
151
+ return (_tokenInfo$metadata4 = tokenInfo.metadata) === null || _tokenInfo$metadata4 === void 0 ? void 0 : _tokenInfo$metadata4.onChainInfo;
147
152
  }
148
153
  function _getTokenMinAmount(tokenInfo) {
149
154
  return tokenInfo.minAmount || '0';
@@ -296,16 +301,16 @@ function _isXcmPathSupported(originTokenSlug, destinationTokenSlug, assetRefMap)
296
301
  return assetRef.path === _types._AssetRefPath.XCM;
297
302
  }
298
303
  function _getXcmAssetType(tokenInfo) {
299
- var _tokenInfo$metadata4;
300
- return ((_tokenInfo$metadata4 = tokenInfo.metadata) === null || _tokenInfo$metadata4 === void 0 ? void 0 : _tokenInfo$metadata4.assetType) || '';
304
+ var _tokenInfo$metadata5;
305
+ return ((_tokenInfo$metadata5 = tokenInfo.metadata) === null || _tokenInfo$metadata5 === void 0 ? void 0 : _tokenInfo$metadata5.assetType) || '';
301
306
  }
302
307
  function _getXcmAssetId(tokenInfo) {
303
- var _tokenInfo$metadata5;
304
- return ((_tokenInfo$metadata5 = tokenInfo.metadata) === null || _tokenInfo$metadata5 === void 0 ? void 0 : _tokenInfo$metadata5.assetId) || '-1';
308
+ var _tokenInfo$metadata6;
309
+ return ((_tokenInfo$metadata6 = tokenInfo.metadata) === null || _tokenInfo$metadata6 === void 0 ? void 0 : _tokenInfo$metadata6.assetId) || '-1';
305
310
  }
306
311
  function _getXcmAssetMultilocation(tokenInfo) {
307
- var _tokenInfo$metadata6;
308
- return (_tokenInfo$metadata6 = tokenInfo.metadata) === null || _tokenInfo$metadata6 === void 0 ? void 0 : _tokenInfo$metadata6.multilocation;
312
+ var _tokenInfo$metadata7;
313
+ return (_tokenInfo$metadata7 = tokenInfo.metadata) === null || _tokenInfo$metadata7 === void 0 ? void 0 : _tokenInfo$metadata7.multilocation;
309
314
  }
310
315
  function _getXcmTransferType(originChainInfo, destinationChainInfo) {
311
316
  var _originChainInfo$subs, _destinationChainInfo;
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.historyRecover = exports.HistoryRecoverStatus = void 0;
7
+ var _utils = require("@subwallet/extension-base/utils");
7
8
  // Copyright 2019-2022 @subwallet/extension-koni authors & contributors
8
9
  // SPDX-License-Identifier: Apache-2.0
9
10
  let HistoryRecoverStatus;
@@ -16,78 +17,194 @@ exports.HistoryRecoverStatus = HistoryRecoverStatus;
16
17
  HistoryRecoverStatus["FAIL_DETECT"] = "FAIL_DETECT";
17
18
  HistoryRecoverStatus["UNKNOWN"] = "UNKNOWN";
18
19
  })(HistoryRecoverStatus || (exports.HistoryRecoverStatus = HistoryRecoverStatus = {}));
20
+ const BLOCK_LIMIT = 6;
19
21
  const substrateRecover = async (history, chainService) => {
20
22
  const {
23
+ address,
21
24
  blockHash,
22
25
  chain,
23
- extrinsicHash
26
+ extrinsicHash,
27
+ from,
28
+ nonce,
29
+ startBlock
24
30
  } = history;
31
+ const result = {
32
+ status: HistoryRecoverStatus.UNKNOWN
33
+ };
25
34
  try {
26
35
  const substrateApi = chainService.getSubstrateApi(chain);
27
36
  if (substrateApi) {
28
37
  const _api = await substrateApi.isReady;
29
38
  const api = _api.api;
30
39
  if (!blockHash) {
31
- console.log(`Fail to find extrinsic ${extrinsicHash} on ${chain}: No block hash`);
32
- return HistoryRecoverStatus.LACK_INFO;
33
- }
34
- const block = await api.rpc.chain.getBlock(blockHash);
35
- const allEvents = await api.query.system.events.at(blockHash);
36
- const extrinsics = block.block.extrinsics;
37
- let index;
38
- extrinsics.forEach((extrinsic, _idx) => {
39
- if (extrinsicHash === extrinsic.hash.toHex()) {
40
- index = _idx;
40
+ if (!nonce || !startBlock) {
41
+ console.log(`Fail to find extrinsic for ${address} on ${chain}: With nonce ${nonce || 'undefined'} from block ${startBlock || 'undefined'}`);
42
+ return {
43
+ status: HistoryRecoverStatus.LACK_INFO
44
+ };
41
45
  }
42
- });
43
- if (index === undefined) {
44
- console.log(`Fail to find extrinsic ${extrinsicHash} on ${chain}`);
45
- return HistoryRecoverStatus.FAIL_DETECT;
46
- }
47
- const events = allEvents.filter(_ref => {
48
- let {
49
- phase
50
- } = _ref;
51
- return phase.isApplyExtrinsic && phase.asApplyExtrinsic.eq(index);
52
- });
53
- for (const {
54
- event
55
- } of events) {
56
- if (api.events.system.ExtrinsicSuccess.is(event)) {
57
- return HistoryRecoverStatus.SUCCESS;
58
- } else if (api.events.system.ExtrinsicFailed.is(event)) {
59
- return HistoryRecoverStatus.FAILED;
46
+ const currentBlock = (await api.query.system.number()).toPrimitive();
47
+ for (let i = 1, found = false; i < BLOCK_LIMIT && !found && startBlock + i <= currentBlock; i++) {
48
+ const blockHash = (await api.rpc.chain.getBlockHash(startBlock + i)).toHex();
49
+ const block = await api.rpc.chain.getBlock(blockHash);
50
+ const extrinsics = block.block.extrinsics;
51
+ let index;
52
+ for (const [idx, extrinsic] of Object.entries(extrinsics)) {
53
+ if (extrinsic.signer && (0, _utils.isSameAddress)(from, extrinsic.signer.toString()) && nonce === extrinsic.nonce.toNumber()) {
54
+ index = parseInt(idx);
55
+ found = true;
56
+ result.extrinsicHash = extrinsic.hash.toHex();
57
+ result.blockHash = block.block.hash.toHex();
58
+ result.blockNumber = block.block.header.number.toNumber();
59
+ break;
60
+ }
61
+ }
62
+ if (index !== undefined) {
63
+ const allEvents = await api.query.system.events.at(blockHash);
64
+ const events = allEvents.filter(_ref => {
65
+ let {
66
+ phase
67
+ } = _ref;
68
+ return phase.isApplyExtrinsic && phase.asApplyExtrinsic.eq(index);
69
+ });
70
+ for (const {
71
+ event
72
+ } of events) {
73
+ if (api.events.system.ExtrinsicSuccess.is(event)) {
74
+ return {
75
+ ...result,
76
+ status: HistoryRecoverStatus.SUCCESS
77
+ };
78
+ } else if (api.events.system.ExtrinsicFailed.is(event)) {
79
+ return {
80
+ ...result,
81
+ status: HistoryRecoverStatus.FAILED
82
+ };
83
+ }
84
+ }
85
+ }
86
+ }
87
+ } else {
88
+ const block = await api.rpc.chain.getBlock(blockHash);
89
+ const allEvents = await api.query.system.events.at(blockHash);
90
+ const extrinsics = block.block.extrinsics;
91
+ let index;
92
+ for (const [idx, extrinsic] of Object.entries(extrinsics)) {
93
+ if (extrinsicHash === extrinsic.hash.toHex()) {
94
+ index = parseInt(idx);
95
+ break;
96
+ }
97
+ }
98
+ if (index === undefined) {
99
+ console.log(`Fail to find extrinsic ${extrinsicHash} on ${chain}`);
100
+ return {
101
+ status: HistoryRecoverStatus.FAIL_DETECT
102
+ };
103
+ }
104
+ const events = allEvents.filter(_ref2 => {
105
+ let {
106
+ phase
107
+ } = _ref2;
108
+ return phase.isApplyExtrinsic && phase.asApplyExtrinsic.eq(index);
109
+ });
110
+ for (const {
111
+ event
112
+ } of events) {
113
+ if (api.events.system.ExtrinsicSuccess.is(event)) {
114
+ return {
115
+ ...result,
116
+ status: HistoryRecoverStatus.SUCCESS
117
+ };
118
+ } else if (api.events.system.ExtrinsicFailed.is(event)) {
119
+ return {
120
+ ...result,
121
+ status: HistoryRecoverStatus.FAILED
122
+ };
123
+ }
60
124
  }
61
125
  }
62
- return HistoryRecoverStatus.FAIL_DETECT;
126
+ return {
127
+ status: HistoryRecoverStatus.FAIL_DETECT
128
+ };
63
129
  } else {
64
130
  console.error(`Fail to update history ${chain}-${extrinsicHash}: Api not active`);
65
- return HistoryRecoverStatus.API_INACTIVE;
131
+ return {
132
+ status: HistoryRecoverStatus.API_INACTIVE
133
+ };
66
134
  }
67
135
  } catch (e) {
68
136
  console.error(`Fail to update history ${chain}-${extrinsicHash}:`, e.message);
69
- return HistoryRecoverStatus.UNKNOWN;
137
+ return {
138
+ status: HistoryRecoverStatus.UNKNOWN
139
+ };
70
140
  }
71
141
  };
72
142
  const evmRecover = async (history, chainService) => {
73
143
  const {
144
+ address,
74
145
  chain,
75
- extrinsicHash
146
+ extrinsicHash,
147
+ from,
148
+ nonce,
149
+ startBlock
76
150
  } = history;
151
+ const result = {
152
+ status: HistoryRecoverStatus.UNKNOWN
153
+ };
77
154
  try {
78
155
  const evmApi = chainService.getEvmApi(chain);
79
156
  if (evmApi) {
80
157
  const _api = await evmApi.isReady;
81
158
  const api = _api.api;
82
- const block = await api.eth.getTransactionReceipt(extrinsicHash);
83
- return block.status ? HistoryRecoverStatus.SUCCESS : HistoryRecoverStatus.FAILED;
159
+ if (extrinsicHash) {
160
+ const transactionReceipt = await api.eth.getTransactionReceipt(extrinsicHash);
161
+ return {
162
+ ...result,
163
+ status: transactionReceipt.status ? HistoryRecoverStatus.SUCCESS : HistoryRecoverStatus.FAILED
164
+ };
165
+ } else {
166
+ if (!nonce || !startBlock) {
167
+ console.log(`Fail to find extrinsic for ${address} on ${chain}: With nonce ${nonce || 'undefined'} from block ${startBlock || 'undefined'}`);
168
+ return {
169
+ ...result,
170
+ status: HistoryRecoverStatus.LACK_INFO
171
+ };
172
+ }
173
+ const currentBlock = await api.eth.getBlockNumber();
174
+ for (let i = 1, found = false; i < BLOCK_LIMIT && !found && startBlock + i <= currentBlock; i++) {
175
+ const block = await api.eth.getBlock(startBlock + i, true);
176
+ for (const transaction of block.transactions) {
177
+ if ((0, _utils.isSameAddress)(transaction.from, from) && nonce === transaction.nonce) {
178
+ result.extrinsicHash = transaction.hash;
179
+ result.blockHash = block.hash;
180
+ result.blockNumber = block.number;
181
+ found = true;
182
+ break;
183
+ }
184
+ }
185
+ if (result.extrinsicHash) {
186
+ const transactionReceipt = await api.eth.getTransactionReceipt(result.extrinsicHash);
187
+ return {
188
+ ...result,
189
+ status: transactionReceipt.status ? HistoryRecoverStatus.SUCCESS : HistoryRecoverStatus.FAILED
190
+ };
191
+ }
192
+ }
193
+ }
194
+ return {
195
+ status: HistoryRecoverStatus.FAIL_DETECT
196
+ };
84
197
  } else {
85
198
  console.error(`Fail to update history ${chain}-${extrinsicHash}: Api not active`);
86
- return HistoryRecoverStatus.API_INACTIVE;
199
+ return {
200
+ status: HistoryRecoverStatus.API_INACTIVE
201
+ };
87
202
  }
88
203
  } catch (e) {
89
204
  console.error(`Fail to update history ${chain}-${extrinsicHash}:`, e.message);
90
- return HistoryRecoverStatus.UNKNOWN;
205
+ return {
206
+ status: HistoryRecoverStatus.UNKNOWN
207
+ };
91
208
  }
92
209
  };
93
210
 
@@ -102,7 +219,9 @@ const historyRecover = async (history, chainService) => {
102
219
  const checkFunction = chainType === 'substrate' ? substrateRecover : evmRecover;
103
220
  return await checkFunction(history, chainService);
104
221
  } else {
105
- return HistoryRecoverStatus.LACK_INFO;
222
+ return {
223
+ status: HistoryRecoverStatus.LACK_INFO
224
+ };
106
225
  }
107
226
  };
108
227
  exports.historyRecover = historyRecover;
@@ -17,7 +17,7 @@ var _subsquidMultiChainHistory = require("./subsquid-multi-chain-history");
17
17
 
18
18
  class HistoryService {
19
19
  historySubject = new _rxjs.BehaviorSubject([]);
20
- #processingHistories = {};
20
+ #needRecoveryHistories = {};
21
21
  constructor(dbService, chainService, eventService, keyringService) {
22
22
  this.dbService = dbService;
23
23
  this.chainService = chainService;
@@ -80,7 +80,7 @@ class HistoryService {
80
80
  await this.addHistoryItems(updatedRecords);
81
81
  }
82
82
  async updateHistoryByExtrinsicHash(extrinsicHash, updateData) {
83
- await this.dbService.updateHistoryByNewExtrinsicHash(extrinsicHash, updateData);
83
+ await this.dbService.updateHistoryByExtrinsicHash(extrinsicHash, updateData);
84
84
  this.historySubject.next(await this.dbService.getHistories());
85
85
  }
86
86
 
@@ -139,7 +139,7 @@ class HistoryService {
139
139
  }
140
140
  async recoverHistories() {
141
141
  const list = [];
142
- for (const processingHistory of Object.values(this.#processingHistories)) {
142
+ for (const processingHistory of Object.values(this.#needRecoveryHistories)) {
143
143
  const chainState = this.chainService.getChainStateByKey(processingHistory.chain);
144
144
  if (chainState.active) {
145
145
  list.push(processingHistory);
@@ -149,24 +149,28 @@ class HistoryService {
149
149
  }
150
150
  }
151
151
  const promises = list.map(history => (0, _recoverHistoryStatus.historyRecover)(history, this.chainService));
152
- const result = await Promise.all(promises);
153
- result.forEach((status, index) => {
154
- const extrinsicHash = list[index].extrinsicHash;
155
- switch (status) {
152
+ const results = await Promise.all(promises);
153
+ results.forEach((recoverResult, index) => {
154
+ const currentExtrinsicHash = list[index].extrinsicHash;
155
+ const updateData = {
156
+ ...recoverResult,
157
+ status: _KoniTypes.ExtrinsicStatus.UNKNOWN
158
+ };
159
+ switch (recoverResult.status) {
156
160
  case _recoverHistoryStatus.HistoryRecoverStatus.API_INACTIVE:
157
161
  break;
158
162
  case _recoverHistoryStatus.HistoryRecoverStatus.FAILED:
159
163
  case _recoverHistoryStatus.HistoryRecoverStatus.SUCCESS:
160
- this.updateHistoryByExtrinsicHash(extrinsicHash, {
161
- status: status === _recoverHistoryStatus.HistoryRecoverStatus.SUCCESS ? _KoniTypes.ExtrinsicStatus.SUCCESS : _KoniTypes.ExtrinsicStatus.FAIL
162
- }).catch(console.error);
163
- delete this.#processingHistories[extrinsicHash];
164
+ updateData.status = recoverResult.status === _recoverHistoryStatus.HistoryRecoverStatus.SUCCESS ? _KoniTypes.ExtrinsicStatus.SUCCESS : _KoniTypes.ExtrinsicStatus.FAIL;
165
+ this.updateHistoryByExtrinsicHash(currentExtrinsicHash, updateData).catch(console.error);
166
+ delete this.#needRecoveryHistories[currentExtrinsicHash];
164
167
  break;
165
168
  default:
166
- delete this.#processingHistories[extrinsicHash];
169
+ this.updateHistoryByExtrinsicHash(currentExtrinsicHash, updateData).catch(console.error);
170
+ delete this.#needRecoveryHistories[currentExtrinsicHash];
167
171
  }
168
172
  });
169
- if (!Object.keys(this.#processingHistories).length) {
173
+ if (!Object.keys(this.#needRecoveryHistories).length) {
170
174
  await this.stopRecoverHistories();
171
175
  }
172
176
  }
@@ -176,7 +180,7 @@ class HistoryService {
176
180
  await this.loadData();
177
181
  Promise.all([this.eventService.waitKeyringReady, this.eventService.waitChainReady]).then(() => {
178
182
  this.getHistories().catch(console.log);
179
- this.getProcessingHistory().catch(console.log);
183
+ this.recoverProcessingHistory().catch(console.error);
180
184
  this.eventService.on('account.add', () => {
181
185
  (async () => {
182
186
  await this.stopCron();
@@ -189,14 +193,18 @@ class HistoryService {
189
193
  }).catch(console.error);
190
194
  this.status = _types.ServiceStatus.INITIALIZED;
191
195
  }
192
- async getProcessingHistory() {
196
+ async recoverProcessingHistory() {
193
197
  const histories = await this.dbService.getHistories();
194
- this.#processingHistories = {};
198
+ this.#needRecoveryHistories = {};
195
199
  histories.filter(history => {
196
- return history.status === 'processing';
200
+ return [_KoniTypes.ExtrinsicStatus.PROCESSING, _KoniTypes.ExtrinsicStatus.SUBMITTING].includes(history.status);
197
201
  }).forEach(history => {
198
- this.#processingHistories[history.extrinsicHash] = history;
202
+ this.#needRecoveryHistories[history.extrinsicHash] = history;
199
203
  });
204
+ const recoverNumber = Object.keys(this.#needRecoveryHistories).length;
205
+ if (recoverNumber > 0) {
206
+ console.log(`Recover ${recoverNumber} processing history`);
207
+ }
200
208
  this.startRecoverHistories().catch(console.error);
201
209
  }
202
210
  async start() {
@@ -204,7 +212,6 @@ class HistoryService {
204
212
  this.startPromiseHandler = (0, _promise.createPromiseHandler)();
205
213
  this.status = _types.ServiceStatus.STARTING;
206
214
  await this.startCron();
207
- await this.startRecoverHistories();
208
215
  this.status = _types.ServiceStatus.STARTED;
209
216
  this.startPromiseHandler.resolve();
210
217
  } catch (e) {
@@ -255,7 +255,7 @@ async function fetchMultiChainHistories(addresses, chainMap) {
255
255
  const usedAddresses = relatedAddresses.filter(a => lowerAddresses.includes(a.toLowerCase()));
256
256
  const chainInfo = chainMap[chainId];
257
257
  if (chainInfo === undefined) {
258
- console.warn(`Not found chain info for chain id: ${chainId}`); // TODO: resolve conflicting chainId
258
+ console.debug(`Not found chain info for chain id: ${chainId}`); // TODO: resolve conflicting chainId
259
259
 
260
260
  return;
261
261
  }
@@ -264,7 +264,7 @@ async function fetchMultiChainHistories(addresses, chainMap) {
264
264
  const transactionData = parseSubsquidTransactionData(address, name, historyItem, chainInfo, parseData(args), parseData(_data));
265
265
  histories.push(transactionData);
266
266
  } catch (e) {
267
- console.warn('Parse transaction data failed', address, e);
267
+ console.debug('Parse transaction data failed', address, e);
268
268
  }
269
269
  });
270
270
  });
@@ -36,7 +36,7 @@ class NotificationService {
36
36
  type: 'basic',
37
37
  title,
38
38
  message,
39
- iconUrl: 'https://subwallet.app/assets/images/favicon/favicon-192x192.png',
39
+ iconUrl: './images/icon-128.png',
40
40
  priority: 2,
41
41
  isClickable: !!link
42
42
  }, notificationId => {
@@ -120,7 +120,7 @@ class DatabaseService {
120
120
  const cleanedHistory = histories.filter(x => x && x.address && x.chain && x.extrinsicHash);
121
121
  return this.stores.transaction.bulkUpsert(cleanedHistory);
122
122
  }
123
- async updateHistoryByNewExtrinsicHash(extrinsicHash, updateData) {
123
+ async updateHistoryByExtrinsicHash(extrinsicHash, updateData) {
124
124
  const canUpdate = updateData && extrinsicHash;
125
125
  if (!canUpdate) {
126
126
  return;