@metamask-previews/keyring-controller 25.1.1-preview-ce9be8b82 → 25.2.0-preview-373edc9

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
@@ -7,10 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [25.2.0]
11
+
10
12
  ### Added
11
13
 
12
14
  - Added `filter` selector variant to `withKeyring` ([#8348](https://github.com/MetaMask/core/pull/8348))
13
15
  - `KeyringSelector` now accepts `{ filter: ({ keyring, metadata }) => boolean }`, which selects the first keyring for which the predicate returns `true`.
16
+ - Add `isKeyringNotFoundError` ([#8351](https://github.com/MetaMask/core/pull/8351))
17
+ - This function can be used when trying to access a non-existing keyring using `withKeyring`.
18
+ - Added `withKeyringUnsafe` action ([#8358](https://github.com/MetaMask/core/pull/8358))
19
+ - This new variant of `withKeyring` allows to fetch a keyring instance the same way.
20
+ - Mutations are not allowed and won't be replicated in the vault.
21
+ - Can be used to read immutable data safely.
22
+ - Add `KeyringTypes.money` enum value ([#8360](https://github.com/MetaMask/core/pull/8360))
14
23
 
15
24
  ## [25.1.1]
16
25
 
@@ -943,7 +952,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
943
952
 
944
953
  All changes listed after this point were applied to this package following the monorepo conversion.
945
954
 
946
- [Unreleased]: https://github.com/MetaMask/core/compare/@metamask/keyring-controller@25.1.1...HEAD
955
+ [Unreleased]: https://github.com/MetaMask/core/compare/@metamask/keyring-controller@25.2.0...HEAD
956
+ [25.2.0]: https://github.com/MetaMask/core/compare/@metamask/keyring-controller@25.1.1...@metamask/keyring-controller@25.2.0
947
957
  [25.1.1]: https://github.com/MetaMask/core/compare/@metamask/keyring-controller@25.1.0...@metamask/keyring-controller@25.1.1
948
958
  [25.1.0]: https://github.com/MetaMask/core/compare/@metamask/keyring-controller@25.0.0...@metamask/keyring-controller@25.1.0
949
959
  [25.0.0]: https://github.com/MetaMask/core/compare/@metamask/keyring-controller@24.0.0...@metamask/keyring-controller@25.0.0
@@ -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_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;
39
+ var _KeyringController_instances, _KeyringController_controllerOperationMutex, _KeyringController_vaultOperationMutex, _KeyringController_keyringBuilders, _KeyringController_encryptor, _KeyringController_keyrings, _KeyringController_unsupportedKeyrings, _KeyringController_encryptionKey, _KeyringController_getKeyringForAccount, _KeyringController_findKeyringIndexForAccount, _KeyringController_assertNoUnsafeDirectKeyringAccess, _KeyringController_registerMessageHandlers, _KeyringController_selectKeyring, _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");
@@ -69,6 +69,7 @@ var KeyringTypes;
69
69
  KeyringTypes["ledger"] = "Ledger Hardware";
70
70
  KeyringTypes["lattice"] = "Lattice Hardware";
71
71
  KeyringTypes["snap"] = "Snap Keyring";
72
+ KeyringTypes["money"] = "Money Keyring";
72
73
  /* eslint-enable @typescript-eslint/naming-convention */
73
74
  })(KeyringTypes || (exports.KeyringTypes = KeyringTypes = {}));
74
75
  /**
@@ -526,19 +527,14 @@ class KeyringController extends base_controller_1.BaseController {
526
527
  */
527
528
  async getKeyringForAccount(account) {
528
529
  __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_assertIsUnlocked).call(this);
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;
530
+ const keyring = await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_getKeyringForAccount).call(this, account);
531
+ if (keyring) {
532
+ return keyring;
532
533
  }
533
- // Adding more info to the error
534
- let errorInfo = '';
535
534
  if (__classPrivateFieldGet(this, _KeyringController_keyrings, "f").length === 0) {
536
- errorInfo = 'There are no keyrings';
535
+ throw new errors_1.KeyringControllerError(constants_1.KeyringControllerErrorMessage.NoKeyring);
537
536
  }
538
- else {
539
- errorInfo = 'There are keyrings, but none match the address';
540
- }
541
- throw new errors_1.KeyringControllerError(`${constants_1.KeyringControllerErrorMessage.NoKeyring}. Error info: ${errorInfo}`);
537
+ throw new errors_1.KeyringControllerError(constants_1.KeyringControllerErrorMessage.KeyringNotFound);
542
538
  }
543
539
  /**
544
540
  * Returns all keyrings of the given type.
@@ -961,39 +957,66 @@ class KeyringController extends base_controller_1.BaseController {
961
957
  }) {
962
958
  __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_assertIsUnlocked).call(this);
963
959
  return __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_persistOrRollback).call(this, async () => {
964
- let keyring;
965
- if ('address' in selector) {
966
- keyring = (await this.getKeyringForAccount(selector.address));
967
- }
968
- else if ('type' in selector) {
969
- keyring = this.getKeyringsByType(selector.type)[selector.index ?? 0];
970
- if (!keyring && options.createIfMissing) {
971
- keyring = (await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_newKeyring).call(this, selector.type, options.createWithData));
972
- }
973
- }
974
- else if ('id' in selector) {
975
- keyring = __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_getKeyringById).call(this, selector.id);
976
- }
977
- else if ('filter' in selector) {
978
- keyring = __classPrivateFieldGet(this, _KeyringController_keyrings, "f").find(({ keyring: filteredKeyring, metadata }) => selector.filter(filteredKeyring, metadata))?.keyring;
960
+ let keyring = await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_selectKeyring).call(this, selector);
961
+ if (!keyring && 'type' in selector && options.createIfMissing) {
962
+ keyring = (await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_newKeyring).call(this, selector.type, options.createWithData));
979
963
  }
980
964
  if (!keyring) {
981
965
  throw new errors_1.KeyringControllerError(constants_1.KeyringControllerErrorMessage.KeyringNotFound);
982
966
  }
983
- const result = await operation({
967
+ return __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_assertNoUnsafeDirectKeyringAccess).call(this, await operation({
984
968
  keyring,
985
969
  metadata: __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_getKeyringMetadata).call(this, keyring),
986
- });
987
- if (Object.is(result, keyring)) {
988
- // Access to a keyring instance outside of controller safeguards
989
- // should be discouraged, as it can lead to unexpected behavior.
990
- // This error is thrown to prevent consumers using `withKeyring`
991
- // as a way to get a reference to a keyring instance.
992
- throw new errors_1.KeyringControllerError(constants_1.KeyringControllerErrorMessage.UnsafeDirectKeyringAccess);
993
- }
994
- return result;
970
+ }), keyring);
995
971
  });
996
972
  }
973
+ /**
974
+ * Select a keyring and execute the given operation with the selected
975
+ * keyring, **without** acquiring the controller's mutual exclusion lock.
976
+ *
977
+ * ## When to use this method
978
+ *
979
+ * This method is an escape hatch for read-only access to keyring data that
980
+ * is immutable once the keyring is initialized. A typical safe use case is
981
+ * reading the `mnemonic` from an `HdKeyring`: the mnemonic is set during
982
+ * `deserialize()` and never mutated afterwards, so it can safely be read
983
+ * without holding the lock.
984
+ *
985
+ * ## Why it is "unsafe"
986
+ *
987
+ * The "unsafe" designation mirrors the semantics of `unsafe { }` blocks in
988
+ * Rust: the method itself does not enforce thread-safety guarantees. By
989
+ * calling this method the **caller** explicitly takes responsibility for
990
+ * ensuring that:
991
+ *
992
+ * - The operation is **read-only** — no state is mutated.
993
+ * - The data being read is **immutable** after the keyring is initialized,
994
+ * so concurrent locked operations cannot alter it while this callback
995
+ * runs.
996
+ *
997
+ * Do **not** use this method to:
998
+ * - Mutate keyring state (add accounts, sign, etc.) — use `withKeyring`.
999
+ * - Read mutable fields that could change during concurrent operations.
1000
+ *
1001
+ * @param selector - Keyring selector object.
1002
+ * @param operation - Read-only function to execute with the selected keyring.
1003
+ * @returns Promise resolving to the result of the function execution.
1004
+ * @template SelectedKeyring - The type of the selected keyring.
1005
+ * @template CallbackResult - The type of the value resolved by the callback function.
1006
+ */
1007
+ async withKeyringUnsafe(selector, operation) {
1008
+ __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_assertIsUnlocked).call(this);
1009
+ const keyring = await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_selectKeyring).call(this, selector);
1010
+ if (!keyring) {
1011
+ throw new errors_1.KeyringControllerError(constants_1.KeyringControllerErrorMessage.KeyringNotFound);
1012
+ }
1013
+ // Even if this method is "unsafe", we still want to prevent returning
1014
+ // the keyring directly.
1015
+ return __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_assertNoUnsafeDirectKeyringAccess).call(this, await operation({
1016
+ keyring,
1017
+ metadata: __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_getKeyringMetadata).call(this, keyring),
1018
+ }), keyring);
1019
+ }
997
1020
  async getAccountKeyringType(account) {
998
1021
  __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_assertIsUnlocked).call(this);
999
1022
  const keyring = (await this.getKeyringForAccount(account));
@@ -1001,11 +1024,27 @@ class KeyringController extends base_controller_1.BaseController {
1001
1024
  }
1002
1025
  }
1003
1026
  exports.KeyringController = KeyringController;
1004
- _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) {
1027
+ _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_getKeyringForAccount = async function _KeyringController_getKeyringForAccount(account) {
1028
+ __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_assertIsUnlocked).call(this);
1029
+ const keyringIndex = await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_findKeyringIndexForAccount).call(this, account);
1030
+ if (keyringIndex > -1) {
1031
+ return __classPrivateFieldGet(this, _KeyringController_keyrings, "f")[keyringIndex].keyring;
1032
+ }
1033
+ return undefined;
1034
+ }, _KeyringController_findKeyringIndexForAccount = async function _KeyringController_findKeyringIndexForAccount(account) {
1005
1035
  __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_assertIsUnlocked).call(this);
1006
1036
  const address = account.toLowerCase();
1007
1037
  const accountsPerKeyring = await Promise.all(__classPrivateFieldGet(this, _KeyringController_keyrings, "f").map(({ keyring }) => keyring.getAccounts()));
1008
1038
  return accountsPerKeyring.findIndex((accounts) => accounts.map((a) => a.toLowerCase()).includes(address));
1039
+ }, _KeyringController_assertNoUnsafeDirectKeyringAccess = function _KeyringController_assertNoUnsafeDirectKeyringAccess(value, keyring) {
1040
+ if (Object.is(value, keyring)) {
1041
+ // Access to a keyring instance outside of controller safeguards
1042
+ // should be discouraged, as it can lead to unexpected behavior.
1043
+ // This error is thrown to prevent consumers using `withKeyring`
1044
+ // as a way to get a reference to a keyring instance.
1045
+ throw new errors_1.KeyringControllerError(constants_1.KeyringControllerErrorMessage.UnsafeDirectKeyringAccess);
1046
+ }
1047
+ return value;
1009
1048
  }, _KeyringController_registerMessageHandlers = function _KeyringController_registerMessageHandlers() {
1010
1049
  this.messenger.registerActionHandler(`${name}:signMessage`, this.signMessage.bind(this));
1011
1050
  this.messenger.registerActionHandler(`${name}:signEip7702Authorization`, this.signEip7702Authorization.bind(this));
@@ -1022,10 +1061,34 @@ _KeyringController_controllerOperationMutex = new WeakMap(), _KeyringController_
1022
1061
  this.messenger.registerActionHandler(`${name}:signUserOperation`, this.signUserOperation.bind(this));
1023
1062
  this.messenger.registerActionHandler(`${name}:addNewAccount`, this.addNewAccount.bind(this));
1024
1063
  this.messenger.registerActionHandler(`${name}:withKeyring`, this.withKeyring.bind(this));
1064
+ this.messenger.registerActionHandler(`${name}:withKeyringUnsafe`, this.withKeyringUnsafe.bind(this));
1025
1065
  this.messenger.registerActionHandler(`${name}:addNewKeyring`, this.addNewKeyring.bind(this));
1026
1066
  this.messenger.registerActionHandler(`${name}:createNewVaultAndKeychain`, this.createNewVaultAndKeychain.bind(this));
1027
1067
  this.messenger.registerActionHandler(`${name}:createNewVaultAndRestore`, this.createNewVaultAndRestore.bind(this));
1028
1068
  this.messenger.registerActionHandler(`${name}:removeAccount`, this.removeAccount.bind(this));
1069
+ }, _KeyringController_selectKeyring =
1070
+ /**
1071
+ * Select a keyring using a selector without acquiring the controller lock.
1072
+ *
1073
+ * @param selector - Keyring selector object.
1074
+ * @returns The selected keyring, or `undefined` if no match is found.
1075
+ * @template SelectedKeyring - The expected type of the selected keyring.
1076
+ */
1077
+ async function _KeyringController_selectKeyring(selector) {
1078
+ let keyring;
1079
+ if ('address' in selector) {
1080
+ keyring = (await __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_getKeyringForAccount).call(this, selector.address));
1081
+ }
1082
+ else if ('type' in selector) {
1083
+ keyring = this.getKeyringsByType(selector.type)[selector.index ?? 0];
1084
+ }
1085
+ else if ('id' in selector) {
1086
+ keyring = __classPrivateFieldGet(this, _KeyringController_instances, "m", _KeyringController_getKeyringById).call(this, selector.id);
1087
+ }
1088
+ else if ('filter' in selector) {
1089
+ keyring = __classPrivateFieldGet(this, _KeyringController_keyrings, "f").find(({ keyring: filteredKeyring, metadata }) => selector.filter(filteredKeyring, metadata))?.keyring;
1090
+ }
1091
+ return keyring;
1029
1092
  }, _KeyringController_getKeyringById = function _KeyringController_getKeyringById(keyringId) {
1030
1093
  return __classPrivateFieldGet(this, _KeyringController_keyrings, "f").find(({ metadata }) => metadata.id === keyringId)
1031
1094
  ?.keyring;