@metamask/accounts-controller 13.0.0 → 15.0.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 (41) hide show
  1. package/CHANGELOG.md +34 -1
  2. package/dist/AccountsController.js +4 -3
  3. package/dist/AccountsController.mjs +3 -2
  4. package/dist/{chunk-JNCGDLI7.mjs → chunk-3R7MA6KV.mjs} +188 -56
  5. package/dist/chunk-3R7MA6KV.mjs.map +1 -0
  6. package/dist/{chunk-HS62N4JH.js → chunk-7CWWNB3P.js} +204 -72
  7. package/dist/chunk-7CWWNB3P.js.map +1 -0
  8. package/dist/{chunk-KCST55AQ.js → chunk-EIQ5DUI6.js} +10 -17
  9. package/dist/chunk-EIQ5DUI6.js.map +1 -0
  10. package/dist/chunk-IBSI66UQ.js +65 -0
  11. package/dist/chunk-IBSI66UQ.js.map +1 -0
  12. package/dist/chunk-QAHRSXGM.mjs +65 -0
  13. package/dist/chunk-QAHRSXGM.mjs.map +1 -0
  14. package/dist/chunk-UJIPPGP6.js +19 -0
  15. package/dist/chunk-UJIPPGP6.js.map +1 -0
  16. package/dist/{chunk-E7SBT5BV.mjs → chunk-YNILZSBK.mjs} +11 -18
  17. package/dist/chunk-YNILZSBK.mjs.map +1 -0
  18. package/dist/chunk-ZNSHBDHA.mjs +19 -0
  19. package/dist/chunk-ZNSHBDHA.mjs.map +1 -0
  20. package/dist/index.js +8 -3
  21. package/dist/index.mjs +7 -2
  22. package/dist/tests/mocks.js +8 -0
  23. package/dist/tests/mocks.js.map +1 -0
  24. package/dist/tests/mocks.mjs +8 -0
  25. package/dist/tests/mocks.mjs.map +1 -0
  26. package/dist/tsconfig.build.tsbuildinfo +1 -1
  27. package/dist/types/AccountsController.d.ts +39 -4
  28. package/dist/types/AccountsController.d.ts.map +1 -1
  29. package/dist/types/index.d.ts +2 -1
  30. package/dist/types/index.d.ts.map +1 -1
  31. package/dist/types/tests/mocks.d.ts +17 -0
  32. package/dist/types/tests/mocks.d.ts.map +1 -0
  33. package/dist/types/utils.d.ts +19 -0
  34. package/dist/types/utils.d.ts.map +1 -1
  35. package/dist/utils.js +7 -2
  36. package/dist/utils.mjs +6 -1
  37. package/package.json +10 -10
  38. package/dist/chunk-E7SBT5BV.mjs.map +0 -1
  39. package/dist/chunk-HS62N4JH.js.map +0 -1
  40. package/dist/chunk-JNCGDLI7.mjs.map +0 -1
  41. package/dist/chunk-KCST55AQ.js.map +0 -1
@@ -3,13 +3,25 @@
3
3
 
4
4
 
5
5
 
6
- var _chunkKCST55AQjs = require('./chunk-KCST55AQ.js');
6
+ var _chunkEIQ5DUI6js = require('./chunk-EIQ5DUI6.js');
7
+
8
+
9
+
10
+ var _chunkUJIPPGP6js = require('./chunk-UJIPPGP6.js');
7
11
 
8
12
  // src/AccountsController.ts
9
13
  var _basecontroller = require('@metamask/base-controller');
10
14
  var _ethsnapkeyring = require('@metamask/eth-snap-keyring');
15
+
16
+
17
+
18
+
11
19
  var _keyringapi = require('@metamask/keyring-api');
12
20
  var _keyringcontroller = require('@metamask/keyring-controller');
21
+
22
+
23
+
24
+ var _utils = require('@metamask/utils');
13
25
  var controllerName = "AccountsController";
14
26
  var accountsControllerMetadata = {
15
27
  internalAccounts: {
@@ -23,7 +35,7 @@ var defaultState = {
23
35
  selectedAccount: ""
24
36
  }
25
37
  };
26
- var _generateInternalAccountForNonSnapAccount, generateInternalAccountForNonSnapAccount_fn, _listSnapAccounts, listSnapAccounts_fn, _listNormalAccounts, listNormalAccounts_fn, _handleOnKeyringStateChange, handleOnKeyringStateChange_fn, _handleOnSnapStateChange, handleOnSnapStateChange_fn, _getAccountsByKeyringType, getAccountsByKeyringType_fn, _getNextAccountNumber, getNextAccountNumber_fn, _handleNewAccountAdded, handleNewAccountAdded_fn, _handleAccountRemoved, handleAccountRemoved_fn, _registerMessageHandlers, registerMessageHandlers_fn;
38
+ var _generateInternalAccountForNonSnapAccount, generateInternalAccountForNonSnapAccount_fn, _listSnapAccounts, listSnapAccounts_fn, _listNormalAccounts, listNormalAccounts_fn, _handleOnKeyringStateChange, handleOnKeyringStateChange_fn, _handleOnSnapStateChange, handleOnSnapStateChange_fn, _getAccountsByKeyringType, getAccountsByKeyringType_fn, _getLastSelectedAccount, getLastSelectedAccount_fn, _isAccountCompatibleWithChain, isAccountCompatibleWithChain_fn, _handleNewAccountAdded, handleNewAccountAdded_fn, _handleAccountRemoved, handleAccountRemoved_fn, _populateExistingMetadata, populateExistingMetadata_fn, _registerMessageHandlers, registerMessageHandlers_fn;
27
39
  var AccountsController = class extends _basecontroller.BaseController {
28
40
  /**
29
41
  * Constructor for AccountsController.
@@ -51,13 +63,13 @@ var AccountsController = class extends _basecontroller.BaseController {
51
63
  * @param type - The type of the account.
52
64
  * @returns The generated internal account.
53
65
  */
54
- _chunkKCST55AQjs.__privateAdd.call(void 0, this, _generateInternalAccountForNonSnapAccount);
66
+ _chunkUJIPPGP6js.__privateAdd.call(void 0, this, _generateInternalAccountForNonSnapAccount);
55
67
  /**
56
68
  * Returns a list of internal accounts created using the SnapKeyring.
57
69
  *
58
70
  * @returns A promise that resolves to an array of InternalAccount objects.
59
71
  */
60
- _chunkKCST55AQjs.__privateAdd.call(void 0, this, _listSnapAccounts);
72
+ _chunkUJIPPGP6js.__privateAdd.call(void 0, this, _listSnapAccounts);
61
73
  /**
62
74
  * Returns a list of normal accounts.
63
75
  * Note: listNormalAccounts is a temporary method until the keyrings all implement the InternalAccount interface.
@@ -65,57 +77,73 @@ var AccountsController = class extends _basecontroller.BaseController {
65
77
  *
66
78
  * @returns A Promise that resolves to an array of InternalAccount objects.
67
79
  */
68
- _chunkKCST55AQjs.__privateAdd.call(void 0, this, _listNormalAccounts);
80
+ _chunkUJIPPGP6js.__privateAdd.call(void 0, this, _listNormalAccounts);
69
81
  /**
70
82
  * Handles changes in the keyring state, specifically when new accounts are added or removed.
71
83
  *
72
84
  * @param keyringState - The new state of the keyring controller.
73
85
  */
74
- _chunkKCST55AQjs.__privateAdd.call(void 0, this, _handleOnKeyringStateChange);
86
+ _chunkUJIPPGP6js.__privateAdd.call(void 0, this, _handleOnKeyringStateChange);
75
87
  /**
76
88
  * Handles the change in SnapControllerState by updating the metadata of accounts that have a snap enabled.
77
89
  *
78
90
  * @param snapState - The new SnapControllerState.
79
91
  */
80
- _chunkKCST55AQjs.__privateAdd.call(void 0, this, _handleOnSnapStateChange);
92
+ _chunkUJIPPGP6js.__privateAdd.call(void 0, this, _handleOnSnapStateChange);
81
93
  /**
82
94
  * Returns the list of accounts for a given keyring type.
83
95
  * @param keyringType - The type of keyring.
84
96
  * @returns The list of accounts associcated with this keyring type.
85
97
  */
86
- _chunkKCST55AQjs.__privateAdd.call(void 0, this, _getAccountsByKeyringType);
98
+ _chunkUJIPPGP6js.__privateAdd.call(void 0, this, _getAccountsByKeyringType);
87
99
  /**
88
- * Returns the next account number for a given keyring type.
89
- * @param keyringType - The type of keyring.
90
- * @returns An object containing the account prefix and index to use.
100
+ * Returns the last selected account from the given array of accounts.
101
+ *
102
+ * @param accounts - An array of InternalAccount objects.
103
+ * @returns The InternalAccount object that was last selected, or undefined if the array is empty.
91
104
  */
92
- _chunkKCST55AQjs.__privateAdd.call(void 0, this, _getNextAccountNumber);
105
+ _chunkUJIPPGP6js.__privateAdd.call(void 0, this, _getLastSelectedAccount);
106
+ /**
107
+ * Checks if an account is compatible with a given chain namespace.
108
+ * @private
109
+ * @param account - The account to check compatibility for.
110
+ * @param chainId - The CAIP2 to check compatibility with.
111
+ * @returns Returns true if the account is compatible with the chain namespace, otherwise false.
112
+ */
113
+ _chunkUJIPPGP6js.__privateAdd.call(void 0, this, _isAccountCompatibleWithChain);
93
114
  /**
94
115
  * Handles the addition of a new account to the controller.
95
116
  * If the account is not a Snap Keyring account, generates an internal account for it and adds it to the controller.
96
117
  * If the account is a Snap Keyring account, retrieves the account from the keyring and adds it to the controller.
97
118
  * @param account - The address and keyring type object of the new account.
98
119
  */
99
- _chunkKCST55AQjs.__privateAdd.call(void 0, this, _handleNewAccountAdded);
120
+ _chunkUJIPPGP6js.__privateAdd.call(void 0, this, _handleNewAccountAdded);
100
121
  /**
101
122
  * Handles the removal of an account from the internal accounts list.
102
123
  * @param accountId - The ID of the account to be removed.
103
124
  */
104
- _chunkKCST55AQjs.__privateAdd.call(void 0, this, _handleAccountRemoved);
125
+ _chunkUJIPPGP6js.__privateAdd.call(void 0, this, _handleAccountRemoved);
126
+ /**
127
+ * Retrieves the value of a specific metadata key for an existing account.
128
+ * @param accountId - The ID of the account.
129
+ * @param metadataKey - The key of the metadata to retrieve.
130
+ * @returns The value of the specified metadata key, or undefined if the account or metadata key does not exist.
131
+ */
132
+ _chunkUJIPPGP6js.__privateAdd.call(void 0, this, _populateExistingMetadata);
105
133
  /**
106
134
  * Registers message handlers for the AccountsController.
107
135
  * @private
108
136
  */
109
- _chunkKCST55AQjs.__privateAdd.call(void 0, this, _registerMessageHandlers);
137
+ _chunkUJIPPGP6js.__privateAdd.call(void 0, this, _registerMessageHandlers);
110
138
  this.messagingSystem.subscribe(
111
139
  "SnapController:stateChange",
112
- (snapStateState) => _chunkKCST55AQjs.__privateMethod.call(void 0, this, _handleOnSnapStateChange, handleOnSnapStateChange_fn).call(this, snapStateState)
140
+ (snapStateState) => _chunkUJIPPGP6js.__privateMethod.call(void 0, this, _handleOnSnapStateChange, handleOnSnapStateChange_fn).call(this, snapStateState)
113
141
  );
114
142
  this.messagingSystem.subscribe(
115
143
  "KeyringController:stateChange",
116
- (keyringState) => _chunkKCST55AQjs.__privateMethod.call(void 0, this, _handleOnKeyringStateChange, handleOnKeyringStateChange_fn).call(this, keyringState)
144
+ (keyringState) => _chunkUJIPPGP6js.__privateMethod.call(void 0, this, _handleOnKeyringStateChange, handleOnKeyringStateChange_fn).call(this, keyringState)
117
145
  );
118
- _chunkKCST55AQjs.__privateMethod.call(void 0, this, _registerMessageHandlers, registerMessageHandlers_fn).call(this);
146
+ _chunkUJIPPGP6js.__privateMethod.call(void 0, this, _registerMessageHandlers, registerMessageHandlers_fn).call(this);
119
147
  }
120
148
  /**
121
149
  * Returns the internal account object for the given account ID, if it exists.
@@ -127,12 +155,31 @@ var AccountsController = class extends _basecontroller.BaseController {
127
155
  return this.state.internalAccounts.accounts[accountId];
128
156
  }
129
157
  /**
130
- * Returns an array of all internal accounts.
158
+ * Returns an array of all evm internal accounts.
131
159
  *
132
160
  * @returns An array of InternalAccount objects.
133
161
  */
134
162
  listAccounts() {
135
- return Object.values(this.state.internalAccounts.accounts);
163
+ const accounts = Object.values(this.state.internalAccounts.accounts);
164
+ return accounts.filter((account) => _keyringapi.isEvmAccountType.call(void 0, account.type));
165
+ }
166
+ /**
167
+ * Returns an array of all internal accounts.
168
+ *
169
+ * @param chainId - The chain ID.
170
+ * @returns An array of InternalAccount objects.
171
+ */
172
+ listMultichainAccounts(chainId) {
173
+ const accounts = Object.values(this.state.internalAccounts.accounts);
174
+ if (!chainId) {
175
+ return accounts;
176
+ }
177
+ if (!_utils.isCaipChainId.call(void 0, chainId)) {
178
+ throw new Error(`Invalid CAIP-2 chain ID: ${String(chainId)}`);
179
+ }
180
+ return accounts.filter(
181
+ (account) => _chunkUJIPPGP6js.__privateMethod.call(void 0, this, _isAccountCompatibleWithChain, isAccountCompatibleWithChain_fn).call(this, account, chainId)
182
+ );
136
183
  }
137
184
  /**
138
185
  * Returns the internal account object for the given account ID.
@@ -153,7 +200,8 @@ var AccountsController = class extends _basecontroller.BaseController {
153
200
  name: "",
154
201
  keyring: {
155
202
  type: ""
156
- }
203
+ },
204
+ importTime: 0
157
205
  }
158
206
  };
159
207
  }
@@ -164,12 +212,42 @@ var AccountsController = class extends _basecontroller.BaseController {
164
212
  return account;
165
213
  }
166
214
  /**
167
- * Returns the selected internal account.
215
+ * Returns the last selected evm account.
168
216
  *
169
217
  * @returns The selected internal account.
170
218
  */
171
219
  getSelectedAccount() {
172
- return this.getAccountExpect(this.state.internalAccounts.selectedAccount);
220
+ const selectedAccount = this.getAccountExpect(
221
+ this.state.internalAccounts.selectedAccount
222
+ );
223
+ if (_keyringapi.isEvmAccountType.call(void 0, selectedAccount.type)) {
224
+ return selectedAccount;
225
+ }
226
+ const accounts = this.listAccounts();
227
+ if (!accounts.length) {
228
+ throw new Error("No EVM accounts");
229
+ }
230
+ return _chunkUJIPPGP6js.__privateMethod.call(void 0, this, _getLastSelectedAccount, getLastSelectedAccount_fn).call(this, accounts);
231
+ }
232
+ /**
233
+ * __WARNING The return value may be undefined if there isn't an account for that chain id.__
234
+ *
235
+ * Retrieves the last selected account by chain ID.
236
+ *
237
+ * @param chainId - The chain ID to filter the accounts.
238
+ * @returns The last selected account compatible with the specified chain ID or undefined.
239
+ */
240
+ getSelectedMultichainAccount(chainId) {
241
+ if (!chainId) {
242
+ return this.getAccountExpect(this.state.internalAccounts.selectedAccount);
243
+ }
244
+ if (!_utils.isCaipChainId.call(void 0, chainId)) {
245
+ throw new Error(`Invalid CAIP-2 chain ID: ${chainId}`);
246
+ }
247
+ const accounts = Object.values(this.state.internalAccounts.accounts).filter(
248
+ (account) => _chunkUJIPPGP6js.__privateMethod.call(void 0, this, _isAccountCompatibleWithChain, isAccountCompatibleWithChain_fn).call(this, account, chainId)
249
+ );
250
+ return _chunkUJIPPGP6js.__privateMethod.call(void 0, this, _getLastSelectedAccount, getLastSelectedAccount_fn).call(this, accounts);
173
251
  }
174
252
  /**
175
253
  * Returns the account with the specified address.
@@ -193,6 +271,12 @@ var AccountsController = class extends _basecontroller.BaseController {
193
271
  currentState.internalAccounts.accounts[account.id].metadata.lastSelected = Date.now();
194
272
  currentState.internalAccounts.selectedAccount = account.id;
195
273
  });
274
+ if (_keyringapi.isEvmAccountType.call(void 0, account.type)) {
275
+ this.messagingSystem.publish(
276
+ "AccountsController:selectedEvmAccountChange",
277
+ account
278
+ );
279
+ }
196
280
  this.messagingSystem.publish(
197
281
  "AccountsController:selectedAccountChange",
198
282
  account
@@ -217,8 +301,9 @@ var AccountsController = class extends _basecontroller.BaseController {
217
301
  ...account,
218
302
  metadata: { ...account.metadata, name: accountName }
219
303
  };
220
- currentState.internalAccounts.accounts[accountId] = // @ts-expect-error Assigning a complex type `T` to `Draft<T>` causes an excessive type instantiation depth error.
221
- internalAccount;
304
+ const newState = _chunkEIQ5DUI6js.deepCloneDraft.call(void 0, currentState);
305
+ newState.internalAccounts.accounts[accountId] = internalAccount;
306
+ return newState;
222
307
  });
223
308
  }
224
309
  /**
@@ -228,19 +313,15 @@ var AccountsController = class extends _basecontroller.BaseController {
228
313
  * @returns A Promise that resolves when the accounts have been updated.
229
314
  */
230
315
  async updateAccounts() {
231
- const snapAccounts = await _chunkKCST55AQjs.__privateMethod.call(void 0, this, _listSnapAccounts, listSnapAccounts_fn).call(this);
232
- const normalAccounts = (await _chunkKCST55AQjs.__privateMethod.call(void 0, this, _listNormalAccounts, listNormalAccounts_fn).call(this)).filter(
233
- (account) => !snapAccounts.find(
234
- (snapAccount) => snapAccount.address === account.address
235
- )
236
- );
316
+ const snapAccounts = await _chunkUJIPPGP6js.__privateMethod.call(void 0, this, _listSnapAccounts, listSnapAccounts_fn).call(this);
317
+ const normalAccounts = await _chunkUJIPPGP6js.__privateMethod.call(void 0, this, _listNormalAccounts, listNormalAccounts_fn).call(this);
237
318
  const keyringTypes = /* @__PURE__ */ new Map();
238
319
  const previousAccounts = this.state.internalAccounts.accounts;
239
320
  const accounts = [
240
321
  ...normalAccounts,
241
322
  ...snapAccounts
242
323
  ].reduce((internalAccountMap, internalAccount) => {
243
- const keyringTypeName = _chunkKCST55AQjs.keyringTypeToName.call(void 0,
324
+ const keyringTypeName = _chunkEIQ5DUI6js.keyringTypeToName.call(void 0,
244
325
  internalAccount.metadata.keyring.type
245
326
  );
246
327
  const keyringAccountIndex = keyringTypes.get(keyringTypeName) ?? 0;
@@ -254,14 +335,17 @@ var AccountsController = class extends _basecontroller.BaseController {
254
335
  ...internalAccount,
255
336
  metadata: {
256
337
  ...internalAccount.metadata,
257
- name: existingAccount && existingAccount.metadata.name !== "" ? existingAccount.metadata.name : `${keyringTypeName} ${keyringAccountIndex + 1}`,
258
- lastSelected: existingAccount?.metadata?.lastSelected
338
+ name: _chunkUJIPPGP6js.__privateMethod.call(void 0, this, _populateExistingMetadata, populateExistingMetadata_fn).call(this, existingAccount?.id, "name") ?? `${keyringTypeName} ${keyringAccountIndex + 1}`,
339
+ importTime: _chunkUJIPPGP6js.__privateMethod.call(void 0, this, _populateExistingMetadata, populateExistingMetadata_fn).call(this, existingAccount?.id, "importTime") ?? Date.now(),
340
+ lastSelected: _chunkUJIPPGP6js.__privateMethod.call(void 0, this, _populateExistingMetadata, populateExistingMetadata_fn).call(this, existingAccount?.id, "lastSelected") ?? 0
259
341
  }
260
342
  };
261
343
  return internalAccountMap;
262
344
  }, {});
263
345
  this.update((currentState) => {
264
- currentState.internalAccounts.accounts = accounts;
346
+ const newState = _chunkEIQ5DUI6js.deepCloneDraft.call(void 0, currentState);
347
+ newState.internalAccounts.accounts = accounts;
348
+ return newState;
265
349
  });
266
350
  }
267
351
  /**
@@ -272,15 +356,44 @@ var AccountsController = class extends _basecontroller.BaseController {
272
356
  loadBackup(backup) {
273
357
  if (backup.internalAccounts) {
274
358
  this.update((currentState) => {
275
- currentState.internalAccounts = backup.internalAccounts;
359
+ const newState = _chunkEIQ5DUI6js.deepCloneDraft.call(void 0, currentState);
360
+ newState.internalAccounts = backup.internalAccounts;
361
+ return newState;
276
362
  });
277
363
  }
278
364
  }
365
+ /**
366
+ * Returns the next account number for a given keyring type.
367
+ * @param keyringType - The type of keyring.
368
+ * @returns An object containing the account prefix and index to use.
369
+ */
370
+ getNextAvailableAccountName(keyringType = _keyringcontroller.KeyringTypes.hd) {
371
+ const keyringName = _chunkEIQ5DUI6js.keyringTypeToName.call(void 0, keyringType);
372
+ const keyringAccounts = _chunkUJIPPGP6js.__privateMethod.call(void 0, this, _getAccountsByKeyringType, getAccountsByKeyringType_fn).call(this, keyringType);
373
+ const lastDefaultIndexUsedForKeyringType = keyringAccounts.reduce(
374
+ (maxInternalAccountIndex, internalAccount) => {
375
+ const match = new RegExp(`${keyringName} ([0-9]+)$`, "u").exec(
376
+ internalAccount.metadata.name
377
+ );
378
+ if (match) {
379
+ const internalAccountIndex = parseInt(match[1], 10);
380
+ return Math.max(maxInternalAccountIndex, internalAccountIndex);
381
+ }
382
+ return maxInternalAccountIndex;
383
+ },
384
+ 0
385
+ );
386
+ const index = Math.max(
387
+ keyringAccounts.length + 1,
388
+ lastDefaultIndexUsedForKeyringType + 1
389
+ );
390
+ return `${keyringName} ${index}`;
391
+ }
279
392
  };
280
393
  _generateInternalAccountForNonSnapAccount = new WeakSet();
281
394
  generateInternalAccountForNonSnapAccount_fn = function(address, type) {
282
395
  return {
283
- id: _chunkKCST55AQjs.getUUIDFromAddressOfNormalAccount.call(void 0, address),
396
+ id: _chunkEIQ5DUI6js.getUUIDFromAddressOfNormalAccount.call(void 0, address),
284
397
  address,
285
398
  options: {},
286
399
  methods: [
@@ -294,6 +407,7 @@ generateInternalAccountForNonSnapAccount_fn = function(address, type) {
294
407
  type: _keyringapi.EthAccountType.Eoa,
295
408
  metadata: {
296
409
  name: "",
410
+ importTime: Date.now(),
297
411
  keyring: {
298
412
  type
299
413
  }
@@ -323,8 +437,13 @@ listNormalAccounts_fn = async function() {
323
437
  "KeyringController:getKeyringForAccount",
324
438
  address
325
439
  );
440
+ const keyringType = keyring.type;
441
+ if (!_chunkEIQ5DUI6js.isNormalKeyringType.call(void 0, keyringType)) {
442
+ continue;
443
+ }
444
+ const id = _chunkEIQ5DUI6js.getUUIDFromAddressOfNormalAccount.call(void 0, address);
326
445
  internalAccounts.push({
327
- id: _chunkKCST55AQjs.getUUIDFromAddressOfNormalAccount.call(void 0, address),
446
+ id,
328
447
  address,
329
448
  options: {},
330
449
  methods: [
@@ -337,16 +456,16 @@ listNormalAccounts_fn = async function() {
337
456
  ],
338
457
  type: _keyringapi.EthAccountType.Eoa,
339
458
  metadata: {
340
- name: "",
459
+ name: _chunkUJIPPGP6js.__privateMethod.call(void 0, this, _populateExistingMetadata, populateExistingMetadata_fn).call(this, id, "name") ?? "",
460
+ importTime: _chunkUJIPPGP6js.__privateMethod.call(void 0, this, _populateExistingMetadata, populateExistingMetadata_fn).call(this, id, "importTime") ?? Date.now(),
461
+ lastSelected: _chunkUJIPPGP6js.__privateMethod.call(void 0, this, _populateExistingMetadata, populateExistingMetadata_fn).call(this, id, "lastSelected") ?? 0,
341
462
  keyring: {
342
463
  type: keyring.type
343
464
  }
344
465
  }
345
466
  });
346
467
  }
347
- return internalAccounts.filter(
348
- (account) => account.metadata.keyring.type !== _keyringcontroller.KeyringTypes.snap
349
- );
468
+ return internalAccounts;
350
469
  };
351
470
  _handleOnKeyringStateChange = new WeakSet();
352
471
  handleOnKeyringStateChange_fn = function(keyringState) {
@@ -391,7 +510,7 @@ handleOnKeyringStateChange_fn = function(keyringState) {
391
510
  const addedAccounts = [];
392
511
  const deletedAccounts = [];
393
512
  for (const account of updatedNormalKeyringAddresses) {
394
- if (!this.state.internalAccounts.accounts[_chunkKCST55AQjs.getUUIDFromAddressOfNormalAccount.call(void 0, account.address)]) {
513
+ if (!this.state.internalAccounts.accounts[_chunkEIQ5DUI6js.getUUIDFromAddressOfNormalAccount.call(void 0, account.address)]) {
395
514
  addedAccounts.push(account);
396
515
  }
397
516
  }
@@ -418,12 +537,12 @@ handleOnKeyringStateChange_fn = function(keyringState) {
418
537
  }
419
538
  if (deletedAccounts.length > 0) {
420
539
  for (const account of deletedAccounts) {
421
- _chunkKCST55AQjs.__privateMethod.call(void 0, this, _handleAccountRemoved, handleAccountRemoved_fn).call(this, account.id);
540
+ _chunkUJIPPGP6js.__privateMethod.call(void 0, this, _handleAccountRemoved, handleAccountRemoved_fn).call(this, account.id);
422
541
  }
423
542
  }
424
543
  if (addedAccounts.length > 0) {
425
544
  for (const account of addedAccounts) {
426
- _chunkKCST55AQjs.__privateMethod.call(void 0, this, _handleNewAccountAdded, handleNewAccountAdded_fn).call(this, account);
545
+ _chunkUJIPPGP6js.__privateMethod.call(void 0, this, _handleNewAccountAdded, handleNewAccountAdded_fn).call(this, account);
427
546
  }
428
547
  }
429
548
  if (!this.getAccount(this.state.internalAccounts.selectedAccount)) {
@@ -470,34 +589,30 @@ getAccountsByKeyringType_fn = function(keyringType) {
470
589
  return internalAccount.metadata.keyring.type === keyringType;
471
590
  });
472
591
  };
473
- _getNextAccountNumber = new WeakSet();
474
- getNextAccountNumber_fn = function(keyringType) {
475
- const keyringName = _chunkKCST55AQjs.keyringTypeToName.call(void 0, keyringType);
476
- const keyringAccounts = _chunkKCST55AQjs.__privateMethod.call(void 0, this, _getAccountsByKeyringType, getAccountsByKeyringType_fn).call(this, keyringType);
477
- const lastDefaultIndexUsedForKeyringType = keyringAccounts.reduce(
478
- (maxInternalAccountIndex, internalAccount) => {
479
- const match = new RegExp(`${keyringName} ([0-9]+)$`, "u").exec(
480
- internalAccount.metadata.name
481
- );
482
- if (match) {
483
- const internalAccountIndex = parseInt(match[1], 10);
484
- return Math.max(maxInternalAccountIndex, internalAccountIndex);
485
- }
486
- return maxInternalAccountIndex;
487
- },
488
- 0
489
- );
490
- const indexToUse = Math.max(
491
- keyringAccounts.length + 1,
492
- lastDefaultIndexUsedForKeyringType + 1
493
- );
494
- return { accountPrefix: keyringName, indexToUse };
592
+ _getLastSelectedAccount = new WeakSet();
593
+ getLastSelectedAccount_fn = function(accounts) {
594
+ return accounts.reduce((prevAccount, currentAccount) => {
595
+ if (
596
+ // When the account is added, lastSelected will be set
597
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
598
+ currentAccount.metadata.lastSelected > // When the account is added, lastSelected will be set
599
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
600
+ prevAccount.metadata.lastSelected
601
+ ) {
602
+ return currentAccount;
603
+ }
604
+ return prevAccount;
605
+ }, accounts[0]);
606
+ };
607
+ _isAccountCompatibleWithChain = new WeakSet();
608
+ isAccountCompatibleWithChain_fn = function(account, chainId) {
609
+ return account.type.startsWith(_utils.parseCaipChainId.call(void 0, chainId).namespace);
495
610
  };
496
611
  _handleNewAccountAdded = new WeakSet();
497
612
  handleNewAccountAdded_fn = function(account) {
498
613
  let newAccount;
499
614
  if (account.type !== _keyringcontroller.KeyringTypes.snap) {
500
- newAccount = _chunkKCST55AQjs.__privateMethod.call(void 0, this, _generateInternalAccountForNonSnapAccount, generateInternalAccountForNonSnapAccount_fn).call(this, account.address, account.type);
615
+ newAccount = _chunkUJIPPGP6js.__privateMethod.call(void 0, this, _generateInternalAccountForNonSnapAccount, generateInternalAccountForNonSnapAccount_fn).call(this, account.address, account.type);
501
616
  } else {
502
617
  const [snapKeyring] = this.messagingSystem.call(
503
618
  "KeyringController:getKeyringsByType",
@@ -510,17 +625,21 @@ handleNewAccountAdded_fn = function(account) {
510
625
  return;
511
626
  }
512
627
  }
513
- const { accountPrefix, indexToUse } = _chunkKCST55AQjs.__privateMethod.call(void 0, this, _getNextAccountNumber, getNextAccountNumber_fn).call(this, newAccount.metadata.keyring.type);
514
- const accountName = `${accountPrefix} ${indexToUse}`;
628
+ const accountName = this.getNextAvailableAccountName(
629
+ newAccount.metadata.keyring.type
630
+ );
515
631
  this.update((currentState) => {
516
- currentState.internalAccounts.accounts[newAccount.id] = {
632
+ const newState = _chunkEIQ5DUI6js.deepCloneDraft.call(void 0, currentState);
633
+ newState.internalAccounts.accounts[newAccount.id] = {
517
634
  ...newAccount,
518
635
  metadata: {
519
636
  ...newAccount.metadata,
520
637
  name: accountName,
638
+ importTime: Date.now(),
521
639
  lastSelected: Date.now()
522
640
  }
523
641
  };
642
+ return newState;
524
643
  });
525
644
  this.setSelectedAccount(newAccount.id);
526
645
  };
@@ -530,6 +649,11 @@ handleAccountRemoved_fn = function(accountId) {
530
649
  delete currentState.internalAccounts.accounts[accountId];
531
650
  });
532
651
  };
652
+ _populateExistingMetadata = new WeakSet();
653
+ populateExistingMetadata_fn = function(accountId, metadataKey) {
654
+ const internalAccount = this.getAccount(accountId);
655
+ return internalAccount ? internalAccount.metadata[metadataKey] : void 0;
656
+ };
533
657
  _registerMessageHandlers = new WeakSet();
534
658
  registerMessageHandlers_fn = function() {
535
659
  this.messagingSystem.registerActionHandler(
@@ -552,10 +676,18 @@ registerMessageHandlers_fn = function() {
552
676
  `${controllerName}:getSelectedAccount`,
553
677
  this.getSelectedAccount.bind(this)
554
678
  );
679
+ this.messagingSystem.registerActionHandler(
680
+ `${controllerName}:getSelectedMultichainAccount`,
681
+ this.getSelectedMultichainAccount.bind(this)
682
+ );
555
683
  this.messagingSystem.registerActionHandler(
556
684
  `${controllerName}:getAccountByAddress`,
557
685
  this.getAccountByAddress.bind(this)
558
686
  );
687
+ this.messagingSystem.registerActionHandler(
688
+ `${controllerName}:getNextAvailableAccountName`,
689
+ this.getNextAvailableAccountName.bind(this)
690
+ );
559
691
  this.messagingSystem.registerActionHandler(
560
692
  `AccountsController:getAccount`,
561
693
  this.getAccount.bind(this)
@@ -565,4 +697,4 @@ registerMessageHandlers_fn = function() {
565
697
 
566
698
 
567
699
  exports.AccountsController = AccountsController;
568
- //# sourceMappingURL=chunk-HS62N4JH.js.map
700
+ //# sourceMappingURL=chunk-7CWWNB3P.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/AccountsController.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n RestrictedControllerMessenger,\n} from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport { SnapKeyring } from '@metamask/eth-snap-keyring';\nimport type { InternalAccount } from '@metamask/keyring-api';\nimport {\n EthAccountType,\n EthMethod,\n isEvmAccountType,\n} from '@metamask/keyring-api';\nimport { KeyringTypes } from '@metamask/keyring-controller';\nimport type {\n KeyringControllerState,\n KeyringControllerGetKeyringForAccountAction,\n KeyringControllerGetKeyringsByTypeAction,\n KeyringControllerGetAccountsAction,\n KeyringControllerStateChangeEvent,\n} from '@metamask/keyring-controller';\nimport type {\n SnapControllerState,\n SnapStateChange,\n} from '@metamask/snaps-controllers';\nimport type { SnapId } from '@metamask/snaps-sdk';\nimport type { Snap } from '@metamask/snaps-utils';\nimport type { CaipChainId } from '@metamask/utils';\nimport {\n type Keyring,\n type Json,\n isCaipChainId,\n parseCaipChainId,\n} from '@metamask/utils';\nimport type { Draft } from 'immer';\n\nimport {\n deepCloneDraft,\n getUUIDFromAddressOfNormalAccount,\n isNormalKeyringType,\n keyringTypeToName,\n} from './utils';\n\nconst controllerName = 'AccountsController';\n\nexport type AccountsControllerState = {\n internalAccounts: {\n accounts: Record<string, InternalAccount>;\n selectedAccount: string; // id of the selected account\n };\n};\n\nexport type AccountsControllerGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n AccountsControllerState\n>;\n\nexport type AccountsControllerSetSelectedAccountAction = {\n type: `${typeof controllerName}:setSelectedAccount`;\n handler: AccountsController['setSelectedAccount'];\n};\n\nexport type AccountsControllerSetAccountNameAction = {\n type: `${typeof controllerName}:setAccountName`;\n handler: AccountsController['setAccountName'];\n};\n\nexport type AccountsControllerListAccountsAction = {\n type: `${typeof controllerName}:listAccounts`;\n handler: AccountsController['listAccounts'];\n};\n\nexport type AccountsControllerUpdateAccountsAction = {\n type: `${typeof controllerName}:updateAccounts`;\n handler: AccountsController['updateAccounts'];\n};\n\nexport type AccountsControllerGetSelectedAccountAction = {\n type: `${typeof controllerName}:getSelectedAccount`;\n handler: AccountsController['getSelectedAccount'];\n};\n\nexport type AccountsControllerGetSelectedMultichainAccountAction = {\n type: `${typeof controllerName}:getSelectedMultichainAccount`;\n handler: AccountsController['getSelectedMultichainAccount'];\n};\n\nexport type AccountsControllerGetAccountByAddressAction = {\n type: `${typeof controllerName}:getAccountByAddress`;\n handler: AccountsController['getAccountByAddress'];\n};\n\nexport type AccountsControllerGetNextAvailableAccountNameAction = {\n type: `${typeof controllerName}:getNextAvailableAccountName`;\n handler: AccountsController['getNextAvailableAccountName'];\n};\n\nexport type AccountsControllerGetAccountAction = {\n type: `${typeof controllerName}:getAccount`;\n handler: AccountsController['getAccount'];\n};\n\nexport type AllowedActions =\n | KeyringControllerGetKeyringForAccountAction\n | KeyringControllerGetKeyringsByTypeAction\n | KeyringControllerGetAccountsAction;\n\nexport type AccountsControllerActions =\n | AccountsControllerGetStateAction\n | AccountsControllerSetSelectedAccountAction\n | AccountsControllerListAccountsAction\n | AccountsControllerSetAccountNameAction\n | AccountsControllerUpdateAccountsAction\n | AccountsControllerGetAccountByAddressAction\n | AccountsControllerGetSelectedAccountAction\n | AccountsControllerGetNextAvailableAccountNameAction\n | AccountsControllerGetAccountAction\n | AccountsControllerGetSelectedMultichainAccountAction;\n\nexport type AccountsControllerChangeEvent = ControllerStateChangeEvent<\n typeof controllerName,\n AccountsControllerState\n>;\n\nexport type AccountsControllerSelectedAccountChangeEvent = {\n type: `${typeof controllerName}:selectedAccountChange`;\n payload: [InternalAccount];\n};\n\nexport type AccountsControllerSelectedEvmAccountChangeEvent = {\n type: `${typeof controllerName}:selectedEvmAccountChange`;\n payload: [InternalAccount];\n};\n\nexport type AllowedEvents = SnapStateChange | KeyringControllerStateChangeEvent;\n\nexport type AccountsControllerEvents =\n | AccountsControllerChangeEvent\n | AccountsControllerSelectedAccountChangeEvent\n | AccountsControllerSelectedEvmAccountChangeEvent;\n\nexport type AccountsControllerMessenger = RestrictedControllerMessenger<\n typeof controllerName,\n AccountsControllerActions | AllowedActions,\n AccountsControllerEvents | AllowedEvents,\n AllowedActions['type'],\n AllowedEvents['type']\n>;\n\ntype AddressAndKeyringTypeObject = {\n address: string;\n type: string;\n};\n\nconst accountsControllerMetadata = {\n internalAccounts: {\n persist: true,\n anonymous: false,\n },\n};\n\nconst defaultState: AccountsControllerState = {\n internalAccounts: {\n accounts: {},\n selectedAccount: '',\n },\n};\n\n/**\n * Controller that manages internal accounts.\n * The accounts controller is responsible for creating and managing internal accounts.\n * It also provides convenience methods for accessing and updating the internal accounts.\n * The accounts controller also listens for keyring state changes and updates the internal accounts accordingly.\n * The accounts controller also listens for snap state changes and updates the internal accounts accordingly.\n *\n */\nexport class AccountsController extends BaseController<\n typeof controllerName,\n AccountsControllerState,\n AccountsControllerMessenger\n> {\n /**\n * Constructor for AccountsController.\n *\n * @param options - The controller options.\n * @param options.messenger - The messenger object.\n * @param options.state - Initial state to set on this controller\n */\n constructor({\n messenger,\n state,\n }: {\n messenger: AccountsControllerMessenger;\n state: AccountsControllerState;\n }) {\n super({\n messenger,\n name: controllerName,\n metadata: accountsControllerMetadata,\n state: {\n ...defaultState,\n ...state,\n },\n });\n\n this.messagingSystem.subscribe(\n 'SnapController:stateChange',\n (snapStateState) => this.#handleOnSnapStateChange(snapStateState),\n );\n\n this.messagingSystem.subscribe(\n 'KeyringController:stateChange',\n (keyringState) => this.#handleOnKeyringStateChange(keyringState),\n );\n\n this.#registerMessageHandlers();\n }\n\n /**\n * Returns the internal account object for the given account ID, if it exists.\n *\n * @param accountId - The ID of the account to retrieve.\n * @returns The internal account object, or undefined if the account does not exist.\n */\n getAccount(accountId: string): InternalAccount | undefined {\n return this.state.internalAccounts.accounts[accountId];\n }\n\n /**\n * Returns an array of all evm internal accounts.\n *\n * @returns An array of InternalAccount objects.\n */\n listAccounts(): InternalAccount[] {\n const accounts = Object.values(this.state.internalAccounts.accounts);\n return accounts.filter((account) => isEvmAccountType(account.type));\n }\n\n /**\n * Returns an array of all internal accounts.\n *\n * @param chainId - The chain ID.\n * @returns An array of InternalAccount objects.\n */\n listMultichainAccounts(chainId?: CaipChainId): InternalAccount[] {\n const accounts = Object.values(this.state.internalAccounts.accounts);\n if (!chainId) {\n return accounts;\n }\n\n if (!isCaipChainId(chainId)) {\n throw new Error(`Invalid CAIP-2 chain ID: ${String(chainId)}`);\n }\n\n return accounts.filter((account) =>\n this.#isAccountCompatibleWithChain(account, chainId),\n );\n }\n\n /**\n * Returns the internal account object for the given account ID.\n *\n * @param accountId - The ID of the account to retrieve.\n * @returns The internal account object.\n * @throws An error if the account ID is not found.\n */\n getAccountExpect(accountId: string): InternalAccount {\n // Edge case where the extension is setup but the srp is not yet created\n // certain ui elements will query the selected address before any accounts are created.\n if (!accountId) {\n return {\n id: '',\n address: '',\n options: {},\n methods: [],\n type: EthAccountType.Eoa,\n metadata: {\n name: '',\n keyring: {\n type: '',\n },\n importTime: 0,\n },\n };\n }\n\n const account = this.getAccount(accountId);\n if (account === undefined) {\n throw new Error(`Account Id \"${accountId}\" not found`);\n }\n return account;\n }\n\n /**\n * Returns the last selected evm account.\n *\n * @returns The selected internal account.\n */\n getSelectedAccount(): InternalAccount {\n const selectedAccount = this.getAccountExpect(\n this.state.internalAccounts.selectedAccount,\n );\n if (isEvmAccountType(selectedAccount.type)) {\n return selectedAccount;\n }\n\n const accounts = this.listAccounts();\n\n if (!accounts.length) {\n // ! Should never reach this.\n throw new Error('No EVM accounts');\n }\n\n // This will never be undefined because we have already checked if accounts.length is > 0\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n return this.#getLastSelectedAccount(accounts)!;\n }\n\n /**\n * __WARNING The return value may be undefined if there isn't an account for that chain id.__\n *\n * Retrieves the last selected account by chain ID.\n *\n * @param chainId - The chain ID to filter the accounts.\n * @returns The last selected account compatible with the specified chain ID or undefined.\n */\n getSelectedMultichainAccount(\n chainId?: CaipChainId,\n ): InternalAccount | undefined {\n if (!chainId) {\n return this.getAccountExpect(this.state.internalAccounts.selectedAccount);\n }\n\n if (!isCaipChainId(chainId)) {\n throw new Error(`Invalid CAIP-2 chain ID: ${chainId as string}`);\n }\n\n const accounts = Object.values(this.state.internalAccounts.accounts).filter(\n (account) => this.#isAccountCompatibleWithChain(account, chainId),\n );\n\n return this.#getLastSelectedAccount(accounts);\n }\n\n /**\n * Returns the account with the specified address.\n * ! This method will only return the first account that matches the address\n * @param address - The address of the account to retrieve.\n * @returns The account with the specified address, or undefined if not found.\n */\n getAccountByAddress(address: string): InternalAccount | undefined {\n return this.listAccounts().find(\n (account) => account.address.toLowerCase() === address.toLowerCase(),\n );\n }\n\n /**\n * Sets the selected account by its ID.\n *\n * @param accountId - The ID of the account to be selected.\n */\n setSelectedAccount(accountId: string): void {\n const account = this.getAccountExpect(accountId);\n\n this.update((currentState: Draft<AccountsControllerState>) => {\n currentState.internalAccounts.accounts[account.id].metadata.lastSelected =\n Date.now();\n currentState.internalAccounts.selectedAccount = account.id;\n });\n\n if (isEvmAccountType(account.type)) {\n this.messagingSystem.publish(\n 'AccountsController:selectedEvmAccountChange',\n account,\n );\n }\n\n this.messagingSystem.publish(\n 'AccountsController:selectedAccountChange',\n account,\n );\n }\n\n /**\n * Sets the name of the account with the given ID.\n *\n * @param accountId - The ID of the account to set the name for.\n * @param accountName - The new name for the account.\n * @throws An error if an account with the same name already exists.\n */\n setAccountName(accountId: string, accountName: string): void {\n const account = this.getAccountExpect(accountId);\n\n if (\n this.listAccounts().find(\n (internalAccount) =>\n internalAccount.metadata.name === accountName &&\n internalAccount.id !== accountId,\n )\n ) {\n throw new Error('Account name already exists');\n }\n\n this.update((currentState: Draft<AccountsControllerState>) => {\n const internalAccount = {\n ...account,\n metadata: { ...account.metadata, name: accountName },\n };\n // FIXME: deep clone of old state to get around Type instantiation is excessively deep and possibly infinite.\n const newState = deepCloneDraft(currentState);\n\n newState.internalAccounts.accounts[accountId] = internalAccount;\n\n return newState;\n });\n }\n\n /**\n * Updates the internal accounts list by retrieving normal and snap accounts,\n * removing duplicates, and updating the metadata of each account.\n *\n * @returns A Promise that resolves when the accounts have been updated.\n */\n async updateAccounts(): Promise<void> {\n const snapAccounts = await this.#listSnapAccounts();\n const normalAccounts = await this.#listNormalAccounts();\n\n // keyring type map.\n const keyringTypes = new Map<string, number>();\n const previousAccounts = this.state.internalAccounts.accounts;\n\n const accounts: Record<string, InternalAccount> = [\n ...normalAccounts,\n ...snapAccounts,\n ].reduce((internalAccountMap, internalAccount) => {\n const keyringTypeName = keyringTypeToName(\n internalAccount.metadata.keyring.type,\n );\n const keyringAccountIndex = keyringTypes.get(keyringTypeName) ?? 0;\n if (keyringAccountIndex) {\n keyringTypes.set(keyringTypeName, keyringAccountIndex + 1);\n } else {\n keyringTypes.set(keyringTypeName, 1);\n }\n\n const existingAccount = previousAccounts[internalAccount.id];\n\n internalAccountMap[internalAccount.id] = {\n ...internalAccount,\n\n metadata: {\n ...internalAccount.metadata,\n name:\n this.#populateExistingMetadata(existingAccount?.id, 'name') ??\n `${keyringTypeName} ${keyringAccountIndex + 1}`,\n importTime:\n this.#populateExistingMetadata(existingAccount?.id, 'importTime') ??\n Date.now(),\n lastSelected:\n this.#populateExistingMetadata(\n existingAccount?.id,\n 'lastSelected',\n ) ?? 0,\n },\n };\n\n return internalAccountMap;\n }, {} as Record<string, InternalAccount>);\n\n this.update((currentState: Draft<AccountsControllerState>) => {\n // FIXME: deep clone of old state to get around Type instantiation is excessively deep and possibly infinite.\n const newState = deepCloneDraft(currentState);\n\n newState.internalAccounts.accounts = accounts;\n\n return newState;\n });\n }\n\n /**\n * Loads the backup state of the accounts controller.\n *\n * @param backup - The backup state to load.\n */\n loadBackup(backup: AccountsControllerState): void {\n if (backup.internalAccounts) {\n this.update((currentState: Draft<AccountsControllerState>) => {\n // FIXME: deep clone of old state to get around Type instantiation is excessively deep and possibly infinite.\n const newState = deepCloneDraft(currentState);\n\n newState.internalAccounts = backup.internalAccounts;\n\n return newState;\n });\n }\n }\n\n /**\n * Generates an internal account for a non-Snap account.\n * @param address - The address of the account.\n * @param type - The type of the account.\n * @returns The generated internal account.\n */\n #generateInternalAccountForNonSnapAccount(\n address: string,\n type: string,\n ): InternalAccount {\n return {\n id: getUUIDFromAddressOfNormalAccount(address),\n address,\n options: {},\n methods: [\n EthMethod.PersonalSign,\n EthMethod.Sign,\n EthMethod.SignTransaction,\n EthMethod.SignTypedDataV1,\n EthMethod.SignTypedDataV3,\n EthMethod.SignTypedDataV4,\n ],\n type: EthAccountType.Eoa,\n metadata: {\n name: '',\n importTime: Date.now(),\n keyring: {\n type,\n },\n },\n };\n }\n\n /**\n * Returns a list of internal accounts created using the SnapKeyring.\n *\n * @returns A promise that resolves to an array of InternalAccount objects.\n */\n async #listSnapAccounts(): Promise<InternalAccount[]> {\n const [snapKeyring] = this.messagingSystem.call(\n 'KeyringController:getKeyringsByType',\n SnapKeyring.type,\n );\n // snap keyring is not available until the first account is created in the keyring controller\n if (!snapKeyring) {\n return [];\n }\n\n const snapAccounts = (snapKeyring as SnapKeyring).listAccounts();\n\n return snapAccounts;\n }\n\n /**\n * Returns a list of normal accounts.\n * Note: listNormalAccounts is a temporary method until the keyrings all implement the InternalAccount interface.\n * Once all keyrings implement the InternalAccount interface, this method can be removed and getAccounts can be used instead.\n *\n * @returns A Promise that resolves to an array of InternalAccount objects.\n */\n async #listNormalAccounts(): Promise<InternalAccount[]> {\n const addresses = await this.messagingSystem.call(\n 'KeyringController:getAccounts',\n );\n const internalAccounts: InternalAccount[] = [];\n for (const address of addresses) {\n const keyring = await this.messagingSystem.call(\n 'KeyringController:getKeyringForAccount',\n address,\n );\n\n const keyringType = (keyring as Keyring<Json>).type;\n if (!isNormalKeyringType(keyringType as KeyringTypes)) {\n // We only consider \"normal accounts\" here, so keep looping\n continue;\n }\n\n const id = getUUIDFromAddressOfNormalAccount(address);\n\n internalAccounts.push({\n id,\n address,\n options: {},\n methods: [\n EthMethod.PersonalSign,\n EthMethod.Sign,\n EthMethod.SignTransaction,\n EthMethod.SignTypedDataV1,\n EthMethod.SignTypedDataV3,\n EthMethod.SignTypedDataV4,\n ],\n type: EthAccountType.Eoa,\n metadata: {\n name: this.#populateExistingMetadata(id, 'name') ?? '',\n importTime:\n this.#populateExistingMetadata(id, 'importTime') ?? Date.now(),\n lastSelected: this.#populateExistingMetadata(id, 'lastSelected') ?? 0,\n keyring: {\n type: (keyring as Keyring<Json>).type,\n },\n },\n });\n }\n\n return internalAccounts;\n }\n\n /**\n * Handles changes in the keyring state, specifically when new accounts are added or removed.\n *\n * @param keyringState - The new state of the keyring controller.\n */\n #handleOnKeyringStateChange(keyringState: KeyringControllerState): void {\n // check if there are any new accounts added\n // TODO: change when accountAdded event is added to the keyring controller\n\n // We check for keyrings length to be greater than 0 because the extension client may try execute\n // submit password twice and clear the keyring state.\n // https://github.com/MetaMask/KeyringController/blob/2d73a4deed8d013913f6ef0c9f5c0bb7c614f7d3/src/KeyringController.ts#L910\n if (keyringState.isUnlocked && keyringState.keyrings.length > 0) {\n const updatedNormalKeyringAddresses: AddressAndKeyringTypeObject[] = [];\n const updatedSnapKeyringAddresses: AddressAndKeyringTypeObject[] = [];\n\n for (const keyring of keyringState.keyrings) {\n if (keyring.type === KeyringTypes.snap) {\n updatedSnapKeyringAddresses.push(\n ...keyring.accounts.map((address) => {\n return {\n address,\n type: keyring.type,\n };\n }),\n );\n } else {\n updatedNormalKeyringAddresses.push(\n ...keyring.accounts.map((address) => {\n return {\n address,\n type: keyring.type,\n };\n }),\n );\n }\n }\n\n const { previousNormalInternalAccounts, previousSnapInternalAccounts } =\n this.listAccounts().reduce(\n (accumulator, account) => {\n if (account.metadata.keyring.type === KeyringTypes.snap) {\n accumulator.previousSnapInternalAccounts.push(account);\n } else {\n accumulator.previousNormalInternalAccounts.push(account);\n }\n return accumulator;\n },\n {\n previousNormalInternalAccounts: [] as InternalAccount[],\n previousSnapInternalAccounts: [] as InternalAccount[],\n },\n );\n\n const addedAccounts: AddressAndKeyringTypeObject[] = [];\n const deletedAccounts: InternalAccount[] = [];\n\n // snap account ids are random uuid while normal accounts\n // are determininistic based on the address\n\n // ^NOTE: This will be removed when normal accounts also implement internal accounts\n // finding all the normal accounts that were added\n for (const account of updatedNormalKeyringAddresses) {\n if (\n !this.state.internalAccounts.accounts[\n getUUIDFromAddressOfNormalAccount(account.address)\n ]\n ) {\n addedAccounts.push(account);\n }\n }\n\n // finding all the snap accounts that were added\n for (const account of updatedSnapKeyringAddresses) {\n if (\n !previousSnapInternalAccounts.find(\n (internalAccount: InternalAccount) =>\n internalAccount.address.toLowerCase() ===\n account.address.toLowerCase(),\n )\n ) {\n addedAccounts.push(account);\n }\n }\n\n // finding all the normal accounts that were deleted\n for (const account of previousNormalInternalAccounts) {\n if (\n !updatedNormalKeyringAddresses.find(\n ({ address }) =>\n address.toLowerCase() === account.address.toLowerCase(),\n )\n ) {\n deletedAccounts.push(account);\n }\n }\n\n // finding all the snap accounts that were deleted\n for (const account of previousSnapInternalAccounts) {\n if (\n !updatedSnapKeyringAddresses.find(\n ({ address }) =>\n address.toLowerCase() === account.address.toLowerCase(),\n )\n ) {\n deletedAccounts.push(account);\n }\n }\n\n if (deletedAccounts.length > 0) {\n for (const account of deletedAccounts) {\n this.#handleAccountRemoved(account.id);\n }\n }\n\n if (addedAccounts.length > 0) {\n for (const account of addedAccounts) {\n this.#handleNewAccountAdded(account);\n }\n }\n\n // handle if the selected account was deleted\n if (!this.getAccount(this.state.internalAccounts.selectedAccount)) {\n const [accountToSelect] = this.listAccounts().sort(\n (accountA, accountB) => {\n // sort by lastSelected descending\n return (\n (accountB.metadata.lastSelected ?? 0) -\n (accountA.metadata.lastSelected ?? 0)\n );\n },\n );\n\n // if the accountToSelect is undefined, then there are no accounts\n // it mean the keyring was reinitialized.\n if (!accountToSelect) {\n this.update((currentState: Draft<AccountsControllerState>) => {\n currentState.internalAccounts.selectedAccount = '';\n });\n return;\n }\n\n this.setSelectedAccount(accountToSelect.id);\n }\n }\n }\n\n /**\n * Handles the change in SnapControllerState by updating the metadata of accounts that have a snap enabled.\n *\n * @param snapState - The new SnapControllerState.\n */\n #handleOnSnapStateChange(snapState: SnapControllerState) {\n // only check if snaps changed in status\n const { snaps } = snapState;\n const accounts = this.listAccounts().filter(\n (account) => account.metadata.snap,\n );\n\n this.update((currentState: Draft<AccountsControllerState>) => {\n accounts.forEach((account) => {\n const currentAccount =\n currentState.internalAccounts.accounts[account.id];\n if (currentAccount.metadata.snap) {\n const snapId = currentAccount.metadata.snap.id;\n const storedSnap: Snap = snaps[snapId as SnapId];\n if (storedSnap) {\n currentAccount.metadata.snap.enabled =\n storedSnap.enabled && !storedSnap.blocked;\n }\n }\n });\n });\n }\n\n /**\n * Returns the list of accounts for a given keyring type.\n * @param keyringType - The type of keyring.\n * @returns The list of accounts associcated with this keyring type.\n */\n #getAccountsByKeyringType(keyringType: string) {\n return this.listAccounts().filter((internalAccount) => {\n // We do consider `hd` and `simple` keyrings to be of same type. So we check those 2 types\n // to group those accounts together!\n if (\n keyringType === KeyringTypes.hd ||\n keyringType === KeyringTypes.simple\n ) {\n return (\n internalAccount.metadata.keyring.type === KeyringTypes.hd ||\n internalAccount.metadata.keyring.type === KeyringTypes.simple\n );\n }\n\n return internalAccount.metadata.keyring.type === keyringType;\n });\n }\n\n /**\n * Returns the last selected account from the given array of accounts.\n *\n * @param accounts - An array of InternalAccount objects.\n * @returns The InternalAccount object that was last selected, or undefined if the array is empty.\n */\n #getLastSelectedAccount(\n accounts: InternalAccount[],\n ): InternalAccount | undefined {\n return accounts.reduce((prevAccount, currentAccount) => {\n if (\n // When the account is added, lastSelected will be set\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n currentAccount.metadata.lastSelected! >\n // When the account is added, lastSelected will be set\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n prevAccount.metadata.lastSelected!\n ) {\n return currentAccount;\n }\n return prevAccount;\n }, accounts[0]);\n }\n\n /**\n * Returns the next account number for a given keyring type.\n * @param keyringType - The type of keyring.\n * @returns An object containing the account prefix and index to use.\n */\n getNextAvailableAccountName(keyringType: string = KeyringTypes.hd): string {\n const keyringName = keyringTypeToName(keyringType);\n const keyringAccounts = this.#getAccountsByKeyringType(keyringType);\n const lastDefaultIndexUsedForKeyringType = keyringAccounts.reduce(\n (maxInternalAccountIndex, internalAccount) => {\n // We **DO NOT USE** `\\d+` here to only consider valid \"human\"\n // number (rounded decimal number)\n const match = new RegExp(`${keyringName} ([0-9]+)$`, 'u').exec(\n internalAccount.metadata.name,\n );\n\n if (match) {\n // Quoting `RegExp.exec` documentation:\n // > The returned array has the matched text as the first item, and then one item for\n // > each capturing group of the matched text.\n // So use `match[1]` to get the captured value\n const internalAccountIndex = parseInt(match[1], 10);\n return Math.max(maxInternalAccountIndex, internalAccountIndex);\n }\n\n return maxInternalAccountIndex;\n },\n 0,\n );\n\n const index = Math.max(\n keyringAccounts.length + 1,\n lastDefaultIndexUsedForKeyringType + 1,\n );\n\n return `${keyringName} ${index}`;\n }\n\n /**\n * Checks if an account is compatible with a given chain namespace.\n * @private\n * @param account - The account to check compatibility for.\n * @param chainId - The CAIP2 to check compatibility with.\n * @returns Returns true if the account is compatible with the chain namespace, otherwise false.\n */\n #isAccountCompatibleWithChain(\n account: InternalAccount,\n chainId: CaipChainId,\n ): boolean {\n // TODO: Change this logic to not use account's type\n // Because we currently only use type, we can only use namespace for now.\n return account.type.startsWith(parseCaipChainId(chainId).namespace);\n }\n\n /**\n * Handles the addition of a new account to the controller.\n * If the account is not a Snap Keyring account, generates an internal account for it and adds it to the controller.\n * If the account is a Snap Keyring account, retrieves the account from the keyring and adds it to the controller.\n * @param account - The address and keyring type object of the new account.\n */\n #handleNewAccountAdded(account: AddressAndKeyringTypeObject) {\n let newAccount: InternalAccount;\n if (account.type !== KeyringTypes.snap) {\n newAccount = this.#generateInternalAccountForNonSnapAccount(\n account.address,\n account.type,\n );\n } else {\n const [snapKeyring] = this.messagingSystem.call(\n 'KeyringController:getKeyringsByType',\n SnapKeyring.type,\n );\n\n newAccount = (snapKeyring as SnapKeyring).getAccountByAddress(\n account.address,\n ) as InternalAccount;\n\n // The snap deleted the account before the keyring controller could add it\n if (!newAccount) {\n return;\n }\n }\n\n // Get next account name available for this given keyring\n const accountName = this.getNextAvailableAccountName(\n newAccount.metadata.keyring.type,\n );\n\n this.update((currentState: Draft<AccountsControllerState>) => {\n // FIXME: deep clone of old state to get around Type instantiation is excessively deep and possibly infinite.\n const newState = deepCloneDraft(currentState);\n\n newState.internalAccounts.accounts[newAccount.id] = {\n ...newAccount,\n metadata: {\n ...newAccount.metadata,\n name: accountName,\n importTime: Date.now(),\n lastSelected: Date.now(),\n },\n };\n\n return newState;\n });\n\n this.setSelectedAccount(newAccount.id);\n }\n\n /**\n * Handles the removal of an account from the internal accounts list.\n * @param accountId - The ID of the account to be removed.\n */\n #handleAccountRemoved(accountId: string) {\n this.update((currentState: Draft<AccountsControllerState>) => {\n delete currentState.internalAccounts.accounts[accountId];\n });\n }\n\n /**\n * Retrieves the value of a specific metadata key for an existing account.\n * @param accountId - The ID of the account.\n * @param metadataKey - The key of the metadata to retrieve.\n * @returns The value of the specified metadata key, or undefined if the account or metadata key does not exist.\n */\n #populateExistingMetadata<T extends keyof InternalAccount['metadata']>(\n accountId: string,\n metadataKey: T,\n ): InternalAccount['metadata'][T] | undefined {\n const internalAccount = this.getAccount(accountId);\n return internalAccount ? internalAccount.metadata[metadataKey] : undefined;\n }\n\n /**\n * Registers message handlers for the AccountsController.\n * @private\n */\n #registerMessageHandlers() {\n this.messagingSystem.registerActionHandler(\n `${controllerName}:setSelectedAccount`,\n this.setSelectedAccount.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${controllerName}:listAccounts`,\n this.listAccounts.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${controllerName}:setAccountName`,\n this.setAccountName.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${controllerName}:updateAccounts`,\n this.updateAccounts.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${controllerName}:getSelectedAccount`,\n this.getSelectedAccount.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${controllerName}:getSelectedMultichainAccount`,\n this.getSelectedMultichainAccount.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${controllerName}:getAccountByAddress`,\n this.getAccountByAddress.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `${controllerName}:getNextAvailableAccountName`,\n this.getNextAvailableAccountName.bind(this),\n );\n\n this.messagingSystem.registerActionHandler(\n `AccountsController:getAccount`,\n this.getAccount.bind(this),\n );\n }\n}\n"]}