@metamask-previews/keyring-controller 25.1.0-preview-6ae8a59c → 25.1.0-preview-4888150

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
@@ -21,6 +21,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
21
21
  ### Changed
22
22
 
23
23
  - Upgrade `@metamask/utils` from `^11.8.1` to `^11.9.0` ([#7511](https://github.com/MetaMask/core/pull/7511))
24
+ - Prevent the accidental removal of unrelated empty keyrings when deleting an account ([#7670](https://github.com/MetaMask/core/pull/7670))
25
+ - Empty keyrings were removed during account deletion, regardless of which account was being targeted.
26
+
27
+ ### Fixed
28
+
29
+ - Fixed a bug where `removeAccount` would not prevent deletion of the primary keyring because of missing address normalization ([#7670](https://github.com/MetaMask/core/pull/7670))
24
30
 
25
31
  ## [25.0.0]
26
32
 
@@ -36,7 +36,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
36
36
  var __importDefault = (this && this.__importDefault) || function (mod) {
37
37
  return (mod && mod.__esModule) ? mod : { "default": mod };
38
38
  };
39
- var _KeyringController_instances, _KeyringController_controllerOperationMutex, _KeyringController_vaultOperationMutex, _KeyringController_keyringBuilders, _KeyringController_encryptor, _KeyringController_keyrings, _KeyringController_unsupportedKeyrings, _KeyringController_encryptionKey, _KeyringController_registerMessageHandlers, _KeyringController_getKeyringById, _KeyringController_getKeyringByIdOrDefault, _KeyringController_getKeyringMetadata, _KeyringController_getKeyringBuilderForType, _KeyringController_createNewVaultWithKeyring, _KeyringController_deriveAndSetEncryptionKey, _KeyringController_setEncryptionKey, _KeyringController_verifySeedPhrase, _KeyringController_getUpdatedKeyrings, _KeyringController_getSerializedKeyrings, _KeyringController_getSessionState, _KeyringController_restoreSerializedKeyrings, _KeyringController_unlockKeyrings, _KeyringController_updateVault, _KeyringController_isNewEncryptionAvailable, _KeyringController_getAccountsFromKeyrings, _KeyringController_createKeyringWithFirstAccount, _KeyringController_newKeyring, _KeyringController_createKeyring, _KeyringController_clearKeyrings, _KeyringController_restoreKeyring, _KeyringController_destroyKeyring, _KeyringController_removeEmptyKeyrings, _KeyringController_assertNoDuplicateAccounts, _KeyringController_setUnlocked, _KeyringController_assertIsUnlocked, _KeyringController_persistOrRollback, _KeyringController_withRollback, _KeyringController_assertControllerMutexIsLocked, _KeyringController_withControllerLock, _KeyringController_withVaultLock;
39
+ var _KeyringController_instances, _KeyringController_controllerOperationMutex, _KeyringController_vaultOperationMutex, _KeyringController_keyringBuilders, _KeyringController_encryptor, _KeyringController_keyrings, _KeyringController_unsupportedKeyrings, _KeyringController_encryptionKey, _KeyringController_findKeyringIndexForAccount, _KeyringController_registerMessageHandlers, _KeyringController_getKeyringById, _KeyringController_getKeyringByIdOrDefault, _KeyringController_getKeyringMetadata, _KeyringController_getKeyringBuilderForType, _KeyringController_createNewVaultWithKeyring, _KeyringController_deriveAndSetEncryptionKey, _KeyringController_setEncryptionKey, _KeyringController_verifySeedPhrase, _KeyringController_getUpdatedKeyrings, _KeyringController_getSerializedKeyrings, _KeyringController_getSessionState, _KeyringController_restoreSerializedKeyrings, _KeyringController_unlockKeyrings, _KeyringController_updateVault, _KeyringController_isNewEncryptionAvailable, _KeyringController_getAccountsFromKeyrings, _KeyringController_createKeyringWithFirstAccount, _KeyringController_newKeyring, _KeyringController_createKeyring, _KeyringController_clearKeyrings, _KeyringController_restoreKeyring, _KeyringController_destroyKeyring, _KeyringController_assertNoDuplicateAccounts, _KeyringController_setUnlocked, _KeyringController_assertIsUnlocked, _KeyringController_persistOrRollback, _KeyringController_withRollback, _KeyringController_assertControllerMutexIsLocked, _KeyringController_withControllerLock, _KeyringController_withVaultLock;
40
40
  Object.defineProperty(exports, "__esModule", { value: true });
41
41
  exports.KeyringController = exports.getDefaultKeyringState = exports.keyringBuilderFactory = exports.SignTypedDataVersion = exports.AccountImportStrategy = exports.isCustodyKeyring = exports.KeyringTypes = void 0;
42
42
  const util_1 = require("@ethereumjs/util");
@@ -526,23 +526,16 @@ class KeyringController extends base_controller_1.BaseController {
526
526
  */
527
527
  async getKeyringForAccount(account) {
528
528
  __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_assertIsUnlocked).call(this);
529
- const address = normalize(account);
530
- const candidates = await Promise.all(__classPrivateFieldGet(this, _KeyringController_keyrings, "f").map(async ({ keyring }) => {
531
- return [keyring, await keyring.getAccounts()];
532
- }));
533
- const winners = candidates.filter((candidate) => {
534
- const accounts = candidate[1].map(normalize);
535
- return accounts.includes(address);
536
- });
537
- if (winners.length && winners[0]?.length) {
538
- return winners[0][0];
529
+ const keyringIndex = await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_findKeyringIndexForAccount).call(this, account);
530
+ if (keyringIndex > -1) {
531
+ return __classPrivateFieldGet(this, _KeyringController_keyrings, "f")[keyringIndex].keyring;
539
532
  }
540
533
  // Adding more info to the error
541
534
  let errorInfo = '';
542
- if (!candidates.length) {
535
+ if (__classPrivateFieldGet(this, _KeyringController_keyrings, "f").length === 0) {
543
536
  errorInfo = 'There are no keyrings';
544
537
  }
545
- else if (!winners.length) {
538
+ else {
546
539
  errorInfo = 'There are keyrings, but none match the address';
547
540
  }
548
541
  throw new errors_1.KeyringControllerError(`${constants_1.KeyringControllerErrorMessage.NoKeyring}. Error info: ${errorInfo}`);
@@ -645,8 +638,11 @@ class KeyringController extends base_controller_1.BaseController {
645
638
  async removeAccount(address) {
646
639
  __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_assertIsUnlocked).call(this);
647
640
  await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_persistOrRollback).call(this, async () => {
648
- const keyring = (await this.getKeyringForAccount(address));
649
- const keyringIndex = this.state.keyrings.findIndex((kr) => kr.accounts.includes(address));
641
+ const keyringIndex = await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_findKeyringIndexForAccount).call(this, address);
642
+ if (keyringIndex === -1) {
643
+ throw new errors_1.KeyringControllerError(constants_1.KeyringControllerErrorMessage.NoKeyring);
644
+ }
645
+ const { keyring } = __classPrivateFieldGet(this, _KeyringController_keyrings, "f")[keyringIndex];
650
646
  const isPrimaryKeyring = keyringIndex === 0;
651
647
  const shouldRemoveKeyring = (await keyring.getAccounts()).length === 1;
652
648
  // Primary keyring should never be removed, so we need to keep at least one account in it
@@ -657,14 +653,17 @@ class KeyringController extends base_controller_1.BaseController {
657
653
  if (!keyring.removeAccount) {
658
654
  throw new errors_1.KeyringControllerError(constants_1.KeyringControllerErrorMessage.UnsupportedRemoveAccount);
659
655
  }
660
- // The `removeAccount` method of snaps keyring is async. We have to update
661
- // the interface of the other keyrings to be async as well.
662
- // FIXME: We do cast to `Hex` to makes the type checker happy here, and
663
- // because `Keyring<State>.removeAccount` requires address to be `Hex`. Those
664
- // type would need to be updated for a full non-EVM support.
665
- keyring.removeAccount(address);
656
+ // FIXME #1: We do cast to `Hex` to make the type checker happy here, and
657
+ // because `Keyring<State>.removeAccount` requires address to be `Hex`.
658
+ // Those types would need to be updated for a full non-EVM support.
659
+ //
660
+ // FIXME #2: The `removeAccount` method of snaps keyring is async. We have
661
+ // to update the interface of the other keyrings to be async as well.
662
+ // eslint-disable-next-line @typescript-eslint/await-thenable
663
+ await keyring.removeAccount(address);
666
664
  if (shouldRemoveKeyring) {
667
- await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_removeEmptyKeyrings).call(this);
665
+ __classPrivateFieldGet(this, _KeyringController_keyrings, "f").splice(keyringIndex, 1);
666
+ await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_destroyKeyring).call(this, keyring);
668
667
  }
669
668
  });
670
669
  this.messenger.publish(`${name}:accountRemoved`, address);
@@ -999,7 +998,12 @@ class KeyringController extends base_controller_1.BaseController {
999
998
  }
1000
999
  }
1001
1000
  exports.KeyringController = KeyringController;
1002
- _KeyringController_controllerOperationMutex = new WeakMap(), _KeyringController_vaultOperationMutex = new WeakMap(), _KeyringController_keyringBuilders = new WeakMap(), _KeyringController_encryptor = new WeakMap(), _KeyringController_keyrings = new WeakMap(), _KeyringController_unsupportedKeyrings = new WeakMap(), _KeyringController_encryptionKey = new WeakMap(), _KeyringController_instances = new WeakSet(), _KeyringController_registerMessageHandlers = function _KeyringController_registerMessageHandlers() {
1001
+ _KeyringController_controllerOperationMutex = new WeakMap(), _KeyringController_vaultOperationMutex = new WeakMap(), _KeyringController_keyringBuilders = new WeakMap(), _KeyringController_encryptor = new WeakMap(), _KeyringController_keyrings = new WeakMap(), _KeyringController_unsupportedKeyrings = new WeakMap(), _KeyringController_encryptionKey = new WeakMap(), _KeyringController_instances = new WeakSet(), _KeyringController_findKeyringIndexForAccount = async function _KeyringController_findKeyringIndexForAccount(account) {
1002
+ __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_assertIsUnlocked).call(this);
1003
+ const address = account.toLowerCase();
1004
+ const accountsPerKeyring = await Promise.all(__classPrivateFieldGet(this, _KeyringController_keyrings, "f").map(({ keyring }) => keyring.getAccounts()));
1005
+ return accountsPerKeyring.findIndex((accounts) => accounts.map((a) => a.toLowerCase()).includes(address));
1006
+ }, _KeyringController_registerMessageHandlers = function _KeyringController_registerMessageHandlers() {
1003
1007
  this.messenger.registerActionHandler(`${name}:signMessage`, this.signMessage.bind(this));
1004
1008
  this.messenger.registerActionHandler(`${name}:signEip7702Authorization`, this.signEip7702Authorization.bind(this));
1005
1009
  this.messenger.registerActionHandler(`${name}:signPersonalMessage`, this.signPersonalMessage.bind(this));
@@ -1464,29 +1468,6 @@ async function _KeyringController_restoreKeyring(serialized) {
1464
1468
  */
1465
1469
  async function _KeyringController_destroyKeyring(keyring) {
1466
1470
  await keyring.destroy?.();
1467
- }, _KeyringController_removeEmptyKeyrings =
1468
- /**
1469
- * Remove empty keyrings.
1470
- *
1471
- * Loops through the keyrings and removes the ones with empty accounts
1472
- * (usually after removing the last / only account) from a keyring.
1473
- */
1474
- async function _KeyringController_removeEmptyKeyrings() {
1475
- __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_assertControllerMutexIsLocked).call(this);
1476
- const validKeyrings = [];
1477
- // Since getAccounts returns a Promise
1478
- // We need to wait to hear back form each keyring
1479
- // in order to decide which ones are now valid (accounts.length > 0)
1480
- await Promise.all(__classPrivateFieldGet(this, _KeyringController_keyrings, "f").map(async ({ keyring, metadata }) => {
1481
- const accounts = await keyring.getAccounts();
1482
- if (accounts.length > 0) {
1483
- validKeyrings.push({ keyring, metadata });
1484
- }
1485
- else {
1486
- await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_destroyKeyring).call(this, keyring);
1487
- }
1488
- }));
1489
- __classPrivateFieldSet(this, _KeyringController_keyrings, validKeyrings, "f");
1490
1471
  }, _KeyringController_assertNoDuplicateAccounts =
1491
1472
  /**
1492
1473
  * Assert that there are no duplicate accounts in the keyrings.