@metamask-previews/accounts-controller 31.0.0-preview-21067ca7 → 31.0.0-preview-7b919d75

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9
9
 
10
10
  ### Added
11
11
 
12
- - Use new typed `KeyringAccount.options` for BIP-44 compatible accounts ([#6122](https://github.com/MetaMask/core/pull/6122)), ([#6147](https://github.com/MetaMask/core/pull/6147))
12
+ - Add `groupIndex` to EVM HD account options ([#6122](https://github.com/MetaMask/core/pull/6122))
13
13
 
14
14
  ### Changed
15
15
 
@@ -4,7 +4,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
4
4
  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");
5
5
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
6
6
  };
7
- var _AccountsController_instances, _AccountsController_assertAccountCanBeRenamed, _AccountsController_getInternalAccountForNonSnapAccount, _AccountsController_getSnapKeyring, _AccountsController_handleOnSnapKeyringAccountEvent, _AccountsController_handleOnKeyringStateChange, _AccountsController_update, _AccountsController_handleOnSnapStateChange, _AccountsController_getAccountsByKeyringType, _AccountsController_getLastSelectedAccount, _AccountsController_getLastSelectedIndex, _AccountsController_getInternalAccountFromAddressAndType, _AccountsController_handleOnMultichainNetworkDidChange, _AccountsController_subscribeToMessageEvents, _AccountsController_registerMessageHandlers;
7
+ var _AccountsController_instances, _AccountsController_assertAccountCanBeRenamed, _AccountsController_generateInternalAccountForNonSnapAccount, _AccountsController_getSnapKeyring, _AccountsController_listSnapAccounts, _AccountsController_listNormalAccounts, _AccountsController_handleOnSnapKeyringAccountEvent, _AccountsController_handleOnKeyringStateChange, _AccountsController_update, _AccountsController_handleOnSnapStateChange, _AccountsController_getAccountsByKeyringType, _AccountsController_getLastSelectedAccount, _AccountsController_getLastSelectedIndex, _AccountsController_getInternalAccountFromAddressAndType, _AccountsController_handleOnMultichainNetworkDidChange, _AccountsController_populateExistingMetadata, _AccountsController_subscribeToMessageEvents, _AccountsController_registerMessageHandlers;
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.AccountsController = exports.EMPTY_ACCOUNT = void 0;
10
10
  const base_controller_1 = require("@metamask/base-controller");
@@ -13,7 +13,6 @@ const keyring_api_1 = require("@metamask/keyring-api");
13
13
  const keyring_controller_1 = require("@metamask/keyring-controller");
14
14
  const keyring_utils_1 = require("@metamask/keyring-utils");
15
15
  const utils_1 = require("@metamask/utils");
16
- const lodash_1 = require("lodash");
17
16
  const utils_2 = require("./utils.cjs");
18
17
  const controllerName = 'AccountsController';
19
18
  const accountsControllerMetadata = {
@@ -270,41 +269,38 @@ class AccountsController extends base_controller_1.BaseController {
270
269
  * @returns A Promise that resolves when the accounts have been updated.
271
270
  */
272
271
  async updateAccounts() {
273
- const keyringAccountIndexes = new Map();
274
- const existingInternalAccounts = this.state.internalAccounts.accounts;
275
- const internalAccounts = {};
276
- const { keyrings } = this.messagingSystem.call('KeyringController:getState');
277
- for (const keyring of keyrings) {
278
- const keyringTypeName = (0, utils_2.keyringTypeToName)(keyring.type);
279
- for (const address of keyring.accounts) {
280
- const internalAccount = __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_getInternalAccountFromAddressAndType).call(this, address, keyring);
281
- // This should never really happen, but if for some reason we're not
282
- // able to get the Snap keyring reference, this would return an
283
- // undefined account.
284
- // So we just skip it, even though, this should not really happen.
285
- if (!internalAccount) {
286
- continue;
287
- }
288
- // Get current index for this keyring (we use human indexing, so start at 1).
289
- const keyringAccountIndex = keyringAccountIndexes.get(keyringTypeName) ?? 1;
290
- const existingAccount = existingInternalAccounts[internalAccount.id];
291
- internalAccounts[internalAccount.id] = {
292
- ...internalAccount,
293
- metadata: {
294
- ...internalAccount.metadata,
295
- // Re-use existing metadata if any.
296
- name: existingAccount?.metadata.name ??
297
- `${keyringTypeName} ${keyringAccountIndex}`,
298
- importTime: existingAccount?.metadata.importTime ?? Date.now(),
299
- lastSelected: existingAccount?.metadata.lastSelected ?? 0,
300
- },
301
- };
302
- // Increment the account index for this keyring.
303
- keyringAccountIndexes.set(keyringTypeName, keyringAccountIndex + 1);
272
+ const snapAccounts = await __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_listSnapAccounts).call(this);
273
+ const normalAccounts = await __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_listNormalAccounts).call(this);
274
+ // keyring type map.
275
+ const keyringTypes = new Map();
276
+ const previousAccounts = this.state.internalAccounts.accounts;
277
+ const accounts = [
278
+ ...normalAccounts,
279
+ ...snapAccounts,
280
+ ].reduce((internalAccountMap, internalAccount) => {
281
+ const keyringTypeName = (0, utils_2.keyringTypeToName)(internalAccount.metadata.keyring.type);
282
+ const keyringAccountIndex = keyringTypes.get(keyringTypeName) ?? 0;
283
+ if (keyringAccountIndex) {
284
+ keyringTypes.set(keyringTypeName, keyringAccountIndex + 1);
304
285
  }
305
- }
286
+ else {
287
+ keyringTypes.set(keyringTypeName, 1);
288
+ }
289
+ const existingAccount = previousAccounts[internalAccount.id];
290
+ internalAccountMap[internalAccount.id] = {
291
+ ...internalAccount,
292
+ metadata: {
293
+ ...internalAccount.metadata,
294
+ name: __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_populateExistingMetadata).call(this, existingAccount?.id, 'name') ??
295
+ `${keyringTypeName} ${keyringAccountIndex + 1}`,
296
+ importTime: __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_populateExistingMetadata).call(this, existingAccount?.id, 'importTime') ?? Date.now(),
297
+ lastSelected: __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_populateExistingMetadata).call(this, existingAccount?.id, 'lastSelected') ?? 0,
298
+ },
299
+ };
300
+ return internalAccountMap;
301
+ }, {});
306
302
  __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_update).call(this, (state) => {
307
- state.internalAccounts.accounts = internalAccounts;
303
+ state.internalAccounts.accounts = accounts;
308
304
  });
309
305
  }
310
306
  /**
@@ -353,63 +349,11 @@ _AccountsController_instances = new WeakSet(), _AccountsController_assertAccount
353
349
  internalAccount.id !== account.id)) {
354
350
  throw new Error('Account name already exists');
355
351
  }
356
- }, _AccountsController_getInternalAccountForNonSnapAccount = function _AccountsController_getInternalAccountForNonSnapAccount(address, keyring) {
357
- const id = (0, utils_2.getUUIDFromAddressOfNormalAccount)(address);
358
- // We might have an account for this ID already, so we'll just re-use
359
- // the same metadata
360
- const account = this.getAccount(id);
361
- const metadata = {
362
- name: account?.metadata.name ?? '',
363
- ...(account?.metadata.nameLastUpdatedAt
364
- ? {
365
- nameLastUpdatedAt: account?.metadata.nameLastUpdatedAt,
366
- }
367
- : {}),
368
- importTime: account?.metadata.importTime ?? Date.now(),
369
- lastSelected: account?.metadata.lastSelected ?? 0,
370
- keyring: {
371
- type: keyring.type,
372
- },
373
- };
374
- let options = {};
375
- if ((0, utils_2.isHdKeyringType)(keyring.type)) {
376
- // We need to find the account index from its HD keyring.
377
- const groupIndex = (0, utils_2.getEvmGroupIndexFromAddressIndex)(keyring, address);
378
- // If for some reason, we cannot find this address, then the caller made a mistake
379
- // and it did not use the proper keyring object. For now, we do not fail and just
380
- // consider this account as "simple account".
381
- if (groupIndex !== undefined) {
382
- // NOTE: We are not using the `hdPath` from the associated keyring here and
383
- // getting the keyring instance here feels a bit overkill.
384
- // This will be naturally fixed once every keyring start using `KeyringAccount` and implement the keyring API.
385
- const derivationPath = (0, utils_2.getEvmDerivationPathForIndex)(groupIndex);
386
- // Those are "legacy options" and they were used before `KeyringAccount` added
387
- // support for type options. We keep those temporarily until we update everything
388
- // to use the new typed options.
389
- const legacyOptions = {
390
- entropySource: keyring.metadata.id,
391
- derivationPath,
392
- groupIndex,
393
- };
394
- // New typed entropy options. This is required for multichain accounts.
395
- const entropyOptions = {
396
- entropy: {
397
- type: keyring_api_1.KeyringAccountEntropyTypeOption.Mnemonic,
398
- id: keyring.metadata.id,
399
- derivationPath,
400
- groupIndex,
401
- },
402
- };
403
- options = {
404
- ...legacyOptions,
405
- ...entropyOptions,
406
- };
407
- }
408
- }
352
+ }, _AccountsController_generateInternalAccountForNonSnapAccount = function _AccountsController_generateInternalAccountForNonSnapAccount(address, type) {
409
353
  return {
410
- id,
354
+ id: (0, utils_2.getUUIDFromAddressOfNormalAccount)(address),
411
355
  address,
412
- options,
356
+ options: {},
413
357
  methods: [
414
358
  keyring_api_1.EthMethod.PersonalSign,
415
359
  keyring_api_1.EthMethod.Sign,
@@ -420,13 +364,90 @@ _AccountsController_instances = new WeakSet(), _AccountsController_assertAccount
420
364
  ],
421
365
  scopes: [keyring_api_1.EthScope.Eoa],
422
366
  type: keyring_api_1.EthAccountType.Eoa,
423
- metadata,
367
+ metadata: {
368
+ name: '',
369
+ importTime: Date.now(),
370
+ keyring: {
371
+ type,
372
+ },
373
+ },
424
374
  };
425
375
  }, _AccountsController_getSnapKeyring = function _AccountsController_getSnapKeyring() {
426
376
  const [snapKeyring] = this.messagingSystem.call('KeyringController:getKeyringsByType', eth_snap_keyring_1.SnapKeyring.type);
427
377
  // Snap keyring is not available until the first account is created in the keyring
428
378
  // controller, so this might be undefined.
429
379
  return snapKeyring;
380
+ }, _AccountsController_listSnapAccounts =
381
+ /**
382
+ * Returns a list of internal accounts created using the SnapKeyring.
383
+ *
384
+ * @returns A promise that resolves to an array of InternalAccount objects.
385
+ */
386
+ async function _AccountsController_listSnapAccounts() {
387
+ const keyring = __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_getSnapKeyring).call(this);
388
+ if (!keyring) {
389
+ return [];
390
+ }
391
+ return keyring.listAccounts();
392
+ }, _AccountsController_listNormalAccounts =
393
+ /**
394
+ * Returns a list of normal accounts.
395
+ * Note: listNormalAccounts is a temporary method until the keyrings all implement the InternalAccount interface.
396
+ * Once all keyrings implement the InternalAccount interface, this method can be removed and getAccounts can be used instead.
397
+ *
398
+ * @returns A Promise that resolves to an array of InternalAccount objects.
399
+ */
400
+ async function _AccountsController_listNormalAccounts() {
401
+ const internalAccounts = [];
402
+ const { keyrings } = this.messagingSystem.call('KeyringController:getState');
403
+ for (const keyring of keyrings) {
404
+ const keyringType = keyring.type;
405
+ if (!(0, utils_2.isNormalKeyringType)(keyringType)) {
406
+ // We only consider "normal accounts" here, so keep looping
407
+ continue;
408
+ }
409
+ for (const [accountIndex, address] of keyring.accounts.entries()) {
410
+ const id = (0, utils_2.getUUIDFromAddressOfNormalAccount)(address);
411
+ let options = {};
412
+ if ((0, utils_2.isHdKeyringType)(keyring.type)) {
413
+ options = {
414
+ entropySource: keyring.metadata.id,
415
+ // NOTE: We are not using the `hdPath` from the associated keyring here and
416
+ // getting the keyring instance here feels a bit overkill.
417
+ // This will be naturally fixed once every keyring start using `KeyringAccount` and implement the keyring API.
418
+ derivationPath: (0, utils_2.getDerivationPathForIndex)(accountIndex),
419
+ // Required now for multichain accounts.
420
+ groupIndex: accountIndex,
421
+ };
422
+ }
423
+ const nameLastUpdatedAt = __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_populateExistingMetadata).call(this, id, 'nameLastUpdatedAt');
424
+ internalAccounts.push({
425
+ id,
426
+ address,
427
+ options,
428
+ methods: [
429
+ keyring_api_1.EthMethod.PersonalSign,
430
+ keyring_api_1.EthMethod.Sign,
431
+ keyring_api_1.EthMethod.SignTransaction,
432
+ keyring_api_1.EthMethod.SignTypedDataV1,
433
+ keyring_api_1.EthMethod.SignTypedDataV3,
434
+ keyring_api_1.EthMethod.SignTypedDataV4,
435
+ ],
436
+ scopes: [keyring_api_1.EthScope.Eoa],
437
+ type: keyring_api_1.EthAccountType.Eoa,
438
+ metadata: {
439
+ name: __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_populateExistingMetadata).call(this, id, 'name') ?? '',
440
+ ...(nameLastUpdatedAt && { nameLastUpdatedAt }),
441
+ importTime: __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_populateExistingMetadata).call(this, id, 'importTime') ?? Date.now(),
442
+ lastSelected: __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_populateExistingMetadata).call(this, id, 'lastSelected') ?? 0,
443
+ keyring: {
444
+ type: keyringType,
445
+ },
446
+ },
447
+ });
448
+ }
449
+ }
450
+ return internalAccounts;
430
451
  }, _AccountsController_handleOnSnapKeyringAccountEvent = function _AccountsController_handleOnSnapKeyringAccountEvent(event, ...payload) {
431
452
  this.messagingSystem.publish(event, ...payload);
432
453
  }, _AccountsController_handleOnKeyringStateChange = function _AccountsController_handleOnKeyringStateChange({ isUnlocked, keyrings, }) {
@@ -453,7 +474,7 @@ _AccountsController_instances = new WeakSet(), _AccountsController_assertAccount
453
474
  // Gets the patch object based on the keyring type (since Snap accounts and other accounts
454
475
  // are handled differently).
455
476
  const patchOf = (type) => {
456
- if ((0, utils_2.isSnapKeyringType)(type)) {
477
+ if (type === keyring_controller_1.KeyringTypes.snap) {
457
478
  return patches.snap;
458
479
  }
459
480
  return patches.normal;
@@ -480,7 +501,11 @@ _AccountsController_instances = new WeakSet(), _AccountsController_assertAccount
480
501
  // Otherwise, that's a new account.
481
502
  patch.added.push({
482
503
  address,
483
- keyring,
504
+ type: keyring.type,
505
+ // Automatically injects `entropySource` for HD accounts only.
506
+ options: keyring.type === keyring_controller_1.KeyringTypes.hd
507
+ ? { entropySource: keyring.metadata.id }
508
+ : {},
484
509
  });
485
510
  }
486
511
  // Keep track of those address to check for removed accounts later.
@@ -510,7 +535,7 @@ _AccountsController_instances = new WeakSet(), _AccountsController_assertAccount
510
535
  diff.removed.push(account.id);
511
536
  }
512
537
  for (const added of patch.added) {
513
- const account = __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_getInternalAccountFromAddressAndType).call(this, added.address, added.keyring);
538
+ const account = __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_getInternalAccountFromAddressAndType).call(this, added.address, added.type);
514
539
  if (account) {
515
540
  // Re-compute the list of accounts everytime, so we can make sure new names
516
541
  // are also considered.
@@ -527,6 +552,10 @@ _AccountsController_instances = new WeakSet(), _AccountsController_assertAccount
527
552
  importTime: Date.now(),
528
553
  lastSelected,
529
554
  },
555
+ options: {
556
+ ...account.options,
557
+ ...added.options,
558
+ },
530
559
  };
531
560
  diff.added.push(internalAccounts.accounts[account.id]);
532
561
  }
@@ -611,9 +640,10 @@ _AccountsController_instances = new WeakSet(), _AccountsController_assertAccount
611
640
  return (accounts ?? this.listMultichainAccounts()).filter((internalAccount) => {
612
641
  // We do consider `hd` and `simple` keyrings to be of same type. So we check those 2 types
613
642
  // to group those accounts together!
614
- if ((0, utils_2.isHdKeyringType)(keyringType) || (0, utils_2.isSimpleKeyringType)(keyringType)) {
615
- return ((0, utils_2.isHdKeyringType)(internalAccount.metadata.keyring.type) ||
616
- (0, utils_2.isSimpleKeyringType)(internalAccount.metadata.keyring.type));
643
+ if (keyringType === keyring_controller_1.KeyringTypes.hd ||
644
+ keyringType === keyring_controller_1.KeyringTypes.simple) {
645
+ return (internalAccount.metadata.keyring.type === keyring_controller_1.KeyringTypes.hd ||
646
+ internalAccount.metadata.keyring.type === keyring_controller_1.KeyringTypes.simple);
617
647
  }
618
648
  return internalAccount.metadata.keyring.type === keyringType;
619
649
  });
@@ -628,40 +658,18 @@ _AccountsController_instances = new WeakSet(), _AccountsController_assertAccount
628
658
  // NOTE: For now we use the current date, since we know this value
629
659
  // will always be higher than any already selected account index.
630
660
  return Date.now();
631
- }, _AccountsController_getInternalAccountFromAddressAndType = function _AccountsController_getInternalAccountFromAddressAndType(address, keyring) {
632
- if ((0, utils_2.isSnapKeyringType)(keyring.type)) {
633
- const snapKeyring = __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_getSnapKeyring).call(this);
661
+ }, _AccountsController_getInternalAccountFromAddressAndType = function _AccountsController_getInternalAccountFromAddressAndType(address, type) {
662
+ if (type === keyring_controller_1.KeyringTypes.snap) {
663
+ const keyring = __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_getSnapKeyring).call(this);
634
664
  // We need the Snap keyring to retrieve the account from its address.
635
- if (!snapKeyring) {
665
+ if (!keyring) {
636
666
  return undefined;
637
667
  }
638
668
  // This might be undefined if the Snap deleted the account before
639
669
  // reaching that point.
640
- let account = snapKeyring.getAccountByAddress(address);
641
- if (account) {
642
- // We force the copy here, to avoid mutating the reference returned by the Snap keyring.
643
- account = (0, lodash_1.cloneDeep)(account);
644
- // MIGRATION: To avoid any existing Snap account migration, we are
645
- // just "adding" the new typed options that we need for multichain
646
- // accounts. Ultimately, we would need a real Snap account migrations
647
- // (being handled by each Snaps).
648
- if ((0, utils_2.isHdSnapKeyringAccount)(account)) {
649
- const options = {
650
- ...account.options,
651
- entropy: {
652
- type: keyring_api_1.KeyringAccountEntropyTypeOption.Mnemonic,
653
- id: account.options.entropySource,
654
- groupIndex: account.options.index,
655
- derivationPath: account.options.derivationPath,
656
- },
657
- };
658
- // Inject the new typed options to the internal account copy.
659
- account.options = options;
660
- }
661
- }
662
- return account;
670
+ return keyring.getAccountByAddress(address);
663
671
  }
664
- return __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_getInternalAccountForNonSnapAccount).call(this, address, keyring);
672
+ return __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_generateInternalAccountForNonSnapAccount).call(this, address, type);
665
673
  }, _AccountsController_handleOnMultichainNetworkDidChange = function _AccountsController_handleOnMultichainNetworkDidChange(id) {
666
674
  let accountId;
667
675
  // We only support non-EVM Caip chain IDs at the moment. Ex Solana and Bitcoin
@@ -683,6 +691,9 @@ _AccountsController_instances = new WeakSet(), _AccountsController_assertAccount
683
691
  currentState.internalAccounts.selectedAccount = accountId;
684
692
  });
685
693
  // DO NOT publish AccountsController:setSelectedAccount to prevent circular listener loops
694
+ }, _AccountsController_populateExistingMetadata = function _AccountsController_populateExistingMetadata(accountId, metadataKey, account) {
695
+ const internalAccount = account ?? this.getAccount(accountId);
696
+ return internalAccount ? internalAccount.metadata[metadataKey] : undefined;
686
697
  }, _AccountsController_subscribeToMessageEvents = function _AccountsController_subscribeToMessageEvents() {
687
698
  this.messagingSystem.subscribe('SnapController:stateChange', (snapStateState) => __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_handleOnSnapStateChange).call(this, snapStateState));
688
699
  this.messagingSystem.subscribe('KeyringController:stateChange', (keyringState) => __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_handleOnKeyringStateChange).call(this, keyringState));