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

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.
@@ -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;
@@ -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
  });
@@ -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-2'
17
17
  };
18
18
  exports.packageInfo = packageInfo;
@@ -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) {
@@ -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;
@@ -279,7 +279,7 @@ class TransactionService {
279
279
  const chainInfo = this.chainService.getChainInfoByKey(transaction.chain);
280
280
  return (0, _utils2.getExplorerLink)(chainInfo, transaction.extrinsicHash, 'tx');
281
281
  }
282
- transactionToHistories(id, eventLogs) {
282
+ transactionToHistories(id, startBlock, nonce, eventLogs) {
283
283
  const transaction = this.getTransaction(id);
284
284
  const historyItem = {
285
285
  origin: 'app',
@@ -297,9 +297,12 @@ class TransactionService {
297
297
  fee: transaction.estimateFee,
298
298
  blockNumber: 0,
299
299
  // Will be added in next step
300
- blockHash: '' // Will be added in next step
300
+ blockHash: '',
301
+ // Will be added in next step
302
+ nonce: nonce || 0,
303
+ startBlock: startBlock || 0
301
304
  };
302
-
305
+ console.log('historyItem', historyItem);
303
306
  const chainInfo = this.chainService.getChainInfoByKey(transaction.chain);
304
307
  const nativeAsset = (0, _utils._getChainNativeTokenBasicInfo)(chainInfo);
305
308
  const baseNativeAmount = {
@@ -468,7 +471,9 @@ class TransactionService {
468
471
  }
469
472
  onSend(_ref2) {
470
473
  let {
471
- id
474
+ id,
475
+ nonce,
476
+ startBlock
472
477
  } = _ref2;
473
478
  // Update transaction status
474
479
  this.updateTransaction(id, {
@@ -476,7 +481,7 @@ class TransactionService {
476
481
  });
477
482
 
478
483
  // Create Input History Transaction History
479
- this.historyService.insertHistories(this.transactionToHistories(id)).catch(console.error);
484
+ this.historyService.insertHistories(this.transactionToHistories(id, startBlock, nonce)).catch(console.error);
480
485
  console.debug(`Transaction "${id}" is sent`);
481
486
  }
482
487
  onHasTransactionHash(_ref3) {
@@ -527,15 +532,18 @@ class TransactionService {
527
532
  let {
528
533
  blockHash,
529
534
  blockNumber,
535
+ extrinsicHash,
530
536
  id
531
537
  } = _ref4;
532
538
  const transaction = this.getTransaction(id);
533
539
  this.updateTransaction(id, {
534
- status: _KoniTypes.ExtrinsicStatus.SUCCESS
540
+ status: _KoniTypes.ExtrinsicStatus.SUCCESS,
541
+ extrinsicHash
535
542
  });
536
543
 
537
544
  // Write success transaction history
538
- this.historyService.updateHistories(transaction.chain, transaction.extrinsicHash, {
545
+ this.historyService.updateHistoryByExtrinsicHash(transaction.extrinsicHash, {
546
+ extrinsicHash,
539
547
  status: _KoniTypes.ExtrinsicStatus.SUCCESS,
540
548
  blockNumber: blockNumber || 0,
541
549
  blockHash: blockHash || ''
@@ -556,6 +564,7 @@ class TransactionService {
556
564
  blockHash,
557
565
  blockNumber,
558
566
  errors,
567
+ extrinsicHash,
559
568
  id
560
569
  } = _ref5;
561
570
  const transaction = this.getTransaction(id);
@@ -563,11 +572,13 @@ class TransactionService {
563
572
  if (transaction) {
564
573
  this.updateTransaction(id, {
565
574
  status: nextStatus,
566
- errors
575
+ errors,
576
+ extrinsicHash
567
577
  });
568
578
 
569
579
  // Write failed transaction history
570
- this.historyService.updateHistories(transaction.chain, transaction.extrinsicHash, {
580
+ this.historyService.updateHistoryByExtrinsicHash(transaction.extrinsicHash, {
581
+ extrinsicHash: extrinsicHash || transaction.extrinsicHash,
571
582
  status: nextStatus,
572
583
  blockNumber: blockNumber || 0,
573
584
  blockHash: blockHash || ''
@@ -667,7 +678,7 @@ class TransactionService {
667
678
  errors: [],
668
679
  warnings: []
669
680
  };
670
- this.requestService.addConfirmation(id, url || _constants2.EXTENSION_REQUEST_URL, 'evmSendTransactionRequest', payload, {}).then(_ref7 => {
681
+ this.requestService.addConfirmation(id, url || _constants2.EXTENSION_REQUEST_URL, 'evmSendTransactionRequest', payload, {}).then(async _ref7 => {
671
682
  let {
672
683
  isApproved,
673
684
  payload
@@ -694,11 +705,16 @@ class TransactionService {
694
705
 
695
706
  // Send transaction
696
707
  this.handleTransactionTimeout(emitter, eventData);
708
+
709
+ // Add start info
710
+ eventData.nonce = txObject.nonce;
711
+ eventData.startBlock = await web3Api.eth.getBlockNumber();
697
712
  emitter.emit('send', eventData); // This event is needed after sending transaction with queue
698
713
  signedTransaction && web3Api.eth.sendSignedTransaction(signedTransaction).once('transactionHash', hash => {
699
714
  eventData.extrinsicHash = hash;
700
715
  emitter.emit('extrinsicHash', eventData);
701
716
  }).once('receipt', rs => {
717
+ eventData.extrinsicHash = rs.transactionHash;
702
718
  eventData.blockHash = rs.blockHash;
703
719
  eventData.blockNumber = rs.blockNumber;
704
720
  emitter.emit('success', eventData);
@@ -724,6 +740,7 @@ class TransactionService {
724
740
  signAndSendSubstrateTransaction(_ref8) {
725
741
  let {
726
742
  address,
743
+ chain,
727
744
  id,
728
745
  transaction,
729
746
  url
@@ -744,11 +761,14 @@ class TransactionService {
744
761
  };
745
762
  }
746
763
  }
747
- }).then(rs => {
764
+ }).then(async rs => {
748
765
  // Emit signed event
749
766
  emitter.emit('signed', eventData);
750
767
 
751
768
  // Send transaction
769
+ const api = this.chainService.getSubstrateApi(chain);
770
+ eventData.nonce = rs.nonce.toNumber();
771
+ eventData.startBlock = (await api.api.query.system.number()).toPrimitive();
752
772
  this.handleTransactionTimeout(emitter, eventData);
753
773
  emitter.emit('send', eventData); // This event is needed after sending transaction with queue
754
774
 
@@ -766,6 +786,7 @@ class TransactionService {
766
786
  }
767
787
  }
768
788
  if (txState.status.isFinalized) {
789
+ eventData.extrinsicHash = txState.txHash.toHex();
769
790
  eventData.eventLogs = txState.events;
770
791
  // TODO: push block hash and block number into eventData
771
792
  txState.events.filter(_ref9 => {
@@ -56,6 +56,9 @@ function reformatAddress(address) {
56
56
  let networkPrefix = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 42;
57
57
  let isEthereum = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
58
58
  try {
59
+ if (!address || address === '') {
60
+ return '';
61
+ }
59
62
  if ((0, _utilCrypto.isEthereumAddress)(address)) {
60
63
  return address;
61
64
  }
@@ -1251,7 +1251,7 @@ export default class KoniExtension {
1251
1251
  const isPasswordValidated = this.validatedAccountsPassword(file, password);
1252
1252
  if (isPasswordValidated) {
1253
1253
  try {
1254
- this._saveCurrentAccountAddress(addressList[0], () => {
1254
+ this._saveCurrentAccountAddress(ALL_ACCOUNT_KEY, () => {
1255
1255
  keyring.restoreAccounts(file, password);
1256
1256
  this._addAddressesToAuthList(addressList, isAllowed);
1257
1257
  });
package/package.json CHANGED
@@ -17,7 +17,7 @@
17
17
  "./cjs/detectPackage.js"
18
18
  ],
19
19
  "type": "module",
20
- "version": "1.0.5-1",
20
+ "version": "1.0.5-2",
21
21
  "main": "./cjs/index.js",
22
22
  "module": "./index.js",
23
23
  "types": "./index.d.ts",
@@ -1693,10 +1693,10 @@
1693
1693
  "@subsocial/types": "^0.6.8",
1694
1694
  "@substrate/connect": "^0.7.26",
1695
1695
  "@subwallet/chain-list": "^0.1.2",
1696
- "@subwallet/extension-base": "^1.0.5-1",
1697
- "@subwallet/extension-chains": "^1.0.5-1",
1698
- "@subwallet/extension-dapp": "^1.0.5-1",
1699
- "@subwallet/extension-inject": "^1.0.5-1",
1696
+ "@subwallet/extension-base": "^1.0.5-2",
1697
+ "@subwallet/extension-chains": "^1.0.5-2",
1698
+ "@subwallet/extension-dapp": "^1.0.5-2",
1699
+ "@subwallet/extension-inject": "^1.0.5-2",
1700
1700
  "@subwallet/keyring": "^0.0.9",
1701
1701
  "@subwallet/ui-keyring": "^0.0.9",
1702
1702
  "@unique-nft/types": "^0.6.0-4",
package/packageInfo.js CHANGED
@@ -7,5 +7,5 @@ export const packageInfo = {
7
7
  name: '@subwallet/extension-base',
8
8
  path: (import.meta && import.meta.url) ? new URL(import.meta.url).pathname.substring(0, new URL(import.meta.url).pathname.lastIndexOf('/') + 1) : 'auto',
9
9
  type: 'esm',
10
- version: '1.0.5-1'
10
+ version: '1.0.5-2'
11
11
  };
@@ -8,4 +8,10 @@ export declare enum HistoryRecoverStatus {
8
8
  FAIL_DETECT = "FAIL_DETECT",
9
9
  UNKNOWN = "UNKNOWN"
10
10
  }
11
- export declare const historyRecover: (history: TransactionHistoryItem, chainService: ChainService) => Promise<HistoryRecoverStatus>;
11
+ export interface TransactionRecoverResult {
12
+ status: HistoryRecoverStatus;
13
+ extrinsicHash?: string;
14
+ blockHash?: string;
15
+ blockNumber?: number;
16
+ }
17
+ export declare const historyRecover: (history: TransactionHistoryItem, chainService: ChainService) => Promise<TransactionRecoverResult>;
@@ -1,6 +1,7 @@
1
1
  // Copyright 2019-2022 @subwallet/extension-koni authors & contributors
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
+ import { isSameAddress } from '@subwallet/extension-base/utils';
4
5
  export let HistoryRecoverStatus;
5
6
  (function (HistoryRecoverStatus) {
6
7
  HistoryRecoverStatus["SUCCESS"] = "SUCCESS";
@@ -10,75 +11,188 @@ export let HistoryRecoverStatus;
10
11
  HistoryRecoverStatus["FAIL_DETECT"] = "FAIL_DETECT";
11
12
  HistoryRecoverStatus["UNKNOWN"] = "UNKNOWN";
12
13
  })(HistoryRecoverStatus || (HistoryRecoverStatus = {}));
14
+ const BLOCK_LIMIT = 6;
13
15
  const substrateRecover = async (history, chainService) => {
14
16
  const {
17
+ address,
15
18
  blockHash,
16
19
  chain,
17
- extrinsicHash
20
+ extrinsicHash,
21
+ from,
22
+ nonce,
23
+ startBlock
18
24
  } = history;
25
+ const result = {
26
+ status: HistoryRecoverStatus.UNKNOWN
27
+ };
19
28
  try {
20
29
  const substrateApi = chainService.getSubstrateApi(chain);
21
30
  if (substrateApi) {
22
31
  const _api = await substrateApi.isReady;
23
32
  const api = _api.api;
24
33
  if (!blockHash) {
25
- console.log(`Fail to find extrinsic ${extrinsicHash} on ${chain}: No block hash`);
26
- return HistoryRecoverStatus.LACK_INFO;
27
- }
28
- const block = await api.rpc.chain.getBlock(blockHash);
29
- const allEvents = await api.query.system.events.at(blockHash);
30
- const extrinsics = block.block.extrinsics;
31
- let index;
32
- extrinsics.forEach((extrinsic, _idx) => {
33
- if (extrinsicHash === extrinsic.hash.toHex()) {
34
- index = _idx;
34
+ if (!nonce || !startBlock) {
35
+ console.log(`Fail to find extrinsic for ${address} on ${chain}: With nonce ${nonce || 'undefined'} from block ${startBlock || 'undefined'}`);
36
+ return {
37
+ status: HistoryRecoverStatus.LACK_INFO
38
+ };
35
39
  }
36
- });
37
- if (index === undefined) {
38
- console.log(`Fail to find extrinsic ${extrinsicHash} on ${chain}`);
39
- return HistoryRecoverStatus.FAIL_DETECT;
40
- }
41
- const events = allEvents.filter(({
42
- phase
43
- }) => phase.isApplyExtrinsic && phase.asApplyExtrinsic.eq(index));
44
- for (const {
45
- event
46
- } of events) {
47
- if (api.events.system.ExtrinsicSuccess.is(event)) {
48
- return HistoryRecoverStatus.SUCCESS;
49
- } else if (api.events.system.ExtrinsicFailed.is(event)) {
50
- return HistoryRecoverStatus.FAILED;
40
+ const currentBlock = (await api.query.system.number()).toPrimitive();
41
+ for (let i = 1, found = false; i < BLOCK_LIMIT && !found && startBlock + i <= currentBlock; i++) {
42
+ const blockHash = (await api.rpc.chain.getBlockHash(startBlock + i)).toHex();
43
+ const block = await api.rpc.chain.getBlock(blockHash);
44
+ const extrinsics = block.block.extrinsics;
45
+ let index;
46
+ for (const [idx, extrinsic] of Object.entries(extrinsics)) {
47
+ if (extrinsic.signer && isSameAddress(from, extrinsic.signer.toString()) && nonce === extrinsic.nonce.toNumber()) {
48
+ index = parseInt(idx);
49
+ found = true;
50
+ result.extrinsicHash = extrinsic.hash.toHex();
51
+ result.blockHash = block.block.hash.toHex();
52
+ result.blockNumber = block.block.header.number.toNumber();
53
+ break;
54
+ }
55
+ }
56
+ if (index !== undefined) {
57
+ const allEvents = await api.query.system.events.at(blockHash);
58
+ const events = allEvents.filter(({
59
+ phase
60
+ }) => phase.isApplyExtrinsic && phase.asApplyExtrinsic.eq(index));
61
+ for (const {
62
+ event
63
+ } of events) {
64
+ if (api.events.system.ExtrinsicSuccess.is(event)) {
65
+ return {
66
+ ...result,
67
+ status: HistoryRecoverStatus.SUCCESS
68
+ };
69
+ } else if (api.events.system.ExtrinsicFailed.is(event)) {
70
+ return {
71
+ ...result,
72
+ status: HistoryRecoverStatus.FAILED
73
+ };
74
+ }
75
+ }
76
+ }
77
+ }
78
+ } else {
79
+ const block = await api.rpc.chain.getBlock(blockHash);
80
+ const allEvents = await api.query.system.events.at(blockHash);
81
+ const extrinsics = block.block.extrinsics;
82
+ let index;
83
+ for (const [idx, extrinsic] of Object.entries(extrinsics)) {
84
+ if (extrinsicHash === extrinsic.hash.toHex()) {
85
+ index = parseInt(idx);
86
+ break;
87
+ }
88
+ }
89
+ if (index === undefined) {
90
+ console.log(`Fail to find extrinsic ${extrinsicHash} on ${chain}`);
91
+ return {
92
+ status: HistoryRecoverStatus.FAIL_DETECT
93
+ };
94
+ }
95
+ const events = allEvents.filter(({
96
+ phase
97
+ }) => phase.isApplyExtrinsic && phase.asApplyExtrinsic.eq(index));
98
+ for (const {
99
+ event
100
+ } of events) {
101
+ if (api.events.system.ExtrinsicSuccess.is(event)) {
102
+ return {
103
+ ...result,
104
+ status: HistoryRecoverStatus.SUCCESS
105
+ };
106
+ } else if (api.events.system.ExtrinsicFailed.is(event)) {
107
+ return {
108
+ ...result,
109
+ status: HistoryRecoverStatus.FAILED
110
+ };
111
+ }
51
112
  }
52
113
  }
53
- return HistoryRecoverStatus.FAIL_DETECT;
114
+ return {
115
+ status: HistoryRecoverStatus.FAIL_DETECT
116
+ };
54
117
  } else {
55
118
  console.error(`Fail to update history ${chain}-${extrinsicHash}: Api not active`);
56
- return HistoryRecoverStatus.API_INACTIVE;
119
+ return {
120
+ status: HistoryRecoverStatus.API_INACTIVE
121
+ };
57
122
  }
58
123
  } catch (e) {
59
124
  console.error(`Fail to update history ${chain}-${extrinsicHash}:`, e.message);
60
- return HistoryRecoverStatus.UNKNOWN;
125
+ return {
126
+ status: HistoryRecoverStatus.UNKNOWN
127
+ };
61
128
  }
62
129
  };
63
130
  const evmRecover = async (history, chainService) => {
64
131
  const {
132
+ address,
65
133
  chain,
66
- extrinsicHash
134
+ extrinsicHash,
135
+ from,
136
+ nonce,
137
+ startBlock
67
138
  } = history;
139
+ const result = {
140
+ status: HistoryRecoverStatus.UNKNOWN
141
+ };
68
142
  try {
69
143
  const evmApi = chainService.getEvmApi(chain);
70
144
  if (evmApi) {
71
145
  const _api = await evmApi.isReady;
72
146
  const api = _api.api;
73
- const block = await api.eth.getTransactionReceipt(extrinsicHash);
74
- return block.status ? HistoryRecoverStatus.SUCCESS : HistoryRecoverStatus.FAILED;
147
+ if (extrinsicHash) {
148
+ const transactionReceipt = await api.eth.getTransactionReceipt(extrinsicHash);
149
+ return {
150
+ ...result,
151
+ status: transactionReceipt.status ? HistoryRecoverStatus.SUCCESS : HistoryRecoverStatus.FAILED
152
+ };
153
+ } else {
154
+ if (!nonce || !startBlock) {
155
+ console.log(`Fail to find extrinsic for ${address} on ${chain}: With nonce ${nonce || 'undefined'} from block ${startBlock || 'undefined'}`);
156
+ return {
157
+ ...result,
158
+ status: HistoryRecoverStatus.LACK_INFO
159
+ };
160
+ }
161
+ const currentBlock = await api.eth.getBlockNumber();
162
+ for (let i = 1, found = false; i < BLOCK_LIMIT && !found && startBlock + i <= currentBlock; i++) {
163
+ const block = await api.eth.getBlock(startBlock + i, true);
164
+ for (const transaction of block.transactions) {
165
+ if (isSameAddress(transaction.from, from) && nonce === transaction.nonce) {
166
+ result.extrinsicHash = transaction.hash;
167
+ result.blockHash = block.hash;
168
+ result.blockNumber = block.number;
169
+ found = true;
170
+ break;
171
+ }
172
+ }
173
+ if (result.extrinsicHash) {
174
+ const transactionReceipt = await api.eth.getTransactionReceipt(result.extrinsicHash);
175
+ return {
176
+ ...result,
177
+ status: transactionReceipt.status ? HistoryRecoverStatus.SUCCESS : HistoryRecoverStatus.FAILED
178
+ };
179
+ }
180
+ }
181
+ }
182
+ return {
183
+ status: HistoryRecoverStatus.FAIL_DETECT
184
+ };
75
185
  } else {
76
186
  console.error(`Fail to update history ${chain}-${extrinsicHash}: Api not active`);
77
- return HistoryRecoverStatus.API_INACTIVE;
187
+ return {
188
+ status: HistoryRecoverStatus.API_INACTIVE
189
+ };
78
190
  }
79
191
  } catch (e) {
80
192
  console.error(`Fail to update history ${chain}-${extrinsicHash}:`, e.message);
81
- return HistoryRecoverStatus.UNKNOWN;
193
+ return {
194
+ status: HistoryRecoverStatus.UNKNOWN
195
+ };
82
196
  }
83
197
  };
84
198
 
@@ -93,6 +207,8 @@ export const historyRecover = async (history, chainService) => {
93
207
  const checkFunction = chainType === 'substrate' ? substrateRecover : evmRecover;
94
208
  return await checkFunction(history, chainService);
95
209
  } else {
96
- return HistoryRecoverStatus.LACK_INFO;
210
+ return {
211
+ status: HistoryRecoverStatus.LACK_INFO
212
+ };
97
213
  }
98
214
  };
@@ -38,7 +38,7 @@ export declare class HistoryService implements StoppableServiceInterface, Persis
38
38
  promise: Promise<void>;
39
39
  };
40
40
  init(): Promise<void>;
41
- getProcessingHistory(): Promise<void>;
41
+ recoverProcessingHistory(): Promise<void>;
42
42
  start(): Promise<void>;
43
43
  waitForStarted(): Promise<void>;
44
44
  stopPromiseHandler: {
@@ -11,7 +11,7 @@ import { BehaviorSubject } from 'rxjs';
11
11
  import { fetchMultiChainHistories } from "./subsquid-multi-chain-history.js";
12
12
  export class HistoryService {
13
13
  historySubject = new BehaviorSubject([]);
14
- #processingHistories = {};
14
+ #needRecoveryHistories = {};
15
15
  constructor(dbService, chainService, eventService, keyringService) {
16
16
  this.dbService = dbService;
17
17
  this.chainService = chainService;
@@ -73,7 +73,7 @@ export class HistoryService {
73
73
  await this.addHistoryItems(updatedRecords);
74
74
  }
75
75
  async updateHistoryByExtrinsicHash(extrinsicHash, updateData) {
76
- await this.dbService.updateHistoryByNewExtrinsicHash(extrinsicHash, updateData);
76
+ await this.dbService.updateHistoryByExtrinsicHash(extrinsicHash, updateData);
77
77
  this.historySubject.next(await this.dbService.getHistories());
78
78
  }
79
79
 
@@ -132,7 +132,7 @@ export class HistoryService {
132
132
  }
133
133
  async recoverHistories() {
134
134
  const list = [];
135
- for (const processingHistory of Object.values(this.#processingHistories)) {
135
+ for (const processingHistory of Object.values(this.#needRecoveryHistories)) {
136
136
  const chainState = this.chainService.getChainStateByKey(processingHistory.chain);
137
137
  if (chainState.active) {
138
138
  list.push(processingHistory);
@@ -142,24 +142,28 @@ export class HistoryService {
142
142
  }
143
143
  }
144
144
  const promises = list.map(history => historyRecover(history, this.chainService));
145
- const result = await Promise.all(promises);
146
- result.forEach((status, index) => {
147
- const extrinsicHash = list[index].extrinsicHash;
148
- switch (status) {
145
+ const results = await Promise.all(promises);
146
+ results.forEach((recoverResult, index) => {
147
+ const currentExtrinsicHash = list[index].extrinsicHash;
148
+ const updateData = {
149
+ ...recoverResult,
150
+ status: ExtrinsicStatus.UNKNOWN
151
+ };
152
+ switch (recoverResult.status) {
149
153
  case HistoryRecoverStatus.API_INACTIVE:
150
154
  break;
151
155
  case HistoryRecoverStatus.FAILED:
152
156
  case HistoryRecoverStatus.SUCCESS:
153
- this.updateHistoryByExtrinsicHash(extrinsicHash, {
154
- status: status === HistoryRecoverStatus.SUCCESS ? ExtrinsicStatus.SUCCESS : ExtrinsicStatus.FAIL
155
- }).catch(console.error);
156
- delete this.#processingHistories[extrinsicHash];
157
+ updateData.status = recoverResult.status === HistoryRecoverStatus.SUCCESS ? ExtrinsicStatus.SUCCESS : ExtrinsicStatus.FAIL;
158
+ this.updateHistoryByExtrinsicHash(currentExtrinsicHash, updateData).catch(console.error);
159
+ delete this.#needRecoveryHistories[currentExtrinsicHash];
157
160
  break;
158
161
  default:
159
- delete this.#processingHistories[extrinsicHash];
162
+ this.updateHistoryByExtrinsicHash(currentExtrinsicHash, updateData).catch(console.error);
163
+ delete this.#needRecoveryHistories[currentExtrinsicHash];
160
164
  }
161
165
  });
162
- if (!Object.keys(this.#processingHistories).length) {
166
+ if (!Object.keys(this.#needRecoveryHistories).length) {
163
167
  await this.stopRecoverHistories();
164
168
  }
165
169
  }
@@ -169,7 +173,7 @@ export class HistoryService {
169
173
  await this.loadData();
170
174
  Promise.all([this.eventService.waitKeyringReady, this.eventService.waitChainReady]).then(() => {
171
175
  this.getHistories().catch(console.log);
172
- this.getProcessingHistory().catch(console.log);
176
+ this.recoverProcessingHistory().catch(console.error);
173
177
  this.eventService.on('account.add', () => {
174
178
  (async () => {
175
179
  await this.stopCron();
@@ -182,14 +186,18 @@ export class HistoryService {
182
186
  }).catch(console.error);
183
187
  this.status = ServiceStatus.INITIALIZED;
184
188
  }
185
- async getProcessingHistory() {
189
+ async recoverProcessingHistory() {
186
190
  const histories = await this.dbService.getHistories();
187
- this.#processingHistories = {};
191
+ this.#needRecoveryHistories = {};
188
192
  histories.filter(history => {
189
- return history.status === 'processing';
193
+ return [ExtrinsicStatus.PROCESSING, ExtrinsicStatus.SUBMITTING].includes(history.status);
190
194
  }).forEach(history => {
191
- this.#processingHistories[history.extrinsicHash] = history;
195
+ this.#needRecoveryHistories[history.extrinsicHash] = history;
192
196
  });
197
+ const recoverNumber = Object.keys(this.#needRecoveryHistories).length;
198
+ if (recoverNumber > 0) {
199
+ console.log(`Recover ${recoverNumber} processing history`);
200
+ }
193
201
  this.startRecoverHistories().catch(console.error);
194
202
  }
195
203
  async start() {
@@ -197,7 +205,6 @@ export class HistoryService {
197
205
  this.startPromiseHandler = createPromiseHandler();
198
206
  this.status = ServiceStatus.STARTING;
199
207
  await this.startCron();
200
- await this.startRecoverHistories();
201
208
  this.status = ServiceStatus.STARTED;
202
209
  this.startPromiseHandler.resolve();
203
210
  } catch (e) {
@@ -30,7 +30,7 @@ export default class NotificationService {
30
30
  type: 'basic',
31
31
  title,
32
32
  message,
33
- iconUrl: 'https://subwallet.app/assets/images/favicon/favicon-192x192.png',
33
+ iconUrl: './images/icon-128.png',
34
34
  priority: 2,
35
35
  isClickable: !!link
36
36
  }, notificationId => {
@@ -41,7 +41,7 @@ export default class DatabaseService {
41
41
  subscribeNominatorMetadata(callback: (data: NominatorMetadata[]) => void): void;
42
42
  getHistories(query?: HistoryQuery): Promise<import("@subwallet/extension-base/services/storage-service/databases").ITransactionHistoryItem[]>;
43
43
  upsertHistory(histories: TransactionHistoryItem[]): Promise<unknown>;
44
- updateHistoryByNewExtrinsicHash(extrinsicHash: string, updateData: Partial<TransactionHistoryItem>): Promise<unknown>;
44
+ updateHistoryByExtrinsicHash(extrinsicHash: string, updateData: Partial<TransactionHistoryItem>): Promise<unknown>;
45
45
  addNftCollection(collection: NftCollection): Promise<unknown>;
46
46
  deleteNftCollection(chain: string, collectionId: string): Promise<void>;
47
47
  getAllNftCollection(chainHashes?: string[]): import("dexie").PromiseExtended<NftCollection[]>;
@@ -113,7 +113,7 @@ export default class DatabaseService {
113
113
  const cleanedHistory = histories.filter(x => x && x.address && x.chain && x.extrinsicHash);
114
114
  return this.stores.transaction.bulkUpsert(cleanedHistory);
115
115
  }
116
- async updateHistoryByNewExtrinsicHash(extrinsicHash, updateData) {
116
+ async updateHistoryByExtrinsicHash(extrinsicHash, updateData) {
117
117
  const canUpdate = updateData && extrinsicHash;
118
118
  if (!canUpdate) {
119
119
  return;
@@ -272,7 +272,7 @@ export default class TransactionService {
272
272
  const chainInfo = this.chainService.getChainInfoByKey(transaction.chain);
273
273
  return getExplorerLink(chainInfo, transaction.extrinsicHash, 'tx');
274
274
  }
275
- transactionToHistories(id, eventLogs) {
275
+ transactionToHistories(id, startBlock, nonce, eventLogs) {
276
276
  const transaction = this.getTransaction(id);
277
277
  const historyItem = {
278
278
  origin: 'app',
@@ -290,9 +290,12 @@ export default class TransactionService {
290
290
  fee: transaction.estimateFee,
291
291
  blockNumber: 0,
292
292
  // Will be added in next step
293
- blockHash: '' // Will be added in next step
293
+ blockHash: '',
294
+ // Will be added in next step
295
+ nonce: nonce || 0,
296
+ startBlock: startBlock || 0
294
297
  };
295
-
298
+ console.log('historyItem', historyItem);
296
299
  const chainInfo = this.chainService.getChainInfoByKey(transaction.chain);
297
300
  const nativeAsset = _getChainNativeTokenBasicInfo(chainInfo);
298
301
  const baseNativeAmount = {
@@ -459,7 +462,9 @@ export default class TransactionService {
459
462
  console.debug(`Transaction "${id}" is signed`);
460
463
  }
461
464
  onSend({
462
- id
465
+ id,
466
+ nonce,
467
+ startBlock
463
468
  }) {
464
469
  // Update transaction status
465
470
  this.updateTransaction(id, {
@@ -467,7 +472,7 @@ export default class TransactionService {
467
472
  });
468
473
 
469
474
  // Create Input History Transaction History
470
- this.historyService.insertHistories(this.transactionToHistories(id)).catch(console.error);
475
+ this.historyService.insertHistories(this.transactionToHistories(id, startBlock, nonce)).catch(console.error);
471
476
  console.debug(`Transaction "${id}" is sent`);
472
477
  }
473
478
  onHasTransactionHash({
@@ -516,15 +521,18 @@ export default class TransactionService {
516
521
  onSuccess({
517
522
  blockHash,
518
523
  blockNumber,
524
+ extrinsicHash,
519
525
  id
520
526
  }) {
521
527
  const transaction = this.getTransaction(id);
522
528
  this.updateTransaction(id, {
523
- status: ExtrinsicStatus.SUCCESS
529
+ status: ExtrinsicStatus.SUCCESS,
530
+ extrinsicHash
524
531
  });
525
532
 
526
533
  // Write success transaction history
527
- this.historyService.updateHistories(transaction.chain, transaction.extrinsicHash, {
534
+ this.historyService.updateHistoryByExtrinsicHash(transaction.extrinsicHash, {
535
+ extrinsicHash,
528
536
  status: ExtrinsicStatus.SUCCESS,
529
537
  blockNumber: blockNumber || 0,
530
538
  blockHash: blockHash || ''
@@ -544,6 +552,7 @@ export default class TransactionService {
544
552
  blockHash,
545
553
  blockNumber,
546
554
  errors,
555
+ extrinsicHash,
547
556
  id
548
557
  }) {
549
558
  const transaction = this.getTransaction(id);
@@ -551,11 +560,13 @@ export default class TransactionService {
551
560
  if (transaction) {
552
561
  this.updateTransaction(id, {
553
562
  status: nextStatus,
554
- errors
563
+ errors,
564
+ extrinsicHash
555
565
  });
556
566
 
557
567
  // Write failed transaction history
558
- this.historyService.updateHistories(transaction.chain, transaction.extrinsicHash, {
568
+ this.historyService.updateHistoryByExtrinsicHash(transaction.extrinsicHash, {
569
+ extrinsicHash: extrinsicHash || transaction.extrinsicHash,
559
570
  status: nextStatus,
560
571
  blockNumber: blockNumber || 0,
561
572
  blockHash: blockHash || ''
@@ -654,7 +665,7 @@ export default class TransactionService {
654
665
  errors: [],
655
666
  warnings: []
656
667
  };
657
- this.requestService.addConfirmation(id, url || EXTENSION_REQUEST_URL, 'evmSendTransactionRequest', payload, {}).then(({
668
+ this.requestService.addConfirmation(id, url || EXTENSION_REQUEST_URL, 'evmSendTransactionRequest', payload, {}).then(async ({
658
669
  isApproved,
659
670
  payload
660
671
  }) => {
@@ -680,11 +691,16 @@ export default class TransactionService {
680
691
 
681
692
  // Send transaction
682
693
  this.handleTransactionTimeout(emitter, eventData);
694
+
695
+ // Add start info
696
+ eventData.nonce = txObject.nonce;
697
+ eventData.startBlock = await web3Api.eth.getBlockNumber();
683
698
  emitter.emit('send', eventData); // This event is needed after sending transaction with queue
684
699
  signedTransaction && web3Api.eth.sendSignedTransaction(signedTransaction).once('transactionHash', hash => {
685
700
  eventData.extrinsicHash = hash;
686
701
  emitter.emit('extrinsicHash', eventData);
687
702
  }).once('receipt', rs => {
703
+ eventData.extrinsicHash = rs.transactionHash;
688
704
  eventData.blockHash = rs.blockHash;
689
705
  eventData.blockNumber = rs.blockNumber;
690
706
  emitter.emit('success', eventData);
@@ -709,6 +725,7 @@ export default class TransactionService {
709
725
  }
710
726
  signAndSendSubstrateTransaction({
711
727
  address,
728
+ chain,
712
729
  id,
713
730
  transaction,
714
731
  url
@@ -729,11 +746,14 @@ export default class TransactionService {
729
746
  };
730
747
  }
731
748
  }
732
- }).then(rs => {
749
+ }).then(async rs => {
733
750
  // Emit signed event
734
751
  emitter.emit('signed', eventData);
735
752
 
736
753
  // Send transaction
754
+ const api = this.chainService.getSubstrateApi(chain);
755
+ eventData.nonce = rs.nonce.toNumber();
756
+ eventData.startBlock = (await api.api.query.system.number()).toPrimitive();
737
757
  this.handleTransactionTimeout(emitter, eventData);
738
758
  emitter.emit('send', eventData); // This event is needed after sending transaction with queue
739
759
 
@@ -751,6 +771,7 @@ export default class TransactionService {
751
771
  }
752
772
  }
753
773
  if (txState.status.isFinalized) {
774
+ eventData.extrinsicHash = txState.txHash.toHex();
754
775
  eventData.eventLogs = txState.events;
755
776
  // TODO: push block hash and block number into eventData
756
777
  txState.events.filter(({
@@ -39,6 +39,8 @@ export interface TransactionEventResponse extends ValidateTransactionResponse {
39
39
  blockHash?: string;
40
40
  blockNumber?: number;
41
41
  eventLogs?: EventRecord[];
42
+ nonce?: number;
43
+ startBlock?: number;
42
44
  }
43
45
  export interface TransactionEventMap {
44
46
  send: (response: TransactionEventResponse) => void;
package/utils/index.js CHANGED
@@ -15,6 +15,9 @@ export function isAccountAll(address) {
15
15
  }
16
16
  export function reformatAddress(address, networkPrefix = 42, isEthereum = false) {
17
17
  try {
18
+ if (!address || address === '') {
19
+ return '';
20
+ }
18
21
  if (isEthereumAddress(address)) {
19
22
  return address;
20
23
  }