@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.
- package/background/KoniTypes.d.ts +2 -0
- package/cjs/koni/background/handlers/Extension.js +1 -1
- package/cjs/packageInfo.js +1 -1
- package/cjs/services/history-service/helpers/recoverHistoryStatus.js +157 -38
- package/cjs/services/history-service/index.js +26 -19
- package/cjs/services/notification-service/NotificationService.js +1 -1
- package/cjs/services/storage-service/DatabaseService.js +1 -1
- package/cjs/services/transaction-service/index.js +32 -11
- package/cjs/utils/index.js +3 -0
- package/koni/background/handlers/Extension.js +1 -1
- package/package.json +5 -5
- package/packageInfo.js +1 -1
- package/services/history-service/helpers/recoverHistoryStatus.d.ts +7 -1
- package/services/history-service/helpers/recoverHistoryStatus.js +151 -35
- package/services/history-service/index.d.ts +1 -1
- package/services/history-service/index.js +26 -19
- package/services/notification-service/NotificationService.js +1 -1
- package/services/storage-service/DatabaseService.d.ts +1 -1
- package/services/storage-service/DatabaseService.js +1 -1
- package/services/transaction-service/index.js +32 -11
- package/services/transaction-service/types.d.ts +2 -0
- 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;
|
|
@@ -1299,7 +1299,7 @@ class KoniExtension {
|
|
|
1299
1299
|
const isPasswordValidated = this.validatedAccountsPassword(file, password);
|
|
1300
1300
|
if (isPasswordValidated) {
|
|
1301
1301
|
try {
|
|
1302
|
-
this._saveCurrentAccountAddress(
|
|
1302
|
+
this._saveCurrentAccountAddress(_constants.ALL_ACCOUNT_KEY, () => {
|
|
1303
1303
|
_uiKeyring.keyring.restoreAccounts(file, password);
|
|
1304
1304
|
this._addAddressesToAuthList(addressList, isAllowed);
|
|
1305
1305
|
});
|
package/cjs/packageInfo.js
CHANGED
|
@@ -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
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
83
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
#
|
|
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.
|
|
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.#
|
|
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
|
|
153
|
-
|
|
154
|
-
const
|
|
155
|
-
|
|
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
|
-
|
|
161
|
-
|
|
162
|
-
|
|
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
|
-
|
|
169
|
+
this.updateHistoryByExtrinsicHash(currentExtrinsicHash, updateData).catch(console.error);
|
|
170
|
+
delete this.#needRecoveryHistories[currentExtrinsicHash];
|
|
167
171
|
}
|
|
168
172
|
});
|
|
169
|
-
if (!Object.keys(this.#
|
|
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.
|
|
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
|
|
196
|
+
async recoverProcessingHistory() {
|
|
193
197
|
const histories = await this.dbService.getHistories();
|
|
194
|
-
this.#
|
|
198
|
+
this.#needRecoveryHistories = {};
|
|
195
199
|
histories.filter(history => {
|
|
196
|
-
return history.status
|
|
200
|
+
return [_KoniTypes.ExtrinsicStatus.PROCESSING, _KoniTypes.ExtrinsicStatus.SUBMITTING].includes(history.status);
|
|
197
201
|
}).forEach(history => {
|
|
198
|
-
this.#
|
|
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) {
|
|
@@ -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
|
|
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: ''
|
|
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.
|
|
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.
|
|
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 => {
|
package/cjs/utils/index.js
CHANGED
|
@@ -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(
|
|
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-
|
|
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-
|
|
1697
|
-
"@subwallet/extension-chains": "^1.0.5-
|
|
1698
|
-
"@subwallet/extension-dapp": "^1.0.5-
|
|
1699
|
-
"@subwallet/extension-inject": "^1.0.5-
|
|
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-
|
|
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
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
74
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
#
|
|
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.
|
|
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.#
|
|
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
|
|
146
|
-
|
|
147
|
-
const
|
|
148
|
-
|
|
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
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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
|
-
|
|
162
|
+
this.updateHistoryByExtrinsicHash(currentExtrinsicHash, updateData).catch(console.error);
|
|
163
|
+
delete this.#needRecoveryHistories[currentExtrinsicHash];
|
|
160
164
|
}
|
|
161
165
|
});
|
|
162
|
-
if (!Object.keys(this.#
|
|
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.
|
|
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
|
|
189
|
+
async recoverProcessingHistory() {
|
|
186
190
|
const histories = await this.dbService.getHistories();
|
|
187
|
-
this.#
|
|
191
|
+
this.#needRecoveryHistories = {};
|
|
188
192
|
histories.filter(history => {
|
|
189
|
-
return history.status
|
|
193
|
+
return [ExtrinsicStatus.PROCESSING, ExtrinsicStatus.SUBMITTING].includes(history.status);
|
|
190
194
|
}).forEach(history => {
|
|
191
|
-
this.#
|
|
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: '
|
|
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
|
-
|
|
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
|
|
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: ''
|
|
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.
|
|
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.
|
|
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