@subwallet/extension-base 1.3.73-0 → 1.3.74-1
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 +42 -5
- package/background/KoniTypes.js +14 -1
- package/cjs/background/KoniTypes.js +16 -2
- package/cjs/core/logic-validation/transfer.js +35 -57
- package/cjs/koni/background/handlers/Extension.js +599 -144
- package/cjs/koni/background/handlers/State.js +5 -2
- package/cjs/koni/background/handlers/Tabs.js +3 -2
- package/cjs/packageInfo.js +1 -1
- package/cjs/services/balance-service/helpers/subscribe/substrate/index.js +0 -2
- package/cjs/services/chain-service/handler/SubstrateApi.js +6 -1
- package/cjs/services/chain-service/index.js +1 -0
- package/cjs/services/chain-service/utils/index.js +4 -0
- package/cjs/services/event-service/index.js +1 -0
- package/cjs/services/fee-service/utils/index.js +4 -4
- package/cjs/services/inapp-notification-service/consts.js +4 -2
- package/cjs/services/inapp-notification-service/index.js +51 -6
- package/cjs/services/inapp-notification-service/interfaces.js +2 -0
- package/cjs/services/inapp-notification-service/utils/common.js +4 -0
- package/cjs/services/keyring-service/context/account-context.js +44 -0
- package/cjs/services/keyring-service/context/handlers/Multisig.js +186 -0
- package/cjs/services/keyring-service/context/state.js +12 -0
- package/cjs/services/multisig-service/index.js +627 -0
- package/cjs/services/multisig-service/utils.js +242 -0
- package/cjs/services/request-service/handler/SubstrateRequestHandler.js +25 -0
- package/cjs/services/request-service/index.js +5 -1
- package/cjs/services/storage-service/DatabaseService.js +5 -2
- package/cjs/services/storage-service/db-stores/InappNotification.js +20 -2
- package/cjs/services/substrate-proxy-service/index.js +22 -7
- package/cjs/services/transaction-service/helpers/index.js +8 -0
- package/cjs/services/transaction-service/index.js +348 -147
- package/cjs/services/transaction-service/types.js +18 -1
- package/cjs/types/account/info/keyring.js +5 -0
- package/cjs/types/account/info/proxy.js +1 -0
- package/cjs/types/multisig/index.js +14 -0
- package/cjs/types/transaction/error.js +9 -2
- package/cjs/utils/account/transform.js +28 -4
- package/cjs/utils/logger/Logger.js +294 -0
- package/cjs/utils/logger/index.js +42 -0
- package/cjs/utils/logger/types.js +1 -0
- package/core/logic-validation/transfer.d.ts +2 -2
- package/core/logic-validation/transfer.js +10 -32
- package/koni/background/handlers/Extension.d.ts +7 -0
- package/koni/background/handlers/Extension.js +498 -43
- package/koni/background/handlers/State.d.ts +2 -0
- package/koni/background/handlers/State.js +5 -2
- package/koni/background/handlers/Tabs.js +3 -2
- package/package.json +42 -6
- package/packageInfo.js +1 -1
- package/services/balance-service/helpers/subscribe/substrate/index.js +0 -2
- package/services/chain-service/handler/SubstrateApi.js +7 -2
- package/services/chain-service/index.js +1 -0
- package/services/chain-service/types.d.ts +1 -1
- package/services/chain-service/utils/index.js +4 -0
- package/services/event-service/index.d.ts +1 -0
- package/services/event-service/index.js +1 -0
- package/services/event-service/types.d.ts +1 -0
- package/services/fee-service/utils/index.js +4 -4
- package/services/inapp-notification-service/consts.d.ts +3 -1
- package/services/inapp-notification-service/consts.js +5 -3
- package/services/inapp-notification-service/index.d.ts +3 -2
- package/services/inapp-notification-service/index.js +51 -6
- package/services/inapp-notification-service/interfaces.d.ts +18 -2
- package/services/inapp-notification-service/interfaces.js +2 -0
- package/services/inapp-notification-service/utils/common.d.ts +1 -0
- package/services/inapp-notification-service/utils/common.js +3 -0
- package/services/keyring-service/context/account-context.d.ts +9 -1
- package/services/keyring-service/context/account-context.js +44 -0
- package/services/keyring-service/context/handlers/Multisig.d.ts +18 -0
- package/services/keyring-service/context/handlers/Multisig.js +180 -0
- package/services/keyring-service/context/state.d.ts +2 -0
- package/services/keyring-service/context/state.js +12 -0
- package/services/multisig-service/index.d.ts +245 -0
- package/services/multisig-service/index.js +620 -0
- package/services/multisig-service/utils.d.ts +95 -0
- package/services/multisig-service/utils.js +227 -0
- package/services/request-service/handler/SubstrateRequestHandler.d.ts +1 -0
- package/services/request-service/handler/SubstrateRequestHandler.js +25 -0
- package/services/request-service/index.d.ts +2 -1
- package/services/request-service/index.js +5 -1
- package/services/storage-service/DatabaseService.d.ts +3 -2
- package/services/storage-service/DatabaseService.js +5 -2
- package/services/storage-service/db-stores/InappNotification.d.ts +3 -2
- package/services/storage-service/db-stores/InappNotification.js +20 -2
- package/services/substrate-proxy-service/index.d.ts +4 -1
- package/services/substrate-proxy-service/index.js +22 -8
- package/services/transaction-service/helpers/index.js +8 -0
- package/services/transaction-service/index.d.ts +31 -0
- package/services/transaction-service/index.js +270 -69
- package/services/transaction-service/types.d.ts +28 -3
- package/services/transaction-service/types.js +12 -1
- package/types/account/info/keyring.d.ts +14 -1
- package/types/account/info/keyring.js +6 -0
- package/types/account/info/proxy.d.ts +1 -0
- package/types/account/info/proxy.js +1 -0
- package/types/multisig/index.d.ts +76 -0
- package/types/multisig/index.js +8 -0
- package/types/notification/index.d.ts +8 -0
- package/types/substrateProxyAccount/index.d.ts +26 -1
- package/types/transaction/error.d.ts +6 -1
- package/types/transaction/error.js +7 -1
- package/types/transaction/request.d.ts +0 -1
- package/utils/account/transform.js +28 -4
- package/utils/logger/Logger.d.ts +31 -0
- package/utils/logger/Logger.js +267 -0
- package/utils/logger/index.d.ts +15 -0
- package/utils/logger/index.js +29 -0
- package/utils/logger/types.d.ts +23 -0
- package/utils/logger/types.js +1 -0
|
@@ -0,0 +1,627 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.MultisigTxType = exports.MultisigService = exports.MULTISIG_TX_TYPE_MAP = void 0;
|
|
7
|
+
var _KoniTypes = require("@subwallet/extension-base/background/KoniTypes");
|
|
8
|
+
var _constants = require("@subwallet/extension-base/constants");
|
|
9
|
+
var _types = require("@subwallet/extension-base/services/base/types");
|
|
10
|
+
var _consts = require("@subwallet/extension-base/services/inapp-notification-service/consts");
|
|
11
|
+
var _interfaces = require("@subwallet/extension-base/services/inapp-notification-service/interfaces");
|
|
12
|
+
var _utils = require("@subwallet/extension-base/services/multisig-service/utils");
|
|
13
|
+
var _utils2 = require("@subwallet/extension-base/utils");
|
|
14
|
+
var _logger = require("@subwallet/extension-base/utils/logger");
|
|
15
|
+
var _rxjs = require("rxjs");
|
|
16
|
+
// Copyright 2019-2022 @subwallet/extension-base authors & contributors
|
|
17
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Query key for multisig multisigs subscription
|
|
21
|
+
*/
|
|
22
|
+
const MULTISIG_QUERY_KEY = 'query_multisig_multisigs';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Interface representing multisig extrinsic data from the Substrate pallet
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
const multisigServiceLogger = (0, _logger.createLogger)('MultisigService');
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Interface representing a pending multisig extrinsic with the current signer context
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Interface representing raw pending multisig extrinsic data from the chain
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Map of pending multisig extrinsics
|
|
40
|
+
* Key is created using genPendingMultisigTxKey function
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Request interface for getting pending extrinsics
|
|
45
|
+
*/
|
|
46
|
+
let MultisigTxType;
|
|
47
|
+
/**
|
|
48
|
+
* Mapping of extrinsic categories to their corresponding pallet methods
|
|
49
|
+
* Used to categorize multisig extrinsics by their call method
|
|
50
|
+
*/
|
|
51
|
+
exports.MultisigTxType = MultisigTxType;
|
|
52
|
+
(function (MultisigTxType) {
|
|
53
|
+
MultisigTxType["TRANSFER"] = "Transfer";
|
|
54
|
+
MultisigTxType["TRANSFER_NFT"] = "TransferNFT";
|
|
55
|
+
MultisigTxType["STAKING"] = "Staking";
|
|
56
|
+
MultisigTxType["REDEEM"] = "Redeem";
|
|
57
|
+
MultisigTxType["UNSTAKE"] = "Unstake";
|
|
58
|
+
MultisigTxType["WITHDRAW"] = "Withdraw";
|
|
59
|
+
MultisigTxType["CANCEL_UNSTAKE"] = "CancelUnstake";
|
|
60
|
+
MultisigTxType["CLAIM_REWARD"] = "ClaimReward";
|
|
61
|
+
MultisigTxType["NOMINATE"] = "Nominate";
|
|
62
|
+
MultisigTxType["LENDING"] = "Lending";
|
|
63
|
+
MultisigTxType["SWAP"] = "Swap";
|
|
64
|
+
MultisigTxType["SET_TOKEN_PAY_FEE"] = "SetTokenPayFee";
|
|
65
|
+
MultisigTxType["GOV_VOTE"] = "govVote";
|
|
66
|
+
MultisigTxType["GOV_REMOVE_VOTE"] = "govRemoveVote";
|
|
67
|
+
MultisigTxType["GOV_UNLOCK_VOTE"] = "govUnlockVote";
|
|
68
|
+
MultisigTxType["ADD_PROXY"] = "AddProxy";
|
|
69
|
+
MultisigTxType["REMOVE_PROXY"] = "RemoveProxy";
|
|
70
|
+
MultisigTxType["UNKNOWN"] = "Unknown";
|
|
71
|
+
})(MultisigTxType || (exports.MultisigTxType = MultisigTxType = {}));
|
|
72
|
+
const MULTISIG_TX_TYPE_MAP = {
|
|
73
|
+
transfer: ['balances.transferAll', 'balances.transferKeepAlive', 'balances.transfer', 'foreignAssets.transfer', 'foreignAssets.transferKeepAlive', 'currencies.transfer', 'tokens.transferAll', 'tokens.transfer', 'assets.transfer', 'assetManager.transfer', 'subtensorModule.transferStake'],
|
|
74
|
+
transfer_nft: ['nft.transfer', 'nfts.transfer', 'unique.transfer', 'uniques.transfer'],
|
|
75
|
+
staking: ['homa.mint', 'vtokenMinting.mint', 'liquidStaking.stake', 'parachainStaking.joinDelegators', 'parachainStaking.delegatorStakeMore', 'dappsStaking.bondAndStake', 'parachainStaking.nominate', 'parachainStaking.bondExtra', 'collatorStaking.lock', 'collatorStaking.stake', 'parachainStaking.delegate', 'parachainStaking.delegateWithAutoCompound', 'parachainStaking.delegatorBondMore', 'staking.bond', 'pooledStaking.requestDelegate', 'subtensorModule.addStakeLimit', 'nominationPools.bondExtra', 'nominationPools.join'],
|
|
76
|
+
redeem: ['aggregatedDex.swapWithExactSupply', 'stablePool.swap', 'ammRoute.swapExactTokensForTokens'],
|
|
77
|
+
unstake: ['homa.requestRedeem', 'vtokenMinting.redeem', 'liquidStaking.unstake', 'parachainStaking.delegatorStakeLess', 'parachainStaking.leaveDelegators', 'dappsStaking.unbondAndUnstake', 'parachainStaking.scheduleNominatorUnbond', 'parachainStaking.scheduleRevokeNomination', 'collatorStaking.unstakeFrom && collatorStaking.unlock', 'parachainStaking.scheduleDelegatorBondLess', 'parachainStaking.scheduleRevokeDelegation', 'staking.unbond', 'pooledStaking.requestUndelegate', 'subtensorModule.removeStakeLimit', 'nominationPools.unbond'],
|
|
78
|
+
withdraw: ['homa.claimRedemption', 'parachainStaking.unlockUnstaked', 'dappsStaking.withdrawUnbonded', 'parachainStaking.executeNominationRequest', 'collatorStaking.release', 'parachainStaking.executeDelegationRequest', 'staking.withdrawUnbonded', 'nominationPools.withdrawUnbonded'],
|
|
79
|
+
cancelUnstake: ['parachainStaking.cancelLeaveCandidates', 'parachainStaking.cancelNominationRequest', 'parachainStaking.cancelDelegationRequest', 'staking.rebond'],
|
|
80
|
+
claim: ['parachainStaking.incrementDelegatorRewards', 'parachainStaking.claimRewards', 'dappsStaking.claimStaker', 'collatorStaking.claimRewards', 'pooledStaking.claimManualRewards', 'nominationPools.claimPayout'],
|
|
81
|
+
nominate: ['staking.nominate', 'subtensorModule.moveStake'],
|
|
82
|
+
lending: ['loans.mint', 'loans.redeem', 'loans.redeemAll'],
|
|
83
|
+
// consider remove
|
|
84
|
+
swap: ['assetConversion.swapExactTokensForTokens'],
|
|
85
|
+
setTokenPayFee: ['multiTransactionPayment.setCurrency'],
|
|
86
|
+
govVote: ['convictionVoting.vote'],
|
|
87
|
+
govRemoveVote: ['convictionVoting.removeVote'],
|
|
88
|
+
govUnlockVote: ['convictionVoting.unlock'],
|
|
89
|
+
addProxy: ['proxy.addProxy'],
|
|
90
|
+
removeProxy: ['proxy.removeProxy', 'proxy.removeProxies']
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Service for managing multisig extrinsics
|
|
95
|
+
* Handles subscription to pending multisig extrinsics across supported chains
|
|
96
|
+
* and provides methods to query and monitor multisig extrinsic status
|
|
97
|
+
*/
|
|
98
|
+
exports.MULTISIG_TX_TYPE_MAP = MULTISIG_TX_TYPE_MAP;
|
|
99
|
+
class MultisigService {
|
|
100
|
+
status = _types.ServiceStatus.NOT_INITIALIZED;
|
|
101
|
+
startPromiseHandler = (0, _utils2.createPromiseHandler)();
|
|
102
|
+
stopPromiseHandler = (0, _utils2.createPromiseHandler)();
|
|
103
|
+
|
|
104
|
+
/** BehaviorSubject that holds the current map of pending multisig extrinsics */
|
|
105
|
+
pendingMultisigTxSubject = new _rxjs.BehaviorSubject({});
|
|
106
|
+
/** Function to unsubscribe from all active subscriptions */
|
|
107
|
+
|
|
108
|
+
/** Promise to check if the subscription logic is currently running */
|
|
109
|
+
|
|
110
|
+
/** Set to track notified transaction keys to avoid duplicate notifications */
|
|
111
|
+
notifiedTxKeys = new Set();
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Creates an instance of MultisigService
|
|
115
|
+
* @param eventService - Service for handling application events
|
|
116
|
+
* @param chainService - Service for managing chain connections
|
|
117
|
+
* @param keyringService - Service for managing accounts and keyring
|
|
118
|
+
* @param inappNotificationService - Service for creating in-app notifications (optional)
|
|
119
|
+
*/
|
|
120
|
+
constructor(eventService, chainService, keyringService, inappNotificationService) {
|
|
121
|
+
this.eventService = eventService;
|
|
122
|
+
this.chainService = chainService;
|
|
123
|
+
this.keyringService = keyringService;
|
|
124
|
+
this.inappNotificationService = inappNotificationService;
|
|
125
|
+
this.status = _types.ServiceStatus.NOT_INITIALIZED;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Starts the multisig service
|
|
130
|
+
* Subscribes to pending multisig extrinsics for all multisig accounts
|
|
131
|
+
* @returns Promise that resolves when the service has started, or rejects on error
|
|
132
|
+
*/
|
|
133
|
+
async start() {
|
|
134
|
+
try {
|
|
135
|
+
if (this.status === _types.ServiceStatus.STOPPING) {
|
|
136
|
+
await this.waitForStopped();
|
|
137
|
+
}
|
|
138
|
+
if (this.status === _types.ServiceStatus.STARTED || this.status === _types.ServiceStatus.STARTING) {
|
|
139
|
+
return await this.waitForStarted();
|
|
140
|
+
}
|
|
141
|
+
this.status = _types.ServiceStatus.STARTING;
|
|
142
|
+
await this.runSubscribePendingMultisigTxs();
|
|
143
|
+
this.stopPromiseHandler = (0, _utils2.createPromiseHandler)();
|
|
144
|
+
this.status = _types.ServiceStatus.STARTED;
|
|
145
|
+
this.eventService.emit('multisig-service.ready', true);
|
|
146
|
+
this.startPromiseHandler.resolve();
|
|
147
|
+
} catch (error) {
|
|
148
|
+
this.status = _types.ServiceStatus.NOT_INITIALIZED;
|
|
149
|
+
this.startPromiseHandler.reject(error);
|
|
150
|
+
throw error;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Stops the multisig service
|
|
156
|
+
* Unsubscribes from all active subscriptions
|
|
157
|
+
* @returns Promise that resolves when the service has stopped, or rejects on error
|
|
158
|
+
*/
|
|
159
|
+
async stop() {
|
|
160
|
+
try {
|
|
161
|
+
if (this.status === _types.ServiceStatus.STARTING) {
|
|
162
|
+
await this.waitForStarted();
|
|
163
|
+
}
|
|
164
|
+
if (this.status === _types.ServiceStatus.STOPPED || this.status === _types.ServiceStatus.STOPPING) {
|
|
165
|
+
return await this.waitForStopped();
|
|
166
|
+
}
|
|
167
|
+
this.status = _types.ServiceStatus.STOPPING;
|
|
168
|
+
this.runUnsubscribePendingMultisigTxs();
|
|
169
|
+
this.startPromiseHandler = (0, _utils2.createPromiseHandler)();
|
|
170
|
+
this.status = _types.ServiceStatus.STOPPED;
|
|
171
|
+
this.stopPromiseHandler.resolve();
|
|
172
|
+
} catch (error) {
|
|
173
|
+
this.stopPromiseHandler.reject(error);
|
|
174
|
+
throw error;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Initializes the multisig service
|
|
180
|
+
* Waits for chain and account services to be ready, then sets up event listeners
|
|
181
|
+
* @returns Promise that resolves when initialization is complete
|
|
182
|
+
*/
|
|
183
|
+
async init() {
|
|
184
|
+
this.status = _types.ServiceStatus.INITIALIZING;
|
|
185
|
+
await this.eventService.waitChainReady;
|
|
186
|
+
await this.eventService.waitAccountReady;
|
|
187
|
+
this.status = _types.ServiceStatus.INITIALIZED;
|
|
188
|
+
this.eventService.onLazy(this.handleEvents.bind(this));
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Waits for the service to start
|
|
193
|
+
* @returns Promise that resolves when the service has started
|
|
194
|
+
*/
|
|
195
|
+
waitForStarted() {
|
|
196
|
+
return this.startPromiseHandler.promise;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Waits for the service to stop
|
|
201
|
+
* @returns Promise that resolves when the service has stopped
|
|
202
|
+
*/
|
|
203
|
+
waitForStopped() {
|
|
204
|
+
return this.stopPromiseHandler.promise;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Handles application events and reloads multisig extrinsics when needed
|
|
209
|
+
* Reloads when accounts are added/removed or when supported chain state is updated
|
|
210
|
+
* @param events - Array of event items
|
|
211
|
+
* @param eventTypes - Array of event types that occurred
|
|
212
|
+
*/
|
|
213
|
+
handleEvents(events, eventTypes) {
|
|
214
|
+
let needReload = false;
|
|
215
|
+
if (eventTypes.includes('account.add') || eventTypes.includes('account.remove')) {
|
|
216
|
+
needReload = true;
|
|
217
|
+
}
|
|
218
|
+
if (eventTypes.includes('chain.updateState') || eventTypes.includes('transaction.done')) {
|
|
219
|
+
for (const event of events) {
|
|
220
|
+
if (event.type === 'chain.updateState') {
|
|
221
|
+
var _chainInfo$substrateI;
|
|
222
|
+
const chainSlug = event.data[0];
|
|
223
|
+
const chainInfo = this.chainService.getChainInfoByKey(chainSlug);
|
|
224
|
+
|
|
225
|
+
// Only reload if the updated chain is in the supported chains list
|
|
226
|
+
if ((_chainInfo$substrateI = chainInfo.substrateInfo) !== null && _chainInfo$substrateI !== void 0 && _chainInfo$substrateI.supportMultisig) {
|
|
227
|
+
needReload = true;
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
230
|
+
} else if (event.type === 'transaction.done') {
|
|
231
|
+
const txResult = event.data[0];
|
|
232
|
+
if (txResult.wrappingStatus === 'WRAP_RESULT') {
|
|
233
|
+
needReload = true;
|
|
234
|
+
break;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
if (needReload) {
|
|
240
|
+
(0, _utils2.addLazy)('reloadPendingMultisigTxsByEvents', () => {
|
|
241
|
+
if (this.status === _types.ServiceStatus.STARTED) {
|
|
242
|
+
this.runSubscribePendingMultisigTxs().catch(e => multisigServiceLogger.error('Error in handleEvents reload', e));
|
|
243
|
+
}
|
|
244
|
+
}, 2000, undefined, true);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Subscribes to multisig changes for all multisig addresses across supported chains
|
|
250
|
+
* Clears old subscriptions before creating new ones to avoid duplicates
|
|
251
|
+
* @returns Promise that resolves when subscription setup is complete
|
|
252
|
+
*/
|
|
253
|
+
async runSubscribePendingMultisigTxs() {
|
|
254
|
+
if (this.status === _types.ServiceStatus.STOPPING || this.status === _types.ServiceStatus.STOPPED) {
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
if (this.subscribePromise) {
|
|
258
|
+
return this.subscribePromise;
|
|
259
|
+
}
|
|
260
|
+
this.subscribePromise = (async () => {
|
|
261
|
+
await Promise.all([this.eventService.waitKeyringReady, this.eventService.waitChainReady]);
|
|
262
|
+
this.runUnsubscribePendingMultisigTxs();
|
|
263
|
+
const multisigAccounts = this.keyringService.context.getMultisigAccounts();
|
|
264
|
+
if (!multisigAccounts.length) {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
let cancel = false;
|
|
268
|
+
const unsubList = [];
|
|
269
|
+
const activeChainInfoMap = this.chainService.getActiveChainInfoMap();
|
|
270
|
+
const supportedActiveChains = Object.values(activeChainInfoMap).reduce((rs, chainInfo) => {
|
|
271
|
+
var _chainInfo$substrateI2;
|
|
272
|
+
if ((_chainInfo$substrateI2 = chainInfo.substrateInfo) !== null && _chainInfo$substrateI2 !== void 0 && _chainInfo$substrateI2.supportMultisig) {
|
|
273
|
+
rs.push(chainInfo.slug);
|
|
274
|
+
}
|
|
275
|
+
return rs;
|
|
276
|
+
}, []);
|
|
277
|
+
for (const chain of supportedActiveChains) {
|
|
278
|
+
const chainInfo = this.chainService.getChainInfoByKey(chain);
|
|
279
|
+
for (const account of multisigAccounts) {
|
|
280
|
+
const multisigAddress = account.id;
|
|
281
|
+
const signers = account.accounts[0].signers;
|
|
282
|
+
const reformatMultisigAddress = (0, _utils2._reformatAddressWithChain)(multisigAddress, chainInfo);
|
|
283
|
+
const reformatSigners = signers.map(s => (0, _utils2._reformatAddressWithChain)(s, chainInfo));
|
|
284
|
+
const threshold = account.accounts[0].threshold;
|
|
285
|
+
const unsub = this.subscribePendingMultisigTxs(chain, reformatMultisigAddress, reformatSigners, threshold, rs => {
|
|
286
|
+
multisigServiceLogger.debug(`pending multisig txs of address ${reformatMultisigAddress}`, rs);
|
|
287
|
+
!cancel && this.updatePendingMultisigTxSubjectByChain(reformatMultisigAddress, chain, rs);
|
|
288
|
+
});
|
|
289
|
+
unsubList.push(unsub);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
this.unsubscribes = () => {
|
|
293
|
+
cancel = true;
|
|
294
|
+
unsubList.forEach(unsub => {
|
|
295
|
+
unsub === null || unsub === void 0 ? void 0 : unsub();
|
|
296
|
+
});
|
|
297
|
+
};
|
|
298
|
+
})();
|
|
299
|
+
try {
|
|
300
|
+
await this.subscribePromise;
|
|
301
|
+
} finally {
|
|
302
|
+
this.subscribePromise = undefined;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Subscribes to pending multisig extrinsics for a specific multisig address on a chain
|
|
308
|
+
* Fetches initial data and sets up a subscription for updates
|
|
309
|
+
* @param chain - Chain identifier
|
|
310
|
+
* @param multisigAddress - Multisig address to monitor
|
|
311
|
+
* @param signers - List of signer addresses for the multisig
|
|
312
|
+
* @param threshold - Number of approval required
|
|
313
|
+
* @param callback - Callback function called with updated pending extrinsics
|
|
314
|
+
* @returns Function to unsubscribe from the subscription
|
|
315
|
+
*/
|
|
316
|
+
async subscribePendingMultisigTxsPromise(chain, multisigAddress, signers, threshold, callback) {
|
|
317
|
+
const substrateApi = await this.chainService.getSubstrateApi(chain).isReady;
|
|
318
|
+
if (!substrateApi.api.query.multisig.multisigs) {
|
|
319
|
+
multisigServiceLogger.warn('This chain is not has multisig pallet', chain);
|
|
320
|
+
return () => undefined;
|
|
321
|
+
}
|
|
322
|
+
const keyQuery = MULTISIG_QUERY_KEY;
|
|
323
|
+
const rawKeys = await substrateApi.api.query.multisig.multisigs.keys(multisigAddress);
|
|
324
|
+
const rawKeysArgs = rawKeys.map(rawKey => rawKey.args);
|
|
325
|
+
const params = [{
|
|
326
|
+
section: 'query',
|
|
327
|
+
module: keyQuery.split('_')[1],
|
|
328
|
+
method: keyQuery.split('_')[2],
|
|
329
|
+
args: rawKeysArgs
|
|
330
|
+
}];
|
|
331
|
+
const subscription = substrateApi.subscribeDataWithMulti(params, async rs => {
|
|
332
|
+
try {
|
|
333
|
+
const items = [];
|
|
334
|
+
const pendingMultisigEntries = rs[keyQuery];
|
|
335
|
+
const blockCache = {};
|
|
336
|
+
await Promise.all(pendingMultisigEntries.map(async (_pendingMultisigInfo, index) => {
|
|
337
|
+
const pendingMultisigInfo = _pendingMultisigInfo;
|
|
338
|
+
if (!pendingMultisigInfo) {
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
const blockHeight = pendingMultisigInfo.when.height;
|
|
342
|
+
const extrinsicIndex = pendingMultisigInfo.when.index;
|
|
343
|
+
const callHash = rawKeysArgs[index][1].toHex();
|
|
344
|
+
|
|
345
|
+
// Cache block-level data to avoid many RPC calls
|
|
346
|
+
let blockInfo = blockCache[blockHeight];
|
|
347
|
+
if (!blockInfo) {
|
|
348
|
+
const blockHash = await substrateApi.api.rpc.chain.getBlockHash(blockHeight);
|
|
349
|
+
const signedBlock = await substrateApi.api.rpc.chain.getBlock(blockHash);
|
|
350
|
+
const apiAt = await substrateApi.api.at(blockHash);
|
|
351
|
+
const timestamp = (await apiAt.query.timestamp.now()).toNumber();
|
|
352
|
+
blockInfo = {
|
|
353
|
+
blockHash,
|
|
354
|
+
signedBlock,
|
|
355
|
+
timestamp
|
|
356
|
+
};
|
|
357
|
+
blockCache[blockHeight] = blockInfo;
|
|
358
|
+
}
|
|
359
|
+
const extrinsicHash = blockInfo.signedBlock.block.extrinsics[extrinsicIndex].hash.toHex();
|
|
360
|
+
const callData = blockInfo.blockHash.toHex() === _utils.DEFAULT_BLOCK_HASH ? undefined : (0, _utils.getCallData)({
|
|
361
|
+
callHash,
|
|
362
|
+
extrinsicIndex,
|
|
363
|
+
block: blockInfo.signedBlock.block
|
|
364
|
+
});
|
|
365
|
+
const decodedCallData = (0, _utils.decodeCallData)({
|
|
366
|
+
api: substrateApi.api,
|
|
367
|
+
callData
|
|
368
|
+
});
|
|
369
|
+
items.push({
|
|
370
|
+
chain,
|
|
371
|
+
multisigAddress,
|
|
372
|
+
callHash,
|
|
373
|
+
callData,
|
|
374
|
+
decodedCallData,
|
|
375
|
+
blockHeight,
|
|
376
|
+
extrinsicIndex,
|
|
377
|
+
extrinsicHash,
|
|
378
|
+
threshold,
|
|
379
|
+
signerAddresses: signers,
|
|
380
|
+
depositAmount: pendingMultisigInfo.deposit,
|
|
381
|
+
depositor: pendingMultisigInfo.depositor,
|
|
382
|
+
approvals: pendingMultisigInfo.approvals,
|
|
383
|
+
timestamp: blockInfo.timestamp,
|
|
384
|
+
multisigTxType: (0, _utils.getMultisigTxType)(decodedCallData)
|
|
385
|
+
});
|
|
386
|
+
}));
|
|
387
|
+
callback(items);
|
|
388
|
+
} catch (error) {
|
|
389
|
+
multisigServiceLogger.error(`Multisig Service subscription error ${chain}/${multisigAddress}`, error);
|
|
390
|
+
(0, _utils2.addLazy)(`resubscribeMultisig_${chain}_${multisigAddress}`, () => {
|
|
391
|
+
if (this.status === _types.ServiceStatus.STARTED) {
|
|
392
|
+
this.runSubscribePendingMultisigTxs().catch(e => multisigServiceLogger.error('Error during resubscribeMultisig', e));
|
|
393
|
+
}
|
|
394
|
+
}, 1000, 4000, true);
|
|
395
|
+
}
|
|
396
|
+
});
|
|
397
|
+
return () => subscription.unsubscribe();
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Wrapper function to subscribe to pending multisig extrinsics
|
|
402
|
+
* Returns an unsubscribe function that handles promise resolution
|
|
403
|
+
* @param chain - Chain identifier
|
|
404
|
+
* @param multisigAddress - Multisig address to monitor
|
|
405
|
+
* @param signers - List of signer addresses for the multisig
|
|
406
|
+
* @param threshold - Number of approval required
|
|
407
|
+
* @param callback - Callback function called with updated pending extrinsics
|
|
408
|
+
* @returns Function to unsubscribe from the subscription
|
|
409
|
+
*/
|
|
410
|
+
subscribePendingMultisigTxs(chain, multisigAddress, signers, threshold, callback) {
|
|
411
|
+
const unsubPromise = this.subscribePendingMultisigTxsPromise(chain, multisigAddress, signers, threshold, callback);
|
|
412
|
+
return () => {
|
|
413
|
+
unsubPromise.then(unsub => {
|
|
414
|
+
unsub === null || unsub === void 0 ? void 0 : unsub();
|
|
415
|
+
}).catch(e => multisigServiceLogger.error('Error during unsubscribe in subscribePendingMultisigTxs', e));
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Unsubscribes from all active multisig extrinsic subscriptions
|
|
421
|
+
*/
|
|
422
|
+
runUnsubscribePendingMultisigTxs() {
|
|
423
|
+
this.unsubscribes && this.unsubscribes();
|
|
424
|
+
this.unsubscribes = undefined;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Updates the multisig extrinsic map for a specific chain and multisig address
|
|
429
|
+
* Removes old extrinsics and adds new ones, then notifies all subscribers
|
|
430
|
+
* Creates notifications for new pending transactions that require approval
|
|
431
|
+
* @param multisigAddress - Multisig address
|
|
432
|
+
* @param chain - Chain identifier
|
|
433
|
+
* @param rawPendingTxs - Array of raw pending multisig extrinsics to update
|
|
434
|
+
*/
|
|
435
|
+
updatePendingMultisigTxSubjectByChain(multisigAddress, chain, rawPendingTxs) {
|
|
436
|
+
const allAddresses = this.keyringService.context.getAllAddresses();
|
|
437
|
+
const currentMap = this.getPendingMultisigTxMap();
|
|
438
|
+
const excludedPrefix = `${chain}___${multisigAddress}___`;
|
|
439
|
+
const filteredMap = {};
|
|
440
|
+
|
|
441
|
+
// 1. Clean old extrinsics of multisigAddress and chain
|
|
442
|
+
for (const [key, value] of Object.entries(currentMap)) {
|
|
443
|
+
if (key.startsWith(excludedPrefix)) {
|
|
444
|
+
this.notifiedTxKeys.delete(key);
|
|
445
|
+
} else {
|
|
446
|
+
filteredMap[key] = value;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
const newTxMap = {};
|
|
450
|
+
const newNotifiedTxs = [];
|
|
451
|
+
|
|
452
|
+
// 2. Create new extrinsics of multisigAddress and chain
|
|
453
|
+
for (const rawTx of rawPendingTxs) {
|
|
454
|
+
const extrinsicHash = rawTx.extrinsicHash;
|
|
455
|
+
const signerAddresses = rawTx.signerAddresses;
|
|
456
|
+
if (!extrinsicHash || !signerAddresses || signerAddresses.length === 0) {
|
|
457
|
+
multisigServiceLogger.warn('Skipping multisig extrinsic due to missing required fields: extrinsicHash or signerAddresses');
|
|
458
|
+
continue;
|
|
459
|
+
}
|
|
460
|
+
for (const signerAddress of signerAddresses) {
|
|
461
|
+
// Do not create pending tx for account not in wallet
|
|
462
|
+
if (!allAddresses.includes((0, _utils2.reformatAddress)(signerAddress))) {
|
|
463
|
+
continue;
|
|
464
|
+
}
|
|
465
|
+
const reformatSignerAddress = (0, _utils2._reformatAddressWithChain)(signerAddress, this.chainService.getChainInfoByKey(chain));
|
|
466
|
+
const key = (0, _utils.genPendingMultisigTxKey)(chain, multisigAddress, reformatSignerAddress, extrinsicHash);
|
|
467
|
+
const pendingTx = {
|
|
468
|
+
...rawTx,
|
|
469
|
+
currentSigner: reformatSignerAddress,
|
|
470
|
+
id: key
|
|
471
|
+
};
|
|
472
|
+
newTxMap[key] = pendingTx;
|
|
473
|
+
|
|
474
|
+
// Track new transactions that need notification
|
|
475
|
+
// Only notify if this is a new transaction (not already notified)
|
|
476
|
+
if (!this.notifiedTxKeys.has(key) && !currentMap[key]) {
|
|
477
|
+
// skip notified for account has already approved
|
|
478
|
+
if (pendingTx.approvals.includes(signerAddress)) {
|
|
479
|
+
continue;
|
|
480
|
+
}
|
|
481
|
+
newNotifiedTxs.push(pendingTx);
|
|
482
|
+
this.notifiedTxKeys.add(key);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// 3. Replace the extrinsics of multisigAddress and chain
|
|
488
|
+
// Find approved transactions to clear notifications
|
|
489
|
+
this.clearMultisigApprovalNotifications(newTxMap, multisigAddress, chain).catch(e => multisigServiceLogger.error('Failed to clear multisig approval notifications:', e));
|
|
490
|
+
this.pendingMultisigTxSubject.next({
|
|
491
|
+
...filteredMap,
|
|
492
|
+
...newTxMap
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
// 4. Create notifications for new pending transactions
|
|
496
|
+
if (newNotifiedTxs.length > 0) {
|
|
497
|
+
this.createMultisigApprovalNotifications(newNotifiedTxs).catch(error => {
|
|
498
|
+
multisigServiceLogger.error('Failed to create multisig approval notifications:', error);
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
/**
|
|
504
|
+
* Clears notifications for approved pending multisig transactions
|
|
505
|
+
* @private
|
|
506
|
+
* @param newTxMap - Map of current pending multisig transactions
|
|
507
|
+
* @param multisigAddress - Multisig address
|
|
508
|
+
* @param chain - ChainSlug of the multisig transactions
|
|
509
|
+
*/
|
|
510
|
+
async clearMultisigApprovalNotifications(newTxMap, multisigAddress, chain) {
|
|
511
|
+
const currentNotifications = await this.inappNotificationService.fetchNotificationsByParams({
|
|
512
|
+
notificationTab: _interfaces.NotificationTab.MULTISIG,
|
|
513
|
+
proxyId: _constants.ALL_ACCOUNT_KEY,
|
|
514
|
+
metadata: {
|
|
515
|
+
multisigAddress,
|
|
516
|
+
chain
|
|
517
|
+
}
|
|
518
|
+
});
|
|
519
|
+
const unapprovedPendingTxs = Object.values(newTxMap).reduce((set, tx) => {
|
|
520
|
+
if (!tx.approvals.find(address => (0, _utils2.isSameAddress)(tx.currentSigner, address))) {
|
|
521
|
+
set.add(tx.id);
|
|
522
|
+
}
|
|
523
|
+
return set;
|
|
524
|
+
}, new Set());
|
|
525
|
+
const notificationsIdsToDelete = currentNotifications.reduce((ids, notification) => {
|
|
526
|
+
const metadata = notification.metadata.multisigKey;
|
|
527
|
+
if (!unapprovedPendingTxs.has(metadata)) {
|
|
528
|
+
ids.push(notification.id);
|
|
529
|
+
}
|
|
530
|
+
return ids;
|
|
531
|
+
}, []);
|
|
532
|
+
if (notificationsIdsToDelete.length > 0) {
|
|
533
|
+
await this.inappNotificationService.cleanUpNotificationByIds(notificationsIdsToDelete);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* Creates notifications for pending multisig transactions that require approval
|
|
539
|
+
* @param pendingTxs - Array of pending multisig transactions that need approval
|
|
540
|
+
*/
|
|
541
|
+
async createMultisigApprovalNotifications(pendingTxs) {
|
|
542
|
+
const notifications = pendingTxs.map(tx => {
|
|
543
|
+
const actionType = _interfaces.NotificationActionType.MULTISIG_APPROVAL;
|
|
544
|
+
const timestamp = Date.now();
|
|
545
|
+
const multisigKey = (0, _utils.genPendingMultisigTxKey)(tx.chain, tx.multisigAddress, tx.currentSigner, tx.extrinsicHash);
|
|
546
|
+
const notificationId = `${actionType}___${multisigKey}___${timestamp}`;
|
|
547
|
+
return {
|
|
548
|
+
id: notificationId,
|
|
549
|
+
address: (0, _utils2.reformatAddress)(tx.currentSigner),
|
|
550
|
+
// save notification to default address because it's managed by account
|
|
551
|
+
title: _consts.NotificationTitleMap[actionType],
|
|
552
|
+
description: _consts.NotificationDescriptionMap[actionType](),
|
|
553
|
+
time: timestamp,
|
|
554
|
+
extrinsicType: _KoniTypes.ExtrinsicType.MULTISIG_APPROVE_TX,
|
|
555
|
+
isRead: false,
|
|
556
|
+
actionType,
|
|
557
|
+
metadata: {
|
|
558
|
+
multisigKey,
|
|
559
|
+
chain: tx.chain,
|
|
560
|
+
multisigAddress: tx.multisigAddress,
|
|
561
|
+
extrinsicHash: tx.extrinsicHash,
|
|
562
|
+
callHash: tx.callHash,
|
|
563
|
+
blockHeight: tx.blockHeight,
|
|
564
|
+
extrinsicIndex: tx.extrinsicIndex,
|
|
565
|
+
currentSigner: tx.currentSigner,
|
|
566
|
+
approvals: tx.approvals,
|
|
567
|
+
multisigTxType: tx.multisigTxType
|
|
568
|
+
}
|
|
569
|
+
};
|
|
570
|
+
});
|
|
571
|
+
multisigServiceLogger.debug('notifications', notifications);
|
|
572
|
+
|
|
573
|
+
// Group notifications by address to batch write
|
|
574
|
+
const notificationsByAddress = {};
|
|
575
|
+
for (const notification of notifications) {
|
|
576
|
+
const address = notification.address;
|
|
577
|
+
if (!notificationsByAddress[address]) {
|
|
578
|
+
notificationsByAddress[address] = [];
|
|
579
|
+
}
|
|
580
|
+
notificationsByAddress[address] = [...notificationsByAddress[address], notification];
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
// Write notifications for each address
|
|
584
|
+
for (const [address, addressNotifications] of Object.entries(notificationsByAddress)) {
|
|
585
|
+
if (!address || !addressNotifications || !addressNotifications.length) {
|
|
586
|
+
continue;
|
|
587
|
+
}
|
|
588
|
+
await this.inappNotificationService.validateAndWriteNotificationsToDB(addressNotifications, address);
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
/**
|
|
593
|
+
* Subscribes to changes in the pending multisig extrinsic map
|
|
594
|
+
* @returns BehaviorSubject that emits updates when the extrinsic map changes
|
|
595
|
+
*/
|
|
596
|
+
subscribePendingMultisigTxMap() {
|
|
597
|
+
return this.pendingMultisigTxSubject;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
/**
|
|
601
|
+
* Gets a snapshot of the current pending multisig extrinsic map
|
|
602
|
+
* @returns Copy of the current pending multisig extrinsic map
|
|
603
|
+
*/
|
|
604
|
+
getPendingMultisigTxMap() {
|
|
605
|
+
return {
|
|
606
|
+
...this.pendingMultisigTxSubject.getValue()
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
/**
|
|
611
|
+
* Gets pending extrinsics for a specific multisig address
|
|
612
|
+
* @param request - Request object containing the multisig address
|
|
613
|
+
* @returns Array of pending multisig extrinsics matching the criteria
|
|
614
|
+
*/
|
|
615
|
+
getPendingTxsForMultisigAddress(request) {
|
|
616
|
+
const {
|
|
617
|
+
chain,
|
|
618
|
+
multisigAddress
|
|
619
|
+
} = request;
|
|
620
|
+
const currentMap = this.getPendingMultisigTxMap();
|
|
621
|
+
if (chain) {
|
|
622
|
+
return Object.values(currentMap).filter(tx => (0, _utils2.isSameAddress)(tx.multisigAddress, multisigAddress) && tx.chain === chain);
|
|
623
|
+
}
|
|
624
|
+
return Object.values(currentMap).filter(tx => (0, _utils2.isSameAddress)(tx.multisigAddress, multisigAddress));
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
exports.MultisigService = MultisigService;
|