@metamask/accounts-controller 18.1.1 → 18.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. package/CHANGELOG.md +40 -1
  2. package/dist/AccountsController.cjs +608 -0
  3. package/dist/AccountsController.cjs.map +1 -0
  4. package/dist/{types/AccountsController.d.ts → AccountsController.d.cts} +13 -10
  5. package/dist/AccountsController.d.cts.map +1 -0
  6. package/dist/AccountsController.d.mts +215 -0
  7. package/dist/AccountsController.d.mts.map +1 -0
  8. package/dist/AccountsController.mjs +604 -9
  9. package/dist/AccountsController.mjs.map +1 -1
  10. package/dist/index.cjs +9 -0
  11. package/dist/index.cjs.map +1 -0
  12. package/dist/{types/index.d.ts → index.d.cts} +4 -4
  13. package/dist/index.d.cts.map +1 -0
  14. package/dist/index.d.mts +4 -0
  15. package/dist/index.d.mts.map +1 -0
  16. package/dist/index.mjs +2 -13
  17. package/dist/index.mjs.map +1 -1
  18. package/dist/tests/mocks.cjs +49 -0
  19. package/dist/tests/mocks.cjs.map +1 -0
  20. package/dist/{types/tests/mocks.d.ts → tests/mocks.d.cts} +3 -3
  21. package/dist/tests/mocks.d.cts.map +1 -0
  22. package/dist/tests/mocks.d.mts +17 -0
  23. package/dist/tests/mocks.d.mts.map +1 -0
  24. package/dist/tests/mocks.mjs +41 -60
  25. package/dist/tests/mocks.mjs.map +1 -1
  26. package/dist/utils.cjs +80 -0
  27. package/dist/utils.cjs.map +1 -0
  28. package/dist/{types/utils.d.ts → utils.d.cts} +3 -3
  29. package/dist/utils.d.cts.map +1 -0
  30. package/dist/utils.d.mts +28 -0
  31. package/dist/utils.d.mts.map +1 -0
  32. package/dist/utils.mjs +73 -13
  33. package/dist/utils.mjs.map +1 -1
  34. package/package.json +19 -14
  35. package/dist/AccountsController.js +0 -11
  36. package/dist/AccountsController.js.map +0 -1
  37. package/dist/chunk-5MTWAWAS.js +0 -753
  38. package/dist/chunk-5MTWAWAS.js.map +0 -1
  39. package/dist/chunk-BYPP7G2N.js +0 -56
  40. package/dist/chunk-BYPP7G2N.js.map +0 -1
  41. package/dist/chunk-UJIPPGP6.js +0 -19
  42. package/dist/chunk-UJIPPGP6.js.map +0 -1
  43. package/dist/chunk-Y2QVUNIA.mjs +0 -56
  44. package/dist/chunk-Y2QVUNIA.mjs.map +0 -1
  45. package/dist/chunk-ZNSHBDHA.mjs +0 -19
  46. package/dist/chunk-ZNSHBDHA.mjs.map +0 -1
  47. package/dist/chunk-ZPUB3DMH.mjs +0 -753
  48. package/dist/chunk-ZPUB3DMH.mjs.map +0 -1
  49. package/dist/index.js +0 -14
  50. package/dist/index.js.map +0 -1
  51. package/dist/tests/mocks.js +0 -65
  52. package/dist/tests/mocks.js.map +0 -1
  53. package/dist/tsconfig.build.tsbuildinfo +0 -1
  54. package/dist/types/AccountsController.d.ts.map +0 -1
  55. package/dist/types/index.d.ts.map +0 -1
  56. package/dist/types/tests/mocks.d.ts.map +0 -1
  57. package/dist/types/utils.d.ts.map +0 -1
  58. package/dist/utils.js +0 -14
  59. package/dist/utils.js.map +0 -1
@@ -1,11 +1,606 @@
1
- import {
2
- AccountsController,
3
- EMPTY_ACCOUNT
4
- } from "./chunk-ZPUB3DMH.mjs";
5
- import "./chunk-Y2QVUNIA.mjs";
6
- import "./chunk-ZNSHBDHA.mjs";
7
- export {
8
- AccountsController,
9
- EMPTY_ACCOUNT
1
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
2
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
3
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
4
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
5
+ };
6
+ var _AccountsController_instances, _AccountsController_generateInternalAccountForNonSnapAccount, _AccountsController_listSnapAccounts, _AccountsController_listNormalAccounts, _AccountsController_handleOnKeyringStateChange, _AccountsController_handleOnSnapStateChange, _AccountsController_getAccountsByKeyringType, _AccountsController_getLastSelectedAccount, _AccountsController_isAccountCompatibleWithChain, _AccountsController_getLastSelectedIndex, _AccountsController_handleNewAccountAdded, _AccountsController_publishAccountChangeEvent, _AccountsController_handleAccountRemoved, _AccountsController_populateExistingMetadata, _AccountsController_registerMessageHandlers;
7
+ import { BaseController } from "@metamask/base-controller";
8
+ import $metamaskethsnapkeyring from "@metamask/eth-snap-keyring";
9
+ const { SnapKeyring } = $metamaskethsnapkeyring;
10
+ import $metamaskkeyringapi from "@metamask/keyring-api";
11
+ const { EthAccountType, EthMethod, isEvmAccountType } = $metamaskkeyringapi;
12
+ import { KeyringTypes } from "@metamask/keyring-controller";
13
+ import { isCaipChainId, parseCaipChainId } from "@metamask/utils";
14
+ import { getUUIDFromAddressOfNormalAccount, isNormalKeyringType, keyringTypeToName } from "./utils.mjs";
15
+ const controllerName = 'AccountsController';
16
+ const accountsControllerMetadata = {
17
+ internalAccounts: {
18
+ persist: true,
19
+ anonymous: false,
20
+ },
21
+ };
22
+ const defaultState = {
23
+ internalAccounts: {
24
+ accounts: {},
25
+ selectedAccount: '',
26
+ },
27
+ };
28
+ export const EMPTY_ACCOUNT = {
29
+ id: '',
30
+ address: '',
31
+ options: {},
32
+ methods: [],
33
+ type: EthAccountType.Eoa,
34
+ metadata: {
35
+ name: '',
36
+ keyring: {
37
+ type: '',
38
+ },
39
+ importTime: 0,
40
+ },
41
+ };
42
+ /**
43
+ * Controller that manages internal accounts.
44
+ * The accounts controller is responsible for creating and managing internal accounts.
45
+ * It also provides convenience methods for accessing and updating the internal accounts.
46
+ * The accounts controller also listens for keyring state changes and updates the internal accounts accordingly.
47
+ * The accounts controller also listens for snap state changes and updates the internal accounts accordingly.
48
+ *
49
+ */
50
+ export class AccountsController extends BaseController {
51
+ /**
52
+ * Constructor for AccountsController.
53
+ *
54
+ * @param options - The controller options.
55
+ * @param options.messenger - The messenger object.
56
+ * @param options.state - Initial state to set on this controller
57
+ */
58
+ constructor({ messenger, state, }) {
59
+ super({
60
+ messenger,
61
+ name: controllerName,
62
+ metadata: accountsControllerMetadata,
63
+ state: {
64
+ ...defaultState,
65
+ ...state,
66
+ },
67
+ });
68
+ _AccountsController_instances.add(this);
69
+ this.messagingSystem.subscribe('SnapController:stateChange', (snapStateState) => __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_handleOnSnapStateChange).call(this, snapStateState));
70
+ this.messagingSystem.subscribe('KeyringController:stateChange', (keyringState) => __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_handleOnKeyringStateChange).call(this, keyringState));
71
+ __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_registerMessageHandlers).call(this);
72
+ }
73
+ /**
74
+ * Returns the internal account object for the given account ID, if it exists.
75
+ *
76
+ * @param accountId - The ID of the account to retrieve.
77
+ * @returns The internal account object, or undefined if the account does not exist.
78
+ */
79
+ getAccount(accountId) {
80
+ return this.state.internalAccounts.accounts[accountId];
81
+ }
82
+ /**
83
+ * Returns an array of all evm internal accounts.
84
+ *
85
+ * @returns An array of InternalAccount objects.
86
+ */
87
+ listAccounts() {
88
+ const accounts = Object.values(this.state.internalAccounts.accounts);
89
+ return accounts.filter((account) => isEvmAccountType(account.type));
90
+ }
91
+ /**
92
+ * Returns an array of all internal accounts.
93
+ *
94
+ * @param chainId - The chain ID.
95
+ * @returns An array of InternalAccount objects.
96
+ */
97
+ listMultichainAccounts(chainId) {
98
+ const accounts = Object.values(this.state.internalAccounts.accounts);
99
+ if (!chainId) {
100
+ return accounts;
101
+ }
102
+ if (!isCaipChainId(chainId)) {
103
+ throw new Error(`Invalid CAIP-2 chain ID: ${String(chainId)}`);
104
+ }
105
+ return accounts.filter((account) => __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_isAccountCompatibleWithChain).call(this, account, chainId));
106
+ }
107
+ /**
108
+ * Returns the internal account object for the given account ID.
109
+ *
110
+ * @param accountId - The ID of the account to retrieve.
111
+ * @returns The internal account object.
112
+ * @throws An error if the account ID is not found.
113
+ */
114
+ getAccountExpect(accountId) {
115
+ const account = this.getAccount(accountId);
116
+ if (account === undefined) {
117
+ throw new Error(`Account Id "${accountId}" not found`);
118
+ }
119
+ return account;
120
+ }
121
+ /**
122
+ * Returns the last selected EVM account.
123
+ *
124
+ * @returns The selected internal account.
125
+ */
126
+ getSelectedAccount() {
127
+ // Edge case where the extension is setup but the srp is not yet created
128
+ // certain ui elements will query the selected address before any accounts are created.
129
+ if (this.state.internalAccounts.selectedAccount === '') {
130
+ return EMPTY_ACCOUNT;
131
+ }
132
+ const selectedAccount = this.getAccountExpect(this.state.internalAccounts.selectedAccount);
133
+ if (isEvmAccountType(selectedAccount.type)) {
134
+ return selectedAccount;
135
+ }
136
+ const accounts = this.listAccounts();
137
+ if (!accounts.length) {
138
+ // ! Should never reach this.
139
+ throw new Error('No EVM accounts');
140
+ }
141
+ // This will never be undefined because we have already checked if accounts.length is > 0
142
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
143
+ return __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_getLastSelectedAccount).call(this, accounts);
144
+ }
145
+ /**
146
+ * __WARNING The return value may be undefined if there isn't an account for that chain id.__
147
+ *
148
+ * Retrieves the last selected account by chain ID.
149
+ *
150
+ * @param chainId - The chain ID to filter the accounts.
151
+ * @returns The last selected account compatible with the specified chain ID or undefined.
152
+ */
153
+ getSelectedMultichainAccount(chainId) {
154
+ // Edge case where the extension is setup but the srp is not yet created
155
+ // certain ui elements will query the selected address before any accounts are created.
156
+ if (this.state.internalAccounts.selectedAccount === '') {
157
+ return EMPTY_ACCOUNT;
158
+ }
159
+ if (!chainId) {
160
+ return this.getAccountExpect(this.state.internalAccounts.selectedAccount);
161
+ }
162
+ if (!isCaipChainId(chainId)) {
163
+ throw new Error(`Invalid CAIP-2 chain ID: ${chainId}`);
164
+ }
165
+ const accounts = Object.values(this.state.internalAccounts.accounts).filter((account) => __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_isAccountCompatibleWithChain).call(this, account, chainId));
166
+ return __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_getLastSelectedAccount).call(this, accounts);
167
+ }
168
+ /**
169
+ * Returns the account with the specified address.
170
+ * ! This method will only return the first account that matches the address
171
+ * @param address - The address of the account to retrieve.
172
+ * @returns The account with the specified address, or undefined if not found.
173
+ */
174
+ getAccountByAddress(address) {
175
+ return this.listMultichainAccounts().find((account) => account.address.toLowerCase() === address.toLowerCase());
176
+ }
177
+ /**
178
+ * Sets the selected account by its ID.
179
+ *
180
+ * @param accountId - The ID of the account to be selected.
181
+ */
182
+ setSelectedAccount(accountId) {
183
+ const account = this.getAccountExpect(accountId);
184
+ this.update((currentState) => {
185
+ currentState.internalAccounts.accounts[account.id].metadata.lastSelected =
186
+ Date.now();
187
+ currentState.internalAccounts.selectedAccount = account.id;
188
+ });
189
+ __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_publishAccountChangeEvent).call(this, account);
190
+ }
191
+ /**
192
+ * Sets the name of the account with the given ID.
193
+ *
194
+ * @param accountId - The ID of the account to set the name for.
195
+ * @param accountName - The new name for the account.
196
+ * @throws An error if an account with the same name already exists.
197
+ */
198
+ setAccountName(accountId, accountName) {
199
+ // This will check for name uniqueness and fire the `accountRenamed` event
200
+ // if the account has been renamed.
201
+ this.updateAccountMetadata(accountId, {
202
+ name: accountName,
203
+ nameLastUpdatedAt: Date.now(),
204
+ });
205
+ }
206
+ /**
207
+ * Updates the metadata of the account with the given ID.
208
+ *
209
+ * @param accountId - The ID of the account for which the metadata will be updated.
210
+ * @param metadata - The new metadata for the account.
211
+ */
212
+ updateAccountMetadata(accountId, metadata) {
213
+ const account = this.getAccountExpect(accountId);
214
+ if (metadata.name &&
215
+ this.listMultichainAccounts().find((internalAccount) => internalAccount.metadata.name === metadata.name &&
216
+ internalAccount.id !== accountId)) {
217
+ throw new Error('Account name already exists');
218
+ }
219
+ this.update((currentState) => {
220
+ const internalAccount = {
221
+ ...account,
222
+ metadata: { ...account.metadata, ...metadata },
223
+ };
224
+ // Do not remove this comment - This error is flaky: Comment out or restore the `ts-expect-error` directive below as needed.
225
+ // See: https://github.com/MetaMask/utils/issues/168
226
+ // // @ts-expect-error Known issue - `Json` causes recursive error in immer `Draft`/`WritableDraft` types
227
+ currentState.internalAccounts.accounts[accountId] = internalAccount;
228
+ if (metadata.name) {
229
+ this.messagingSystem.publish('AccountsController:accountRenamed', internalAccount);
230
+ }
231
+ });
232
+ }
233
+ /**
234
+ * Updates the internal accounts list by retrieving normal and snap accounts,
235
+ * removing duplicates, and updating the metadata of each account.
236
+ *
237
+ * @returns A Promise that resolves when the accounts have been updated.
238
+ */
239
+ async updateAccounts() {
240
+ const snapAccounts = await __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_listSnapAccounts).call(this);
241
+ const normalAccounts = await __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_listNormalAccounts).call(this);
242
+ // keyring type map.
243
+ const keyringTypes = new Map();
244
+ const previousAccounts = this.state.internalAccounts.accounts;
245
+ const accounts = [
246
+ ...normalAccounts,
247
+ ...snapAccounts,
248
+ ].reduce((internalAccountMap, internalAccount) => {
249
+ const keyringTypeName = keyringTypeToName(internalAccount.metadata.keyring.type);
250
+ const keyringAccountIndex = keyringTypes.get(keyringTypeName) ?? 0;
251
+ if (keyringAccountIndex) {
252
+ keyringTypes.set(keyringTypeName, keyringAccountIndex + 1);
253
+ }
254
+ else {
255
+ keyringTypes.set(keyringTypeName, 1);
256
+ }
257
+ const existingAccount = previousAccounts[internalAccount.id];
258
+ internalAccountMap[internalAccount.id] = {
259
+ ...internalAccount,
260
+ metadata: {
261
+ ...internalAccount.metadata,
262
+ name: __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_populateExistingMetadata).call(this, existingAccount?.id, 'name') ??
263
+ `${keyringTypeName} ${keyringAccountIndex + 1}`,
264
+ importTime: __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_populateExistingMetadata).call(this, existingAccount?.id, 'importTime') ??
265
+ Date.now(),
266
+ lastSelected: __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_populateExistingMetadata).call(this, existingAccount?.id, 'lastSelected') ?? 0,
267
+ },
268
+ };
269
+ return internalAccountMap;
270
+ }, {});
271
+ this.update((currentState) => {
272
+ currentState.internalAccounts.accounts = accounts;
273
+ if (!currentState.internalAccounts.accounts[currentState.internalAccounts.selectedAccount]) {
274
+ const lastSelectedAccount = __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_getLastSelectedAccount).call(this, Object.values(accounts));
275
+ if (lastSelectedAccount) {
276
+ currentState.internalAccounts.selectedAccount =
277
+ lastSelectedAccount.id;
278
+ currentState.internalAccounts.accounts[lastSelectedAccount.id].metadata.lastSelected = __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_getLastSelectedIndex).call(this);
279
+ __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_publishAccountChangeEvent).call(this, lastSelectedAccount);
280
+ }
281
+ else {
282
+ // It will be undefined if there are no accounts
283
+ currentState.internalAccounts.selectedAccount = '';
284
+ }
285
+ }
286
+ });
287
+ }
288
+ /**
289
+ * Loads the backup state of the accounts controller.
290
+ *
291
+ * @param backup - The backup state to load.
292
+ */
293
+ loadBackup(backup) {
294
+ if (backup.internalAccounts) {
295
+ this.update((currentState) => {
296
+ currentState.internalAccounts = backup.internalAccounts;
297
+ });
298
+ }
299
+ }
300
+ /**
301
+ * Returns the next account number for a given keyring type.
302
+ * @param keyringType - The type of keyring.
303
+ * @param accounts - Existing accounts to check for the next available account number.
304
+ * @returns An object containing the account prefix and index to use.
305
+ */
306
+ getNextAvailableAccountName(keyringType = KeyringTypes.hd, accounts) {
307
+ const keyringName = keyringTypeToName(keyringType);
308
+ const keyringAccounts = __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_getAccountsByKeyringType).call(this, keyringType, accounts);
309
+ const lastDefaultIndexUsedForKeyringType = keyringAccounts.reduce((maxInternalAccountIndex, internalAccount) => {
310
+ // We **DO NOT USE** `\d+` here to only consider valid "human"
311
+ // number (rounded decimal number)
312
+ const match = new RegExp(`${keyringName} ([0-9]+)$`, 'u').exec(internalAccount.metadata.name);
313
+ if (match) {
314
+ // Quoting `RegExp.exec` documentation:
315
+ // > The returned array has the matched text as the first item, and then one item for
316
+ // > each capturing group of the matched text.
317
+ // So use `match[1]` to get the captured value
318
+ const internalAccountIndex = parseInt(match[1], 10);
319
+ return Math.max(maxInternalAccountIndex, internalAccountIndex);
320
+ }
321
+ return maxInternalAccountIndex;
322
+ }, 0);
323
+ const index = Math.max(keyringAccounts.length + 1,
324
+ // ESLint is confused; this is a number.
325
+ // eslint-disable-next-line @typescript-eslint/restrict-plus-operands
326
+ lastDefaultIndexUsedForKeyringType + 1);
327
+ return `${keyringName} ${index}`;
328
+ }
329
+ }
330
+ _AccountsController_instances = new WeakSet(), _AccountsController_generateInternalAccountForNonSnapAccount = function _AccountsController_generateInternalAccountForNonSnapAccount(address, type) {
331
+ return {
332
+ id: getUUIDFromAddressOfNormalAccount(address),
333
+ address,
334
+ options: {},
335
+ methods: [
336
+ EthMethod.PersonalSign,
337
+ EthMethod.Sign,
338
+ EthMethod.SignTransaction,
339
+ EthMethod.SignTypedDataV1,
340
+ EthMethod.SignTypedDataV3,
341
+ EthMethod.SignTypedDataV4,
342
+ ],
343
+ type: EthAccountType.Eoa,
344
+ metadata: {
345
+ name: '',
346
+ importTime: Date.now(),
347
+ keyring: {
348
+ type,
349
+ },
350
+ },
351
+ };
352
+ }, _AccountsController_listSnapAccounts =
353
+ /**
354
+ * Returns a list of internal accounts created using the SnapKeyring.
355
+ *
356
+ * @returns A promise that resolves to an array of InternalAccount objects.
357
+ */
358
+ async function _AccountsController_listSnapAccounts() {
359
+ const [snapKeyring] = this.messagingSystem.call('KeyringController:getKeyringsByType', SnapKeyring.type);
360
+ // snap keyring is not available until the first account is created in the keyring controller
361
+ if (!snapKeyring) {
362
+ return [];
363
+ }
364
+ const snapAccounts = snapKeyring.listAccounts();
365
+ return snapAccounts;
366
+ }, _AccountsController_listNormalAccounts =
367
+ /**
368
+ * Returns a list of normal accounts.
369
+ * Note: listNormalAccounts is a temporary method until the keyrings all implement the InternalAccount interface.
370
+ * Once all keyrings implement the InternalAccount interface, this method can be removed and getAccounts can be used instead.
371
+ *
372
+ * @returns A Promise that resolves to an array of InternalAccount objects.
373
+ */
374
+ async function _AccountsController_listNormalAccounts() {
375
+ const addresses = await this.messagingSystem.call('KeyringController:getAccounts');
376
+ const internalAccounts = [];
377
+ for (const address of addresses) {
378
+ const keyring = await this.messagingSystem.call('KeyringController:getKeyringForAccount', address);
379
+ const keyringType = keyring.type;
380
+ if (!isNormalKeyringType(keyringType)) {
381
+ // We only consider "normal accounts" here, so keep looping
382
+ continue;
383
+ }
384
+ const id = getUUIDFromAddressOfNormalAccount(address);
385
+ const nameLastUpdatedAt = __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_populateExistingMetadata).call(this, id, 'nameLastUpdatedAt');
386
+ internalAccounts.push({
387
+ id,
388
+ address,
389
+ options: {},
390
+ methods: [
391
+ EthMethod.PersonalSign,
392
+ EthMethod.Sign,
393
+ EthMethod.SignTransaction,
394
+ EthMethod.SignTypedDataV1,
395
+ EthMethod.SignTypedDataV3,
396
+ EthMethod.SignTypedDataV4,
397
+ ],
398
+ type: EthAccountType.Eoa,
399
+ metadata: {
400
+ name: __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_populateExistingMetadata).call(this, id, 'name') ?? '',
401
+ ...(nameLastUpdatedAt && { nameLastUpdatedAt }),
402
+ importTime: __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_populateExistingMetadata).call(this, id, 'importTime') ?? Date.now(),
403
+ lastSelected: __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_populateExistingMetadata).call(this, id, 'lastSelected') ?? 0,
404
+ keyring: {
405
+ type: keyring.type,
406
+ },
407
+ },
408
+ });
409
+ }
410
+ return internalAccounts;
411
+ }, _AccountsController_handleOnKeyringStateChange = function _AccountsController_handleOnKeyringStateChange(keyringState) {
412
+ // check if there are any new accounts added
413
+ // TODO: change when accountAdded event is added to the keyring controller
414
+ // We check for keyrings length to be greater than 0 because the extension client may try execute
415
+ // submit password twice and clear the keyring state.
416
+ // https://github.com/MetaMask/KeyringController/blob/2d73a4deed8d013913f6ef0c9f5c0bb7c614f7d3/src/KeyringController.ts#L910
417
+ if (keyringState.isUnlocked && keyringState.keyrings.length > 0) {
418
+ const updatedNormalKeyringAddresses = [];
419
+ const updatedSnapKeyringAddresses = [];
420
+ for (const keyring of keyringState.keyrings) {
421
+ if (keyring.type === KeyringTypes.snap) {
422
+ updatedSnapKeyringAddresses.push(...keyring.accounts.map((address) => {
423
+ return {
424
+ address,
425
+ type: keyring.type,
426
+ };
427
+ }));
428
+ }
429
+ else {
430
+ updatedNormalKeyringAddresses.push(...keyring.accounts.map((address) => {
431
+ return {
432
+ address,
433
+ type: keyring.type,
434
+ };
435
+ }));
436
+ }
437
+ }
438
+ const { previousNormalInternalAccounts, previousSnapInternalAccounts } = this.listMultichainAccounts().reduce((accumulator, account) => {
439
+ if (account.metadata.keyring.type === KeyringTypes.snap) {
440
+ accumulator.previousSnapInternalAccounts.push(account);
441
+ }
442
+ else {
443
+ accumulator.previousNormalInternalAccounts.push(account);
444
+ }
445
+ return accumulator;
446
+ }, {
447
+ previousNormalInternalAccounts: [],
448
+ previousSnapInternalAccounts: [],
449
+ });
450
+ const addedAccounts = [];
451
+ const deletedAccounts = [];
452
+ // snap account ids are random uuid while normal accounts
453
+ // are determininistic based on the address
454
+ // ^NOTE: This will be removed when normal accounts also implement internal accounts
455
+ // finding all the normal accounts that were added
456
+ for (const account of updatedNormalKeyringAddresses) {
457
+ if (!this.state.internalAccounts.accounts[getUUIDFromAddressOfNormalAccount(account.address)]) {
458
+ addedAccounts.push(account);
459
+ }
460
+ }
461
+ // finding all the snap accounts that were added
462
+ for (const account of updatedSnapKeyringAddresses) {
463
+ if (!previousSnapInternalAccounts.find((internalAccount) => internalAccount.address.toLowerCase() ===
464
+ account.address.toLowerCase())) {
465
+ addedAccounts.push(account);
466
+ }
467
+ }
468
+ // finding all the normal accounts that were deleted
469
+ for (const account of previousNormalInternalAccounts) {
470
+ if (!updatedNormalKeyringAddresses.find(({ address }) => address.toLowerCase() === account.address.toLowerCase())) {
471
+ deletedAccounts.push(account);
472
+ }
473
+ }
474
+ // finding all the snap accounts that were deleted
475
+ for (const account of previousSnapInternalAccounts) {
476
+ if (!updatedSnapKeyringAddresses.find(({ address }) => address.toLowerCase() === account.address.toLowerCase())) {
477
+ deletedAccounts.push(account);
478
+ }
479
+ }
480
+ this.update((currentState) => {
481
+ if (deletedAccounts.length > 0) {
482
+ for (const account of deletedAccounts) {
483
+ currentState.internalAccounts.accounts = __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_handleAccountRemoved).call(this, currentState.internalAccounts.accounts, account.id);
484
+ }
485
+ }
486
+ if (addedAccounts.length > 0) {
487
+ for (const account of addedAccounts) {
488
+ currentState.internalAccounts.accounts =
489
+ __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_handleNewAccountAdded).call(this, currentState.internalAccounts.accounts, account);
490
+ }
491
+ }
492
+ // We don't use list accounts because it is not the updated state yet.
493
+ const existingAccounts = Object.values(currentState.internalAccounts.accounts);
494
+ // handle if the selected account was deleted
495
+ if (!currentState.internalAccounts.accounts[this.state.internalAccounts.selectedAccount]) {
496
+ const lastSelectedAccount = __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_getLastSelectedAccount).call(this, existingAccounts);
497
+ if (lastSelectedAccount) {
498
+ currentState.internalAccounts.selectedAccount =
499
+ lastSelectedAccount.id;
500
+ currentState.internalAccounts.accounts[lastSelectedAccount.id].metadata.lastSelected = __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_getLastSelectedIndex).call(this);
501
+ __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_publishAccountChangeEvent).call(this, lastSelectedAccount);
502
+ }
503
+ else {
504
+ // It will be undefined if there are no accounts
505
+ currentState.internalAccounts.selectedAccount = '';
506
+ }
507
+ }
508
+ });
509
+ }
510
+ }, _AccountsController_handleOnSnapStateChange = function _AccountsController_handleOnSnapStateChange(snapState) {
511
+ // only check if snaps changed in status
512
+ const { snaps } = snapState;
513
+ const accounts = this.listMultichainAccounts().filter((account) => account.metadata.snap);
514
+ this.update((currentState) => {
515
+ accounts.forEach((account) => {
516
+ const currentAccount = currentState.internalAccounts.accounts[account.id];
517
+ if (currentAccount.metadata.snap) {
518
+ const snapId = currentAccount.metadata.snap.id;
519
+ const storedSnap = snaps[snapId];
520
+ if (storedSnap) {
521
+ currentAccount.metadata.snap.enabled =
522
+ storedSnap.enabled && !storedSnap.blocked;
523
+ }
524
+ }
525
+ });
526
+ });
527
+ }, _AccountsController_getAccountsByKeyringType = function _AccountsController_getAccountsByKeyringType(keyringType, accounts) {
528
+ return (accounts ?? this.listMultichainAccounts()).filter((internalAccount) => {
529
+ // We do consider `hd` and `simple` keyrings to be of same type. So we check those 2 types
530
+ // to group those accounts together!
531
+ if (keyringType === KeyringTypes.hd ||
532
+ keyringType === KeyringTypes.simple) {
533
+ return (internalAccount.metadata.keyring.type === KeyringTypes.hd ||
534
+ internalAccount.metadata.keyring.type === KeyringTypes.simple);
535
+ }
536
+ return internalAccount.metadata.keyring.type === keyringType;
537
+ });
538
+ }, _AccountsController_getLastSelectedAccount = function _AccountsController_getLastSelectedAccount(accounts) {
539
+ const [accountToSelect] = accounts.sort((accountA, accountB) => {
540
+ // sort by lastSelected descending
541
+ return ((accountB.metadata.lastSelected ?? 0) -
542
+ (accountA.metadata.lastSelected ?? 0));
543
+ });
544
+ return accountToSelect;
545
+ }, _AccountsController_isAccountCompatibleWithChain = function _AccountsController_isAccountCompatibleWithChain(account, chainId) {
546
+ // TODO: Change this logic to not use account's type
547
+ // Because we currently only use type, we can only use namespace for now.
548
+ return account.type.startsWith(parseCaipChainId(chainId).namespace);
549
+ }, _AccountsController_getLastSelectedIndex = function _AccountsController_getLastSelectedIndex() {
550
+ // NOTE: For now we use the current date, since we know this value
551
+ // will always be higher than any already selected account index.
552
+ return Date.now();
553
+ }, _AccountsController_handleNewAccountAdded = function _AccountsController_handleNewAccountAdded(accountsState, account) {
554
+ let newAccount;
555
+ if (account.type !== KeyringTypes.snap) {
556
+ newAccount = __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_generateInternalAccountForNonSnapAccount).call(this, account.address, account.type);
557
+ }
558
+ else {
559
+ const [snapKeyring] = this.messagingSystem.call('KeyringController:getKeyringsByType', SnapKeyring.type);
560
+ newAccount = snapKeyring.getAccountByAddress(account.address);
561
+ // The snap deleted the account before the keyring controller could add it
562
+ if (!newAccount) {
563
+ return accountsState;
564
+ }
565
+ }
566
+ const isFirstAccount = Object.keys(accountsState).length === 0;
567
+ // Get next account name available for this given keyring
568
+ const accountName = this.getNextAvailableAccountName(newAccount.metadata.keyring.type, Object.values(accountsState));
569
+ const newAccountWithUpdatedMetadata = {
570
+ ...newAccount,
571
+ metadata: {
572
+ ...newAccount.metadata,
573
+ name: accountName,
574
+ importTime: Date.now(),
575
+ lastSelected: isFirstAccount ? __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_getLastSelectedIndex).call(this) : 0,
576
+ },
577
+ };
578
+ accountsState[newAccount.id] = newAccountWithUpdatedMetadata;
579
+ this.messagingSystem.publish('AccountsController:accountAdded', newAccountWithUpdatedMetadata);
580
+ return accountsState;
581
+ }, _AccountsController_publishAccountChangeEvent = function _AccountsController_publishAccountChangeEvent(account) {
582
+ if (isEvmAccountType(account.type)) {
583
+ this.messagingSystem.publish('AccountsController:selectedEvmAccountChange', account);
584
+ }
585
+ this.messagingSystem.publish('AccountsController:selectedAccountChange', account);
586
+ }, _AccountsController_handleAccountRemoved = function _AccountsController_handleAccountRemoved(accountsState, accountId) {
587
+ delete accountsState[accountId];
588
+ this.messagingSystem.publish('AccountsController:accountRemoved', accountId);
589
+ return accountsState;
590
+ }, _AccountsController_populateExistingMetadata = function _AccountsController_populateExistingMetadata(accountId, metadataKey, account) {
591
+ const internalAccount = account ?? this.getAccount(accountId);
592
+ return internalAccount ? internalAccount.metadata[metadataKey] : undefined;
593
+ }, _AccountsController_registerMessageHandlers = function _AccountsController_registerMessageHandlers() {
594
+ this.messagingSystem.registerActionHandler(`${controllerName}:setSelectedAccount`, this.setSelectedAccount.bind(this));
595
+ this.messagingSystem.registerActionHandler(`${controllerName}:listAccounts`, this.listAccounts.bind(this));
596
+ this.messagingSystem.registerActionHandler(`${controllerName}:listMultichainAccounts`, this.listMultichainAccounts.bind(this));
597
+ this.messagingSystem.registerActionHandler(`${controllerName}:setAccountName`, this.setAccountName.bind(this));
598
+ this.messagingSystem.registerActionHandler(`${controllerName}:updateAccounts`, this.updateAccounts.bind(this));
599
+ this.messagingSystem.registerActionHandler(`${controllerName}:getSelectedAccount`, this.getSelectedAccount.bind(this));
600
+ this.messagingSystem.registerActionHandler(`${controllerName}:getSelectedMultichainAccount`, this.getSelectedMultichainAccount.bind(this));
601
+ this.messagingSystem.registerActionHandler(`${controllerName}:getAccountByAddress`, this.getAccountByAddress.bind(this));
602
+ this.messagingSystem.registerActionHandler(`${controllerName}:getNextAvailableAccountName`, this.getNextAvailableAccountName.bind(this));
603
+ this.messagingSystem.registerActionHandler(`AccountsController:getAccount`, this.getAccount.bind(this));
604
+ this.messagingSystem.registerActionHandler(`AccountsController:updateAccountMetadata`, this.updateAccountMetadata.bind(this));
10
605
  };
11
606
  //# sourceMappingURL=AccountsController.mjs.map