@metamask/accounts-controller 18.1.1 → 18.2.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.
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