@metamask-previews/accounts-controller 31.0.0-preview-25dbca82 → 31.0.0-preview-31e2b13a

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
- - Add `groupIndex` to EVM HD account options ([#6122](https://github.com/MetaMask/core/pull/6122))
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))
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_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;
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;
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");
@@ -269,38 +269,44 @@ class AccountsController extends base_controller_1.BaseController {
269
269
  * @returns A Promise that resolves when the accounts have been updated.
270
270
  */
271
271
  async updateAccounts() {
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);
285
- }
286
- else {
287
- keyringTypes.set(keyringTypeName, 1);
272
+ const keyringAccountIndexes = new Map();
273
+ const existingInternalAccounts = this.state.internalAccounts.accounts;
274
+ const internalAccounts = {};
275
+ const { keyrings } = this.messagingSystem.call('KeyringController:getState');
276
+ for (const keyring of keyrings) {
277
+ const keyringTypeName = (0, utils_2.keyringTypeToName)(keyring.type);
278
+ for (const address of keyring.accounts) {
279
+ const internalAccount = __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_getInternalAccountFromAddressAndType).call(this, address, keyring);
280
+ // This should never really happen, but if for some reason we're not
281
+ // able to get the Snap keyring reference, this would return an
282
+ // undefined account.
283
+ // So we just skip it, even though, this should not really happen.
284
+ if (!internalAccount) {
285
+ continue;
286
+ }
287
+ const keyringAccountIndex = keyringAccountIndexes.get(keyringTypeName) ?? 0;
288
+ if (keyringAccountIndex) {
289
+ keyringAccountIndexes.set(keyringTypeName, keyringAccountIndex + 1);
290
+ }
291
+ else {
292
+ keyringAccountIndexes.set(keyringTypeName, 1);
293
+ }
294
+ const existingAccount = existingInternalAccounts[internalAccount.id];
295
+ internalAccounts[internalAccount.id] = {
296
+ ...internalAccount,
297
+ metadata: {
298
+ ...internalAccount.metadata,
299
+ // Re-use existing metadata if any.
300
+ name: existingAccount?.metadata.name ??
301
+ `${keyringTypeName} ${keyringAccountIndex + 1}`,
302
+ importTime: existingAccount?.metadata.importTime ?? Date.now(),
303
+ lastSelected: existingAccount?.metadata.lastSelected ?? 0,
304
+ },
305
+ };
288
306
  }
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
- }, {});
307
+ }
302
308
  __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_update).call(this, (state) => {
303
- state.internalAccounts.accounts = accounts;
309
+ state.internalAccounts.accounts = internalAccounts;
304
310
  });
305
311
  }
306
312
  /**
@@ -349,11 +355,63 @@ _AccountsController_instances = new WeakSet(), _AccountsController_assertAccount
349
355
  internalAccount.id !== account.id)) {
350
356
  throw new Error('Account name already exists');
351
357
  }
352
- }, _AccountsController_generateInternalAccountForNonSnapAccount = function _AccountsController_generateInternalAccountForNonSnapAccount(address, type) {
358
+ }, _AccountsController_getInternalAccountForNonSnapAccount = function _AccountsController_getInternalAccountForNonSnapAccount(address, keyring) {
359
+ const id = (0, utils_2.getUUIDFromAddressOfNormalAccount)(address);
360
+ // We might have an account for this ID already, so we'll just re-use
361
+ // the same metadata
362
+ const account = this.getAccount(id);
363
+ const metadata = {
364
+ name: account?.metadata.name ?? '',
365
+ ...(account?.metadata.nameLastUpdatedAt
366
+ ? {
367
+ nameLastUpdatedAt: account?.metadata.nameLastUpdatedAt,
368
+ }
369
+ : {}),
370
+ importTime: account?.metadata.importTime ?? Date.now(),
371
+ lastSelected: account?.metadata.lastSelected ?? 0,
372
+ keyring: {
373
+ type: keyring.type,
374
+ },
375
+ };
376
+ let options = {};
377
+ if ((0, utils_2.isHdKeyringType)(keyring.type)) {
378
+ // We need to find the account index from its HD keyring.
379
+ const groupIndex = (0, utils_2.getEvmGroupIndexFromAddressIndex)(keyring, address);
380
+ // If for some reason, we cannot find this address, then the caller made a mistake
381
+ // and it did not use the proper keyring object. For now, we do not fail and just
382
+ // consider this account as "simple account".
383
+ if (groupIndex !== undefined) {
384
+ // NOTE: We are not using the `hdPath` from the associated keyring here and
385
+ // getting the keyring instance here feels a bit overkill.
386
+ // This will be naturally fixed once every keyring start using `KeyringAccount` and implement the keyring API.
387
+ const derivationPath = (0, utils_2.getEvmDerivationPathForIndex)(groupIndex);
388
+ // Those are "legacy options" and they were used before `KeyringAccount` added
389
+ // support for type options. We keep those temporarily until we update everything
390
+ // to use the new typed options.
391
+ const legacyOptions = {
392
+ entropySource: keyring.metadata.id,
393
+ derivationPath,
394
+ groupIndex,
395
+ };
396
+ // New typed entropy options. This is required for multichain accounts.
397
+ const entropyOptions = {
398
+ entropy: {
399
+ type: keyring_api_1.KeyringAccountEntropyTypeOption.Mnemonic,
400
+ id: keyring.metadata.id,
401
+ derivationPath,
402
+ groupIndex,
403
+ },
404
+ };
405
+ options = {
406
+ ...legacyOptions,
407
+ ...entropyOptions,
408
+ };
409
+ }
410
+ }
353
411
  return {
354
- id: (0, utils_2.getUUIDFromAddressOfNormalAccount)(address),
412
+ id,
355
413
  address,
356
- options: {},
414
+ options,
357
415
  methods: [
358
416
  keyring_api_1.EthMethod.PersonalSign,
359
417
  keyring_api_1.EthMethod.Sign,
@@ -364,90 +422,13 @@ _AccountsController_instances = new WeakSet(), _AccountsController_assertAccount
364
422
  ],
365
423
  scopes: [keyring_api_1.EthScope.Eoa],
366
424
  type: keyring_api_1.EthAccountType.Eoa,
367
- metadata: {
368
- name: '',
369
- importTime: Date.now(),
370
- keyring: {
371
- type,
372
- },
373
- },
425
+ metadata,
374
426
  };
375
427
  }, _AccountsController_getSnapKeyring = function _AccountsController_getSnapKeyring() {
376
428
  const [snapKeyring] = this.messagingSystem.call('KeyringController:getKeyringsByType', eth_snap_keyring_1.SnapKeyring.type);
377
429
  // Snap keyring is not available until the first account is created in the keyring
378
430
  // controller, so this might be undefined.
379
431
  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;
451
432
  }, _AccountsController_handleOnSnapKeyringAccountEvent = function _AccountsController_handleOnSnapKeyringAccountEvent(event, ...payload) {
452
433
  this.messagingSystem.publish(event, ...payload);
453
434
  }, _AccountsController_handleOnKeyringStateChange = function _AccountsController_handleOnKeyringStateChange({ isUnlocked, keyrings, }) {
@@ -474,7 +455,7 @@ async function _AccountsController_listNormalAccounts() {
474
455
  // Gets the patch object based on the keyring type (since Snap accounts and other accounts
475
456
  // are handled differently).
476
457
  const patchOf = (type) => {
477
- if (type === keyring_controller_1.KeyringTypes.snap) {
458
+ if ((0, utils_2.isSnapKeyringType)(type)) {
478
459
  return patches.snap;
479
460
  }
480
461
  return patches.normal;
@@ -501,11 +482,7 @@ async function _AccountsController_listNormalAccounts() {
501
482
  // Otherwise, that's a new account.
502
483
  patch.added.push({
503
484
  address,
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
- : {},
485
+ keyring,
509
486
  });
510
487
  }
511
488
  // Keep track of those address to check for removed accounts later.
@@ -535,7 +512,7 @@ async function _AccountsController_listNormalAccounts() {
535
512
  diff.removed.push(account.id);
536
513
  }
537
514
  for (const added of patch.added) {
538
- const account = __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_getInternalAccountFromAddressAndType).call(this, added.address, added.type);
515
+ const account = __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_getInternalAccountFromAddressAndType).call(this, added.address, added.keyring);
539
516
  if (account) {
540
517
  // Re-compute the list of accounts everytime, so we can make sure new names
541
518
  // are also considered.
@@ -552,10 +529,6 @@ async function _AccountsController_listNormalAccounts() {
552
529
  importTime: Date.now(),
553
530
  lastSelected,
554
531
  },
555
- options: {
556
- ...account.options,
557
- ...added.options,
558
- },
559
532
  };
560
533
  diff.added.push(internalAccounts.accounts[account.id]);
561
534
  }
@@ -640,10 +613,9 @@ async function _AccountsController_listNormalAccounts() {
640
613
  return (accounts ?? this.listMultichainAccounts()).filter((internalAccount) => {
641
614
  // We do consider `hd` and `simple` keyrings to be of same type. So we check those 2 types
642
615
  // to group those accounts together!
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);
616
+ if ((0, utils_2.isHdKeyringType)(keyringType) || (0, utils_2.isSimpleKeyringType)(keyringType)) {
617
+ return ((0, utils_2.isHdKeyringType)(internalAccount.metadata.keyring.type) ||
618
+ (0, utils_2.isSimpleKeyringType)(internalAccount.metadata.keyring.type));
647
619
  }
648
620
  return internalAccount.metadata.keyring.type === keyringType;
649
621
  });
@@ -658,18 +630,40 @@ async function _AccountsController_listNormalAccounts() {
658
630
  // NOTE: For now we use the current date, since we know this value
659
631
  // will always be higher than any already selected account index.
660
632
  return Date.now();
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);
633
+ }, _AccountsController_getInternalAccountFromAddressAndType = function _AccountsController_getInternalAccountFromAddressAndType(address, keyring) {
634
+ if ((0, utils_2.isSnapKeyringType)(keyring.type)) {
635
+ const snapKeyring = __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_getSnapKeyring).call(this);
664
636
  // We need the Snap keyring to retrieve the account from its address.
665
- if (!keyring) {
637
+ if (!snapKeyring) {
666
638
  return undefined;
667
639
  }
668
640
  // This might be undefined if the Snap deleted the account before
669
641
  // reaching that point.
670
- return keyring.getAccountByAddress(address);
642
+ const account = snapKeyring.getAccountByAddress(address);
643
+ if (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
+ // We need to type cast the `account` cause it's now typed as
659
+ // a "HD Snap account" which as very specific options (which
660
+ // are not typed the same way on `KeyringAccountOptions`.
661
+ account.options = options;
662
+ }
663
+ }
664
+ return account;
671
665
  }
672
- return __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_generateInternalAccountForNonSnapAccount).call(this, address, type);
666
+ return __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_getInternalAccountForNonSnapAccount).call(this, address, keyring);
673
667
  }, _AccountsController_handleOnMultichainNetworkDidChange = function _AccountsController_handleOnMultichainNetworkDidChange(id) {
674
668
  let accountId;
675
669
  // We only support non-EVM Caip chain IDs at the moment. Ex Solana and Bitcoin
@@ -691,9 +685,6 @@ async function _AccountsController_listNormalAccounts() {
691
685
  currentState.internalAccounts.selectedAccount = accountId;
692
686
  });
693
687
  // 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;
697
688
  }, _AccountsController_subscribeToMessageEvents = function _AccountsController_subscribeToMessageEvents() {
698
689
  this.messagingSystem.subscribe('SnapController:stateChange', (snapStateState) => __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_handleOnSnapStateChange).call(this, snapStateState));
699
690
  this.messagingSystem.subscribe('KeyringController:stateChange', (keyringState) => __classPrivateFieldGet(this, _AccountsController_instances, "m", _AccountsController_handleOnKeyringStateChange).call(this, keyringState));