@subwallet/extension-base 1.3.72-0 → 1.3.74-0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/background/KoniTypes.d.ts +42 -5
  2. package/background/KoniTypes.js +14 -1
  3. package/cjs/background/KoniTypes.js +16 -2
  4. package/cjs/core/logic-validation/transfer.js +35 -57
  5. package/cjs/koni/background/handlers/Extension.js +599 -146
  6. package/cjs/koni/background/handlers/State.js +5 -6
  7. package/cjs/koni/background/handlers/Tabs.js +3 -2
  8. package/cjs/koni/background/subscription.js +2 -22
  9. package/cjs/packageInfo.js +1 -1
  10. package/cjs/services/balance-service/helpers/subscribe/substrate/index.js +0 -2
  11. package/cjs/services/chain-service/handler/SubstrateApi.js +6 -1
  12. package/cjs/services/chain-service/index.js +1 -0
  13. package/cjs/services/chain-service/utils/index.js +4 -0
  14. package/cjs/services/earning-service/handlers/native-staking/para-chain.js +17 -2
  15. package/cjs/services/event-service/index.js +1 -0
  16. package/cjs/services/fee-service/utils/index.js +4 -4
  17. package/cjs/services/inapp-notification-service/consts.js +4 -2
  18. package/cjs/services/inapp-notification-service/index.js +51 -6
  19. package/cjs/services/inapp-notification-service/interfaces.js +2 -0
  20. package/cjs/services/inapp-notification-service/utils/common.js +4 -0
  21. package/cjs/services/keyring-service/context/account-context.js +44 -0
  22. package/cjs/services/keyring-service/context/handlers/Multisig.js +186 -0
  23. package/cjs/services/keyring-service/context/state.js +12 -0
  24. package/cjs/services/multisig-service/index.js +627 -0
  25. package/cjs/services/multisig-service/utils.js +242 -0
  26. package/cjs/services/request-service/handler/SubstrateRequestHandler.js +25 -0
  27. package/cjs/services/request-service/index.js +5 -1
  28. package/cjs/services/storage-service/DatabaseService.js +5 -2
  29. package/cjs/services/storage-service/db-stores/InappNotification.js +20 -2
  30. package/cjs/services/substrate-proxy-service/index.js +22 -7
  31. package/cjs/services/transaction-service/helpers/index.js +8 -0
  32. package/cjs/services/transaction-service/index.js +348 -147
  33. package/cjs/services/transaction-service/types.js +18 -1
  34. package/cjs/types/account/info/keyring.js +5 -0
  35. package/cjs/types/account/info/proxy.js +1 -0
  36. package/cjs/types/multisig/index.js +14 -0
  37. package/cjs/types/transaction/error.js +9 -2
  38. package/cjs/utils/account/transform.js +28 -4
  39. package/cjs/utils/logger/Logger.js +294 -0
  40. package/cjs/utils/logger/index.js +42 -0
  41. package/cjs/utils/logger/types.js +1 -0
  42. package/core/logic-validation/transfer.d.ts +2 -2
  43. package/core/logic-validation/transfer.js +10 -32
  44. package/koni/background/handlers/Extension.d.ts +7 -0
  45. package/koni/background/handlers/Extension.js +498 -45
  46. package/koni/background/handlers/State.d.ts +2 -1
  47. package/koni/background/handlers/State.js +5 -6
  48. package/koni/background/handlers/Tabs.js +3 -2
  49. package/koni/background/subscription.js +2 -22
  50. package/package.json +43 -7
  51. package/packageInfo.js +1 -1
  52. package/services/balance-service/helpers/subscribe/substrate/index.js +0 -2
  53. package/services/chain-service/handler/SubstrateApi.js +7 -2
  54. package/services/chain-service/index.js +1 -0
  55. package/services/chain-service/types.d.ts +1 -1
  56. package/services/chain-service/utils/index.js +4 -0
  57. package/services/earning-service/handlers/native-staking/para-chain.js +17 -2
  58. package/services/event-service/index.d.ts +1 -0
  59. package/services/event-service/index.js +1 -0
  60. package/services/event-service/types.d.ts +1 -0
  61. package/services/fee-service/utils/index.js +4 -4
  62. package/services/inapp-notification-service/consts.d.ts +3 -1
  63. package/services/inapp-notification-service/consts.js +5 -3
  64. package/services/inapp-notification-service/index.d.ts +3 -2
  65. package/services/inapp-notification-service/index.js +51 -6
  66. package/services/inapp-notification-service/interfaces.d.ts +18 -2
  67. package/services/inapp-notification-service/interfaces.js +2 -0
  68. package/services/inapp-notification-service/utils/common.d.ts +1 -0
  69. package/services/inapp-notification-service/utils/common.js +3 -0
  70. package/services/keyring-service/context/account-context.d.ts +9 -1
  71. package/services/keyring-service/context/account-context.js +44 -0
  72. package/services/keyring-service/context/handlers/Multisig.d.ts +18 -0
  73. package/services/keyring-service/context/handlers/Multisig.js +180 -0
  74. package/services/keyring-service/context/state.d.ts +2 -0
  75. package/services/keyring-service/context/state.js +12 -0
  76. package/services/multisig-service/index.d.ts +245 -0
  77. package/services/multisig-service/index.js +620 -0
  78. package/services/multisig-service/utils.d.ts +95 -0
  79. package/services/multisig-service/utils.js +227 -0
  80. package/services/request-service/handler/SubstrateRequestHandler.d.ts +1 -0
  81. package/services/request-service/handler/SubstrateRequestHandler.js +25 -0
  82. package/services/request-service/index.d.ts +2 -1
  83. package/services/request-service/index.js +5 -1
  84. package/services/storage-service/DatabaseService.d.ts +3 -2
  85. package/services/storage-service/DatabaseService.js +5 -2
  86. package/services/storage-service/db-stores/InappNotification.d.ts +3 -2
  87. package/services/storage-service/db-stores/InappNotification.js +20 -2
  88. package/services/substrate-proxy-service/index.d.ts +4 -1
  89. package/services/substrate-proxy-service/index.js +22 -8
  90. package/services/transaction-service/helpers/index.js +8 -0
  91. package/services/transaction-service/index.d.ts +31 -0
  92. package/services/transaction-service/index.js +270 -69
  93. package/services/transaction-service/types.d.ts +28 -3
  94. package/services/transaction-service/types.js +12 -1
  95. package/types/account/info/keyring.d.ts +14 -1
  96. package/types/account/info/keyring.js +6 -0
  97. package/types/account/info/proxy.d.ts +1 -0
  98. package/types/account/info/proxy.js +1 -0
  99. package/types/multisig/index.d.ts +76 -0
  100. package/types/multisig/index.js +8 -0
  101. package/types/notification/index.d.ts +8 -0
  102. package/types/substrateProxyAccount/index.d.ts +26 -1
  103. package/types/transaction/error.d.ts +6 -1
  104. package/types/transaction/error.js +7 -1
  105. package/types/transaction/request.d.ts +0 -1
  106. package/types/yield/info/pallet.d.ts +1 -1
  107. package/utils/account/transform.js +28 -4
  108. package/utils/logger/Logger.d.ts +31 -0
  109. package/utils/logger/Logger.js +267 -0
  110. package/utils/logger/index.d.ts +15 -0
  111. package/utils/logger/index.js +29 -0
  112. package/utils/logger/types.d.ts +23 -0
  113. 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;