@metamask-previews/eth-snap-keyring 9.0.0-9c70d70 → 10.0.0-157b637

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,6 +7,41 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [10.0.0]
11
+
12
+ ### Changed
13
+
14
+ - **BREAKING:** Use `CaipAccountId` for `ResolvedAccountAddress.address` ([#186](https://github.com/MetaMask/accounts/pull/186))
15
+ - This was missing from SIP-26, but we expect this address to be CAIP-10 compliant.
16
+ - Bump `@metamask/keyring-api` from `^16.1.0` to `^17.0.0` ([#192](https://github.com/MetaMask/accounts/pull/192))
17
+ - Rename `ControllerMessenger` to `Messenger` ([#185](https://github.com/MetaMask/accounts/pull/185))
18
+ - Bump `@metamask/snaps-controllers` dependency from `^9.10.0` to `^9.18.0` ([#177](https://github.com/MetaMask/accounts/pull/177))
19
+ - Bump `@metamask/snaps-sdk` dependency from `^6.7.0` to `^6.16.0` ([#177](https://github.com/MetaMask/accounts/pull/177))
20
+ - Bump `@metamask/snaps-utils` dependency from `^8.3.0` to `^8.9.1` ([#177](https://github.com/MetaMask/accounts/pull/177))
21
+
22
+ ## [9.1.1]
23
+
24
+ ### Changed
25
+
26
+ - Bump `@metamask/keyring-api` from `^16.0.0` to `^16.1.0` ([#176](https://github.com/MetaMask/accounts/pull/176))
27
+
28
+ ## [9.1.0]
29
+
30
+ ### Added
31
+
32
+ - Add `submitRequest` and `resolveAccountAddress` methods ([#157](https://github.com/MetaMask/accounts/pull/157))
33
+ - Those new methods are required by the new signing request routing system (see SIP-26).
34
+
35
+ ### Changed
36
+
37
+ - Bump `@metamask/keyring-api` from `^15.0.0` to `^16.0.0` ([#172](https://github.com/MetaMask/accounts/pull/172))
38
+ - Bump `@metamask/utils` from `^11.0.1` to `^11.1.0` ([#167](https://github.com/MetaMask/accounts/pull/167))
39
+
40
+ ### Fixed
41
+
42
+ - Fix async Snap request leak ([#171](https://github.com/MetaMask/accounts/pull/171))
43
+ - There was a leak with the deferred promises when an asynchronous request was not allowed.
44
+
10
45
  ## [9.0.0]
11
46
 
12
47
  ### Added
@@ -429,7 +464,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
429
464
 
430
465
  - Initial release.
431
466
 
432
- [Unreleased]: https://github.com/MetaMask/accounts/compare/@metamask/eth-snap-keyring@9.0.0...HEAD
467
+ [Unreleased]: https://github.com/MetaMask/accounts/compare/@metamask/eth-snap-keyring@10.0.0...HEAD
468
+ [10.0.0]: https://github.com/MetaMask/accounts/compare/@metamask/eth-snap-keyring@9.1.1...@metamask/eth-snap-keyring@10.0.0
469
+ [9.1.1]: https://github.com/MetaMask/accounts/compare/@metamask/eth-snap-keyring@9.1.0...@metamask/eth-snap-keyring@9.1.1
470
+ [9.1.0]: https://github.com/MetaMask/accounts/compare/@metamask/eth-snap-keyring@9.0.0...@metamask/eth-snap-keyring@9.1.0
433
471
  [9.0.0]: https://github.com/MetaMask/accounts/compare/@metamask/eth-snap-keyring@8.1.1...@metamask/eth-snap-keyring@9.0.0
434
472
  [8.1.1]: https://github.com/MetaMask/accounts/compare/@metamask/eth-snap-keyring@8.1.0...@metamask/eth-snap-keyring@8.1.1
435
473
  [8.1.0]: https://github.com/MetaMask/accounts/compare/@metamask/eth-snap-keyring@8.0.0...@metamask/eth-snap-keyring@8.1.0
@@ -151,6 +151,17 @@ class SnapIdMap {
151
151
  has(snapId, key) {
152
152
  return this.get(snapId, key) !== undefined;
153
153
  }
154
+ /**
155
+ * Checks if a snap ID exists in the map.
156
+ *
157
+ * @param snapId - Snap ID present in the value to check.
158
+ * @returns `true` if the snap ID is present in the map, `false` otherwise.
159
+ */
160
+ hasSnapId(snapId) {
161
+ // We could use a reverse-mapping to map Snap ID to their actual key too, but
162
+ // for now, this will do the trick.
163
+ return [...__classPrivateFieldGet(this, _SnapIdMap_map, "f").values()].some((value) => value.snapId === snapId);
164
+ }
154
165
  /**
155
166
  * Deletes a key from the map.
156
167
  *
@@ -1 +1 @@
1
- {"version":3,"file":"SnapIdMap.cjs","sourceRoot":"","sources":["../src/SnapIdMap.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAEA,iEAA0D;AAE1D;;GAEG;AACH,MAAa,kBAAmB,SAAQ,KAAK;IAW3C;;;;;OAKG;IACH,YAAY,MAAc,EAAE,GAAW;QACrC,KAAK,CAAC,SAAS,MAAM,4BAA4B,GAAG,GAAG,CAAC,CAAC;QACzD,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;QACjC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;IACjB,CAAC;CACF;AAvBD,gDAuBC;AAED;;;;;;GAMG;AACH,MAAa,SAAS;IAGpB;;;;;;;;;;;;;;;OAeG;IACH,YAAY,QAA6C;QAlBhD,iCAAgC;QAmBvC,uBAAA,IAAI,kBAAQ,IAAI,uCAAkB,CAAC,QAAQ,CAAC,MAAA,CAAC;IAC/C,CAAC;IAED;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,QAAQ;QACN,OAAO,uBAAA,IAAI,sBAAK,CAAC,QAAQ,EAAE,CAAC;IAC9B,CAAC;IAED;;;;;;;;;;;;;;;OAeG;IACH,MAAM,CAAC,UAAU,CACf,GAA0B;QAE1B,OAAO,IAAI,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5C,CAAC;IAED;;;;;;;;;;;;;;;;;;;OAmBG;IACH,GAAG,CAAC,MAAc,EAAE,GAAW;QAC7B,MAAM,KAAK,GAAG,uBAAA,IAAI,sBAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACjC,OAAO,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;IACtD,CAAC;IAED;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,GAAG,CAAC,MAAc,EAAE,GAAW;QAC7B,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,SAAS,CAAC;IAC7C,CAAC;IAED;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,MAAM,CAAC,MAAc,EAAE,GAAW;QAChC,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,uBAAA,IAAI,sBAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACxD,CAAC;IAED,4CAA4C;IAC5C;;;;;;;;;;;;;;;;;OAiBG;IACH,2CAA2C;IAC3C,GAAG,CAAC,GAAW,EAAE,KAAY;QAC3B,uEAAuE;QACvE,sEAAsE;QACtE,sCAAsC;QACtC,IAAI,uBAAA,IAAI,sBAAK,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC;YACvD,MAAM,IAAI,kBAAkB,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAClD,CAAC;QACD,uBAAA,IAAI,sBAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC1B,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;;;;;;;;;;;;;;;OAmBG;IACH,MAAM;QACJ,OAAO,uBAAA,IAAI,sBAAK,CAAC,MAAM,EAAE,CAAC;IAC5B,CAAC;IAED;;;;OAIG;IACH,IAAI,IAAI;QACN,OAAO,uBAAA,IAAI,sBAAK,CAAC,IAAI,CAAC;IACxB,CAAC;CACF;AAhND,8BAgNC","sourcesContent":["import type { SnapId } from '@metamask/snaps-sdk';\n\nimport { CaseInsensitiveMap } from './CaseInsensitiveMap';\n\n/**\n * Error thrown when an invalid Snap ID is encountered.\n */\nexport class InvalidSnapIdError extends Error {\n /**\n * The ID of the Snap that caused the error.\n */\n snapId: SnapId;\n\n /**\n * The key of the element that caused the error.\n */\n key: string;\n\n /**\n * Creates an instance of `InvalidSnapIdError`.\n *\n * @param snapId - The invalid Snap ID.\n * @param key - The key associated with the invalid Snap ID.\n */\n constructor(snapId: SnapId, key: string) {\n super(`Snap \"${snapId}\" is not allowed to set \"${key}\"`);\n this.name = 'InvalidSnapIdError';\n this.snapId = snapId;\n this.key = key;\n }\n}\n\n/**\n * A map that associates a string key with a value that has a `snapId`\n * property. Note that the key is case-insensitive.\n *\n * The `snapId` property is used to ensure that only the Snap that added an\n * item to the map can modify or delete it.\n */\nexport class SnapIdMap<Value extends { snapId: SnapId }> {\n readonly #map: CaseInsensitiveMap<Value>;\n\n /**\n * Creates a new `SnapIdMap` object.\n *\n * Example:\n *\n * ```ts\n * const items = [\n * ['foo', { snapId: '1', name: 'foo' }],\n * ['bar', { snapId: '1', name: 'bar' }],\n * ];\n * const map = new SnapIdMap(items);\n * ```\n *\n * @param iterable - An iterable object whose elements are key-value pairs.\n * Each key-value pair will be added to the new map.\n */\n constructor(iterable?: Iterable<readonly [string, Value]>) {\n this.#map = new CaseInsensitiveMap(iterable);\n }\n\n /**\n * Returns a plain object with the same key-value pairs as this map.\n *\n * Example:\n *\n * ```ts\n * const items = [\n * ['foo', { snapId: '1', name: 'foo' }],\n * ['bar', { snapId: '1', name: 'bar' }],\n * ];\n * const map = new SnapIdMap(items);\n * map.toObject();\n * // Returns\n * // {\n * // foo: { snapId: '1', name: 'foo' },\n * // bar: { snapId: '1', name: 'bar' },\n * // }\n * ```\n *\n * @returns A plain object with the same key-value pairs as this map.\n */\n toObject(): Record<string, Value> {\n return this.#map.toObject();\n }\n\n /**\n * Returns a new `SnapIdMap` object from an plain object.\n *\n * Example:\n *\n * ```ts\n * const obj = {\n * foo: { snapId: '1', name: 'foo' },\n * bar: { snapId: '1', name: 'bar' },\n * };\n * const map = SnapIdMap.fromObject(obj);\n * ```\n *\n * @param obj - A plain object whose elements will be added to the new map.\n * @returns A new `SnapIdMap` containing the elements of the given object.\n */\n static fromObject<Value extends { snapId: SnapId }>(\n obj: Record<string, Value>,\n ): SnapIdMap<Value> {\n return new SnapIdMap(Object.entries(obj));\n }\n\n /**\n * Gets a value from the map.\n *\n * If the given key is not present in the map or the Snap ID of the value is\n * different from the given Snap ID, returns `undefined`.\n *\n * Example:\n *\n * ```ts\n * const map = new SnapIdMap();\n * map.set('foo', { snapId: '1', name: 'foo' });\n * map.get('1', 'foo'); // Returns { snapId: '1', name: 'foo' }\n * map.get('2', 'foo'); // Returns `undefined`\n * map.get('1', 'bar'); // Returns `undefined`\n * ```\n *\n * @param snapId - Snap ID present in the value to get.\n * @param key - Key of the element to get.\n * @returns The value associated with the given key and Snap ID.\n */\n get(snapId: SnapId, key: string): Value | undefined {\n const value = this.#map.get(key);\n return value?.snapId === snapId ? value : undefined;\n }\n\n /**\n * Checks if a key is present in the map.\n *\n * If the given key is not present in the map or the Snap ID of the value is\n * different from the given Snap ID, returns `false`.\n *\n * Example:\n *\n * ```ts\n * const map = new SnapIdMap();\n * map.set('foo', { snapId: '1', name: 'foo' });\n * map.has('1', 'foo'); // Returns `true`\n * map.has('2', 'foo'); // Returns `false`\n * map.has('1', 'bar'); // Returns `false`\n * ```\n *\n * @param snapId - Snap ID present in the value to check.\n * @param key - Key of the element to check.\n * @returns `true` if the key is present in the map and the Snap ID of the\n * value is equal to the given Snap ID, `false` otherwise.\n */\n has(snapId: SnapId, key: string): boolean {\n return this.get(snapId, key) !== undefined;\n }\n\n /**\n * Deletes a key from the map.\n *\n * If the given key is not present in the map or the Snap IDs don't match,\n * returns `false` and does nothing.\n *\n * Example:\n *\n * ```ts\n * const map = new SnapIdMap();\n * map.set('foo', { snapId: '1', name: 'foo' });\n * map.delete('2', 'foo'); // Returns `false`\n * map.delete('1', 'bar'); // Returns `false`\n * map.delete('1', 'foo'); // Returns `true`\n * ```\n *\n * @param snapId - Snap ID present in the value to delete.\n * @param key - Key of the element to delete.\n * @returns `true` if the key was present in the map and the Snap ID of the\n * value was equal to the given Snap ID, `false` otherwise.\n */\n delete(snapId: SnapId, key: string): boolean {\n return this.has(snapId, key) && this.#map.delete(key);\n }\n\n /* eslint-disable jsdoc/check-indentation */\n /**\n * Adds or updates a key-value pair in the map.\n *\n * Note that this method has a different behavior from the `Map.set`.\n *\n * - If the given key is not already present in the map, this method adds the\n * key-value pair to the map.\n *\n * - If the given key is already present in the map and the Snap IDs match,\n * this method updates the value associated with the key.\n *\n * - However, if the given key is already present in the map but the Snap IDs\n * do not match, this method throws an error.\n *\n * @param key - Key of the element to add or update.\n * @param value - Value of the element to add or update.\n * @returns The map itself.\n */\n /* eslint-enable jsdoc/check-indentation */\n set(key: string, value: Value): this {\n // If the key is present in the map but isn't associated with the given\n // Snap ID, it means that the item was added to the map by a different\n // Snap. In this case, throw an error.\n if (this.#map.has(key) && !this.has(value.snapId, key)) {\n throw new InvalidSnapIdError(value.snapId, key);\n }\n this.#map.set(key, value);\n return this;\n }\n\n /**\n * Returns an iterable of the values in the map.\n *\n * Example:\n *\n * ```ts\n * const map = new SnapIdMap([\n * ['foo', { snapId: '1', name: 'foo' }],\n * ['bar', { snapId: '1', name: 'bar' }],\n * ]);\n * const values = [...map.values()];\n * // Returns\n * // [\n * // { snapId: '1', name: 'foo' },\n * // { snapId: '1', name: 'bar' },\n * // ]\n * ```\n *\n * @returns An iterable of the values in the map.\n */\n values(): IterableIterator<Value> {\n return this.#map.values();\n }\n\n /**\n * Returns the number of key-value pairs in the map.\n *\n * @returns The number of key-value pairs in the map.\n */\n get size(): number {\n return this.#map.size;\n }\n}\n"]}
1
+ {"version":3,"file":"SnapIdMap.cjs","sourceRoot":"","sources":["../src/SnapIdMap.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SnapId } from '@metamask/snaps-sdk';\n\nimport { CaseInsensitiveMap } from './CaseInsensitiveMap';\n\n/**\n * Error thrown when an invalid Snap ID is encountered.\n */\nexport class InvalidSnapIdError extends Error {\n /**\n * The ID of the Snap that caused the error.\n */\n snapId: SnapId;\n\n /**\n * The key of the element that caused the error.\n */\n key: string;\n\n /**\n * Creates an instance of `InvalidSnapIdError`.\n *\n * @param snapId - The invalid Snap ID.\n * @param key - The key associated with the invalid Snap ID.\n */\n constructor(snapId: SnapId, key: string) {\n super(`Snap \"${snapId}\" is not allowed to set \"${key}\"`);\n this.name = 'InvalidSnapIdError';\n this.snapId = snapId;\n this.key = key;\n }\n}\n\n/**\n * A map that associates a string key with a value that has a `snapId`\n * property. Note that the key is case-insensitive.\n *\n * The `snapId` property is used to ensure that only the Snap that added an\n * item to the map can modify or delete it.\n */\nexport class SnapIdMap<Value extends { snapId: SnapId }> {\n readonly #map: CaseInsensitiveMap<Value>;\n\n /**\n * Creates a new `SnapIdMap` object.\n *\n * Example:\n *\n * ```ts\n * const items = [\n * ['foo', { snapId: '1', name: 'foo' }],\n * ['bar', { snapId: '1', name: 'bar' }],\n * ];\n * const map = new SnapIdMap(items);\n * ```\n *\n * @param iterable - An iterable object whose elements are key-value pairs.\n * Each key-value pair will be added to the new map.\n */\n constructor(iterable?: Iterable<readonly [string, Value]>) {\n this.#map = new CaseInsensitiveMap(iterable);\n }\n\n /**\n * Returns a plain object with the same key-value pairs as this map.\n *\n * Example:\n *\n * ```ts\n * const items = [\n * ['foo', { snapId: '1', name: 'foo' }],\n * ['bar', { snapId: '1', name: 'bar' }],\n * ];\n * const map = new SnapIdMap(items);\n * map.toObject();\n * // Returns\n * // {\n * // foo: { snapId: '1', name: 'foo' },\n * // bar: { snapId: '1', name: 'bar' },\n * // }\n * ```\n *\n * @returns A plain object with the same key-value pairs as this map.\n */\n toObject(): Record<string, Value> {\n return this.#map.toObject();\n }\n\n /**\n * Returns a new `SnapIdMap` object from an plain object.\n *\n * Example:\n *\n * ```ts\n * const obj = {\n * foo: { snapId: '1', name: 'foo' },\n * bar: { snapId: '1', name: 'bar' },\n * };\n * const map = SnapIdMap.fromObject(obj);\n * ```\n *\n * @param obj - A plain object whose elements will be added to the new map.\n * @returns A new `SnapIdMap` containing the elements of the given object.\n */\n static fromObject<Value extends { snapId: SnapId }>(\n obj: Record<string, Value>,\n ): SnapIdMap<Value> {\n return new SnapIdMap(Object.entries(obj));\n }\n\n /**\n * Gets a value from the map.\n *\n * If the given key is not present in the map or the Snap ID of the value is\n * different from the given Snap ID, returns `undefined`.\n *\n * Example:\n *\n * ```ts\n * const map = new SnapIdMap();\n * map.set('foo', { snapId: '1', name: 'foo' });\n * map.get('1', 'foo'); // Returns { snapId: '1', name: 'foo' }\n * map.get('2', 'foo'); // Returns `undefined`\n * map.get('1', 'bar'); // Returns `undefined`\n * ```\n *\n * @param snapId - Snap ID present in the value to get.\n * @param key - Key of the element to get.\n * @returns The value associated with the given key and Snap ID.\n */\n get(snapId: SnapId, key: string): Value | undefined {\n const value = this.#map.get(key);\n return value?.snapId === snapId ? value : undefined;\n }\n\n /**\n * Checks if a key is present in the map.\n *\n * If the given key is not present in the map or the Snap ID of the value is\n * different from the given Snap ID, returns `false`.\n *\n * Example:\n *\n * ```ts\n * const map = new SnapIdMap();\n * map.set('foo', { snapId: '1', name: 'foo' });\n * map.has('1', 'foo'); // Returns `true`\n * map.has('2', 'foo'); // Returns `false`\n * map.has('1', 'bar'); // Returns `false`\n * ```\n *\n * @param snapId - Snap ID present in the value to check.\n * @param key - Key of the element to check.\n * @returns `true` if the key is present in the map and the Snap ID of the\n * value is equal to the given Snap ID, `false` otherwise.\n */\n has(snapId: SnapId, key: string): boolean {\n return this.get(snapId, key) !== undefined;\n }\n\n /**\n * Checks if a snap ID exists in the map.\n *\n * @param snapId - Snap ID present in the value to check.\n * @returns `true` if the snap ID is present in the map, `false` otherwise.\n */\n hasSnapId(snapId: SnapId): boolean {\n // We could use a reverse-mapping to map Snap ID to their actual key too, but\n // for now, this will do the trick.\n return [...this.#map.values()].some((value) => value.snapId === snapId);\n }\n\n /**\n * Deletes a key from the map.\n *\n * If the given key is not present in the map or the Snap IDs don't match,\n * returns `false` and does nothing.\n *\n * Example:\n *\n * ```ts\n * const map = new SnapIdMap();\n * map.set('foo', { snapId: '1', name: 'foo' });\n * map.delete('2', 'foo'); // Returns `false`\n * map.delete('1', 'bar'); // Returns `false`\n * map.delete('1', 'foo'); // Returns `true`\n * ```\n *\n * @param snapId - Snap ID present in the value to delete.\n * @param key - Key of the element to delete.\n * @returns `true` if the key was present in the map and the Snap ID of the\n * value was equal to the given Snap ID, `false` otherwise.\n */\n delete(snapId: SnapId, key: string): boolean {\n return this.has(snapId, key) && this.#map.delete(key);\n }\n\n /* eslint-disable jsdoc/check-indentation */\n /**\n * Adds or updates a key-value pair in the map.\n *\n * Note that this method has a different behavior from the `Map.set`.\n *\n * - If the given key is not already present in the map, this method adds the\n * key-value pair to the map.\n *\n * - If the given key is already present in the map and the Snap IDs match,\n * this method updates the value associated with the key.\n *\n * - However, if the given key is already present in the map but the Snap IDs\n * do not match, this method throws an error.\n *\n * @param key - Key of the element to add or update.\n * @param value - Value of the element to add or update.\n * @returns The map itself.\n */\n /* eslint-enable jsdoc/check-indentation */\n set(key: string, value: Value): this {\n // If the key is present in the map but isn't associated with the given\n // Snap ID, it means that the item was added to the map by a different\n // Snap. In this case, throw an error.\n if (this.#map.has(key) && !this.has(value.snapId, key)) {\n throw new InvalidSnapIdError(value.snapId, key);\n }\n this.#map.set(key, value);\n return this;\n }\n\n /**\n * Returns an iterable of the values in the map.\n *\n * Example:\n *\n * ```ts\n * const map = new SnapIdMap([\n * ['foo', { snapId: '1', name: 'foo' }],\n * ['bar', { snapId: '1', name: 'bar' }],\n * ]);\n * const values = [...map.values()];\n * // Returns\n * // [\n * // { snapId: '1', name: 'foo' },\n * // { snapId: '1', name: 'bar' },\n * // ]\n * ```\n *\n * @returns An iterable of the values in the map.\n */\n values(): IterableIterator<Value> {\n return this.#map.values();\n }\n\n /**\n * Returns the number of key-value pairs in the map.\n *\n * @returns The number of key-value pairs in the map.\n */\n get size(): number {\n return this.#map.size;\n }\n}\n"]}
@@ -131,6 +131,13 @@ export declare class SnapIdMap<Value extends {
131
131
  * value is equal to the given Snap ID, `false` otherwise.
132
132
  */
133
133
  has(snapId: SnapId, key: string): boolean;
134
+ /**
135
+ * Checks if a snap ID exists in the map.
136
+ *
137
+ * @param snapId - Snap ID present in the value to check.
138
+ * @returns `true` if the snap ID is present in the map, `false` otherwise.
139
+ */
140
+ hasSnapId(snapId: SnapId): boolean;
134
141
  /**
135
142
  * Deletes a key from the map.
136
143
  *
@@ -1 +1 @@
1
- {"version":3,"file":"SnapIdMap.d.cts","sourceRoot":"","sources":["../src/SnapIdMap.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,4BAA4B;AAIlD;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;;;;OAKG;gBACS,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM;CAMxC;AAED;;;;;;GAMG;AACH,qBAAa,SAAS,CAAC,KAAK,SAAS;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE;;IAGrD;;;;;;;;;;;;;;;OAeG;gBACS,QAAQ,CAAC,EAAE,QAAQ,CAAC,SAAS,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAIzD;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,QAAQ,IAAI,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC;IAIjC;;;;;;;;;;;;;;;OAeG;IACH,MAAM,CAAC,UAAU,CAAC,KAAK,SAAS;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,EAChD,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,GACzB,SAAS,CAAC,KAAK,CAAC;IAInB;;;;;;;;;;;;;;;;;;;OAmBG;IACH,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS;IAKnD;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO;IAIzC;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO;IAK5C;;;;;;;;;;;;;;;;;OAiBG;IAEH,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI;IAWpC;;;;;;;;;;;;;;;;;;;OAmBG;IACH,MAAM,IAAI,gBAAgB,CAAC,KAAK,CAAC;IAIjC;;;;OAIG;IACH,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF"}
1
+ {"version":3,"file":"SnapIdMap.d.cts","sourceRoot":"","sources":["../src/SnapIdMap.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,4BAA4B;AAIlD;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;;;;OAKG;gBACS,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM;CAMxC;AAED;;;;;;GAMG;AACH,qBAAa,SAAS,CAAC,KAAK,SAAS;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE;;IAGrD;;;;;;;;;;;;;;;OAeG;gBACS,QAAQ,CAAC,EAAE,QAAQ,CAAC,SAAS,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAIzD;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,QAAQ,IAAI,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC;IAIjC;;;;;;;;;;;;;;;OAeG;IACH,MAAM,CAAC,UAAU,CAAC,KAAK,SAAS;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,EAChD,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,GACzB,SAAS,CAAC,KAAK,CAAC;IAInB;;;;;;;;;;;;;;;;;;;OAmBG;IACH,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS;IAKnD;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO;IAIzC;;;;;OAKG;IACH,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAMlC;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO;IAK5C;;;;;;;;;;;;;;;;;OAiBG;IAEH,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI;IAWpC;;;;;;;;;;;;;;;;;;;OAmBG;IACH,MAAM,IAAI,gBAAgB,CAAC,KAAK,CAAC;IAIjC;;;;OAIG;IACH,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF"}
@@ -131,6 +131,13 @@ export declare class SnapIdMap<Value extends {
131
131
  * value is equal to the given Snap ID, `false` otherwise.
132
132
  */
133
133
  has(snapId: SnapId, key: string): boolean;
134
+ /**
135
+ * Checks if a snap ID exists in the map.
136
+ *
137
+ * @param snapId - Snap ID present in the value to check.
138
+ * @returns `true` if the snap ID is present in the map, `false` otherwise.
139
+ */
140
+ hasSnapId(snapId: SnapId): boolean;
134
141
  /**
135
142
  * Deletes a key from the map.
136
143
  *
@@ -1 +1 @@
1
- {"version":3,"file":"SnapIdMap.d.mts","sourceRoot":"","sources":["../src/SnapIdMap.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,4BAA4B;AAIlD;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;;;;OAKG;gBACS,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM;CAMxC;AAED;;;;;;GAMG;AACH,qBAAa,SAAS,CAAC,KAAK,SAAS;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE;;IAGrD;;;;;;;;;;;;;;;OAeG;gBACS,QAAQ,CAAC,EAAE,QAAQ,CAAC,SAAS,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAIzD;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,QAAQ,IAAI,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC;IAIjC;;;;;;;;;;;;;;;OAeG;IACH,MAAM,CAAC,UAAU,CAAC,KAAK,SAAS;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,EAChD,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,GACzB,SAAS,CAAC,KAAK,CAAC;IAInB;;;;;;;;;;;;;;;;;;;OAmBG;IACH,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS;IAKnD;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO;IAIzC;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO;IAK5C;;;;;;;;;;;;;;;;;OAiBG;IAEH,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI;IAWpC;;;;;;;;;;;;;;;;;;;OAmBG;IACH,MAAM,IAAI,gBAAgB,CAAC,KAAK,CAAC;IAIjC;;;;OAIG;IACH,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF"}
1
+ {"version":3,"file":"SnapIdMap.d.mts","sourceRoot":"","sources":["../src/SnapIdMap.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,4BAA4B;AAIlD;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;;;;OAKG;gBACS,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM;CAMxC;AAED;;;;;;GAMG;AACH,qBAAa,SAAS,CAAC,KAAK,SAAS;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE;;IAGrD;;;;;;;;;;;;;;;OAeG;gBACS,QAAQ,CAAC,EAAE,QAAQ,CAAC,SAAS,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAIzD;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,QAAQ,IAAI,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC;IAIjC;;;;;;;;;;;;;;;OAeG;IACH,MAAM,CAAC,UAAU,CAAC,KAAK,SAAS;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,EAChD,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,GACzB,SAAS,CAAC,KAAK,CAAC;IAInB;;;;;;;;;;;;;;;;;;;OAmBG;IACH,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS;IAKnD;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO;IAIzC;;;;;OAKG;IACH,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAMlC;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO;IAK5C;;;;;;;;;;;;;;;;;OAiBG;IAEH,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI;IAWpC;;;;;;;;;;;;;;;;;;;OAmBG;IACH,MAAM,IAAI,gBAAgB,CAAC,KAAK,CAAC;IAIjC;;;;OAIG;IACH,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF"}
@@ -147,6 +147,17 @@ export class SnapIdMap {
147
147
  has(snapId, key) {
148
148
  return this.get(snapId, key) !== undefined;
149
149
  }
150
+ /**
151
+ * Checks if a snap ID exists in the map.
152
+ *
153
+ * @param snapId - Snap ID present in the value to check.
154
+ * @returns `true` if the snap ID is present in the map, `false` otherwise.
155
+ */
156
+ hasSnapId(snapId) {
157
+ // We could use a reverse-mapping to map Snap ID to their actual key too, but
158
+ // for now, this will do the trick.
159
+ return [...__classPrivateFieldGet(this, _SnapIdMap_map, "f").values()].some((value) => value.snapId === snapId);
160
+ }
150
161
  /**
151
162
  * Deletes a key from the map.
152
163
  *
@@ -1 +1 @@
1
- {"version":3,"file":"SnapIdMap.mjs","sourceRoot":"","sources":["../src/SnapIdMap.ts"],"names":[],"mappings":";;;;;;;;;;;;AAEA,OAAO,EAAE,kBAAkB,EAAE,iCAA6B;AAE1D;;GAEG;AACH,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IAW3C;;;;;OAKG;IACH,YAAY,MAAc,EAAE,GAAW;QACrC,KAAK,CAAC,SAAS,MAAM,4BAA4B,GAAG,GAAG,CAAC,CAAC;QACzD,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;QACjC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;IACjB,CAAC;CACF;AAED;;;;;;GAMG;AACH,MAAM,OAAO,SAAS;IAGpB;;;;;;;;;;;;;;;OAeG;IACH,YAAY,QAA6C;QAlBhD,iCAAgC;QAmBvC,uBAAA,IAAI,kBAAQ,IAAI,kBAAkB,CAAC,QAAQ,CAAC,MAAA,CAAC;IAC/C,CAAC;IAED;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,QAAQ;QACN,OAAO,uBAAA,IAAI,sBAAK,CAAC,QAAQ,EAAE,CAAC;IAC9B,CAAC;IAED;;;;;;;;;;;;;;;OAeG;IACH,MAAM,CAAC,UAAU,CACf,GAA0B;QAE1B,OAAO,IAAI,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5C,CAAC;IAED;;;;;;;;;;;;;;;;;;;OAmBG;IACH,GAAG,CAAC,MAAc,EAAE,GAAW;QAC7B,MAAM,KAAK,GAAG,uBAAA,IAAI,sBAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACjC,OAAO,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;IACtD,CAAC;IAED;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,GAAG,CAAC,MAAc,EAAE,GAAW;QAC7B,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,SAAS,CAAC;IAC7C,CAAC;IAED;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,MAAM,CAAC,MAAc,EAAE,GAAW;QAChC,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,uBAAA,IAAI,sBAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACxD,CAAC;IAED,4CAA4C;IAC5C;;;;;;;;;;;;;;;;;OAiBG;IACH,2CAA2C;IAC3C,GAAG,CAAC,GAAW,EAAE,KAAY;QAC3B,uEAAuE;QACvE,sEAAsE;QACtE,sCAAsC;QACtC,IAAI,uBAAA,IAAI,sBAAK,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC;YACvD,MAAM,IAAI,kBAAkB,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAClD,CAAC;QACD,uBAAA,IAAI,sBAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC1B,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;;;;;;;;;;;;;;;OAmBG;IACH,MAAM;QACJ,OAAO,uBAAA,IAAI,sBAAK,CAAC,MAAM,EAAE,CAAC;IAC5B,CAAC;IAED;;;;OAIG;IACH,IAAI,IAAI;QACN,OAAO,uBAAA,IAAI,sBAAK,CAAC,IAAI,CAAC;IACxB,CAAC;CACF","sourcesContent":["import type { SnapId } from '@metamask/snaps-sdk';\n\nimport { CaseInsensitiveMap } from './CaseInsensitiveMap';\n\n/**\n * Error thrown when an invalid Snap ID is encountered.\n */\nexport class InvalidSnapIdError extends Error {\n /**\n * The ID of the Snap that caused the error.\n */\n snapId: SnapId;\n\n /**\n * The key of the element that caused the error.\n */\n key: string;\n\n /**\n * Creates an instance of `InvalidSnapIdError`.\n *\n * @param snapId - The invalid Snap ID.\n * @param key - The key associated with the invalid Snap ID.\n */\n constructor(snapId: SnapId, key: string) {\n super(`Snap \"${snapId}\" is not allowed to set \"${key}\"`);\n this.name = 'InvalidSnapIdError';\n this.snapId = snapId;\n this.key = key;\n }\n}\n\n/**\n * A map that associates a string key with a value that has a `snapId`\n * property. Note that the key is case-insensitive.\n *\n * The `snapId` property is used to ensure that only the Snap that added an\n * item to the map can modify or delete it.\n */\nexport class SnapIdMap<Value extends { snapId: SnapId }> {\n readonly #map: CaseInsensitiveMap<Value>;\n\n /**\n * Creates a new `SnapIdMap` object.\n *\n * Example:\n *\n * ```ts\n * const items = [\n * ['foo', { snapId: '1', name: 'foo' }],\n * ['bar', { snapId: '1', name: 'bar' }],\n * ];\n * const map = new SnapIdMap(items);\n * ```\n *\n * @param iterable - An iterable object whose elements are key-value pairs.\n * Each key-value pair will be added to the new map.\n */\n constructor(iterable?: Iterable<readonly [string, Value]>) {\n this.#map = new CaseInsensitiveMap(iterable);\n }\n\n /**\n * Returns a plain object with the same key-value pairs as this map.\n *\n * Example:\n *\n * ```ts\n * const items = [\n * ['foo', { snapId: '1', name: 'foo' }],\n * ['bar', { snapId: '1', name: 'bar' }],\n * ];\n * const map = new SnapIdMap(items);\n * map.toObject();\n * // Returns\n * // {\n * // foo: { snapId: '1', name: 'foo' },\n * // bar: { snapId: '1', name: 'bar' },\n * // }\n * ```\n *\n * @returns A plain object with the same key-value pairs as this map.\n */\n toObject(): Record<string, Value> {\n return this.#map.toObject();\n }\n\n /**\n * Returns a new `SnapIdMap` object from an plain object.\n *\n * Example:\n *\n * ```ts\n * const obj = {\n * foo: { snapId: '1', name: 'foo' },\n * bar: { snapId: '1', name: 'bar' },\n * };\n * const map = SnapIdMap.fromObject(obj);\n * ```\n *\n * @param obj - A plain object whose elements will be added to the new map.\n * @returns A new `SnapIdMap` containing the elements of the given object.\n */\n static fromObject<Value extends { snapId: SnapId }>(\n obj: Record<string, Value>,\n ): SnapIdMap<Value> {\n return new SnapIdMap(Object.entries(obj));\n }\n\n /**\n * Gets a value from the map.\n *\n * If the given key is not present in the map or the Snap ID of the value is\n * different from the given Snap ID, returns `undefined`.\n *\n * Example:\n *\n * ```ts\n * const map = new SnapIdMap();\n * map.set('foo', { snapId: '1', name: 'foo' });\n * map.get('1', 'foo'); // Returns { snapId: '1', name: 'foo' }\n * map.get('2', 'foo'); // Returns `undefined`\n * map.get('1', 'bar'); // Returns `undefined`\n * ```\n *\n * @param snapId - Snap ID present in the value to get.\n * @param key - Key of the element to get.\n * @returns The value associated with the given key and Snap ID.\n */\n get(snapId: SnapId, key: string): Value | undefined {\n const value = this.#map.get(key);\n return value?.snapId === snapId ? value : undefined;\n }\n\n /**\n * Checks if a key is present in the map.\n *\n * If the given key is not present in the map or the Snap ID of the value is\n * different from the given Snap ID, returns `false`.\n *\n * Example:\n *\n * ```ts\n * const map = new SnapIdMap();\n * map.set('foo', { snapId: '1', name: 'foo' });\n * map.has('1', 'foo'); // Returns `true`\n * map.has('2', 'foo'); // Returns `false`\n * map.has('1', 'bar'); // Returns `false`\n * ```\n *\n * @param snapId - Snap ID present in the value to check.\n * @param key - Key of the element to check.\n * @returns `true` if the key is present in the map and the Snap ID of the\n * value is equal to the given Snap ID, `false` otherwise.\n */\n has(snapId: SnapId, key: string): boolean {\n return this.get(snapId, key) !== undefined;\n }\n\n /**\n * Deletes a key from the map.\n *\n * If the given key is not present in the map or the Snap IDs don't match,\n * returns `false` and does nothing.\n *\n * Example:\n *\n * ```ts\n * const map = new SnapIdMap();\n * map.set('foo', { snapId: '1', name: 'foo' });\n * map.delete('2', 'foo'); // Returns `false`\n * map.delete('1', 'bar'); // Returns `false`\n * map.delete('1', 'foo'); // Returns `true`\n * ```\n *\n * @param snapId - Snap ID present in the value to delete.\n * @param key - Key of the element to delete.\n * @returns `true` if the key was present in the map and the Snap ID of the\n * value was equal to the given Snap ID, `false` otherwise.\n */\n delete(snapId: SnapId, key: string): boolean {\n return this.has(snapId, key) && this.#map.delete(key);\n }\n\n /* eslint-disable jsdoc/check-indentation */\n /**\n * Adds or updates a key-value pair in the map.\n *\n * Note that this method has a different behavior from the `Map.set`.\n *\n * - If the given key is not already present in the map, this method adds the\n * key-value pair to the map.\n *\n * - If the given key is already present in the map and the Snap IDs match,\n * this method updates the value associated with the key.\n *\n * - However, if the given key is already present in the map but the Snap IDs\n * do not match, this method throws an error.\n *\n * @param key - Key of the element to add or update.\n * @param value - Value of the element to add or update.\n * @returns The map itself.\n */\n /* eslint-enable jsdoc/check-indentation */\n set(key: string, value: Value): this {\n // If the key is present in the map but isn't associated with the given\n // Snap ID, it means that the item was added to the map by a different\n // Snap. In this case, throw an error.\n if (this.#map.has(key) && !this.has(value.snapId, key)) {\n throw new InvalidSnapIdError(value.snapId, key);\n }\n this.#map.set(key, value);\n return this;\n }\n\n /**\n * Returns an iterable of the values in the map.\n *\n * Example:\n *\n * ```ts\n * const map = new SnapIdMap([\n * ['foo', { snapId: '1', name: 'foo' }],\n * ['bar', { snapId: '1', name: 'bar' }],\n * ]);\n * const values = [...map.values()];\n * // Returns\n * // [\n * // { snapId: '1', name: 'foo' },\n * // { snapId: '1', name: 'bar' },\n * // ]\n * ```\n *\n * @returns An iterable of the values in the map.\n */\n values(): IterableIterator<Value> {\n return this.#map.values();\n }\n\n /**\n * Returns the number of key-value pairs in the map.\n *\n * @returns The number of key-value pairs in the map.\n */\n get size(): number {\n return this.#map.size;\n }\n}\n"]}
1
+ {"version":3,"file":"SnapIdMap.mjs","sourceRoot":"","sources":["../src/SnapIdMap.ts"],"names":[],"mappings":";;;;;;;;;;;;AAEA,OAAO,EAAE,kBAAkB,EAAE,iCAA6B;AAE1D;;GAEG;AACH,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IAW3C;;;;;OAKG;IACH,YAAY,MAAc,EAAE,GAAW;QACrC,KAAK,CAAC,SAAS,MAAM,4BAA4B,GAAG,GAAG,CAAC,CAAC;QACzD,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;QACjC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;IACjB,CAAC;CACF;AAED;;;;;;GAMG;AACH,MAAM,OAAO,SAAS;IAGpB;;;;;;;;;;;;;;;OAeG;IACH,YAAY,QAA6C;QAlBhD,iCAAgC;QAmBvC,uBAAA,IAAI,kBAAQ,IAAI,kBAAkB,CAAC,QAAQ,CAAC,MAAA,CAAC;IAC/C,CAAC;IAED;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,QAAQ;QACN,OAAO,uBAAA,IAAI,sBAAK,CAAC,QAAQ,EAAE,CAAC;IAC9B,CAAC;IAED;;;;;;;;;;;;;;;OAeG;IACH,MAAM,CAAC,UAAU,CACf,GAA0B;QAE1B,OAAO,IAAI,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5C,CAAC;IAED;;;;;;;;;;;;;;;;;;;OAmBG;IACH,GAAG,CAAC,MAAc,EAAE,GAAW;QAC7B,MAAM,KAAK,GAAG,uBAAA,IAAI,sBAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACjC,OAAO,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;IACtD,CAAC;IAED;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,GAAG,CAAC,MAAc,EAAE,GAAW;QAC7B,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,SAAS,CAAC;IAC7C,CAAC;IAED;;;;;OAKG;IACH,SAAS,CAAC,MAAc;QACtB,6EAA6E;QAC7E,mCAAmC;QACnC,OAAO,CAAC,GAAG,uBAAA,IAAI,sBAAK,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC;IAC1E,CAAC;IAED;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,MAAM,CAAC,MAAc,EAAE,GAAW;QAChC,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,uBAAA,IAAI,sBAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACxD,CAAC;IAED,4CAA4C;IAC5C;;;;;;;;;;;;;;;;;OAiBG;IACH,2CAA2C;IAC3C,GAAG,CAAC,GAAW,EAAE,KAAY;QAC3B,uEAAuE;QACvE,sEAAsE;QACtE,sCAAsC;QACtC,IAAI,uBAAA,IAAI,sBAAK,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC;YACvD,MAAM,IAAI,kBAAkB,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAClD,CAAC;QACD,uBAAA,IAAI,sBAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC1B,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;;;;;;;;;;;;;;;OAmBG;IACH,MAAM;QACJ,OAAO,uBAAA,IAAI,sBAAK,CAAC,MAAM,EAAE,CAAC;IAC5B,CAAC;IAED;;;;OAIG;IACH,IAAI,IAAI;QACN,OAAO,uBAAA,IAAI,sBAAK,CAAC,IAAI,CAAC;IACxB,CAAC;CACF","sourcesContent":["import type { SnapId } from '@metamask/snaps-sdk';\n\nimport { CaseInsensitiveMap } from './CaseInsensitiveMap';\n\n/**\n * Error thrown when an invalid Snap ID is encountered.\n */\nexport class InvalidSnapIdError extends Error {\n /**\n * The ID of the Snap that caused the error.\n */\n snapId: SnapId;\n\n /**\n * The key of the element that caused the error.\n */\n key: string;\n\n /**\n * Creates an instance of `InvalidSnapIdError`.\n *\n * @param snapId - The invalid Snap ID.\n * @param key - The key associated with the invalid Snap ID.\n */\n constructor(snapId: SnapId, key: string) {\n super(`Snap \"${snapId}\" is not allowed to set \"${key}\"`);\n this.name = 'InvalidSnapIdError';\n this.snapId = snapId;\n this.key = key;\n }\n}\n\n/**\n * A map that associates a string key with a value that has a `snapId`\n * property. Note that the key is case-insensitive.\n *\n * The `snapId` property is used to ensure that only the Snap that added an\n * item to the map can modify or delete it.\n */\nexport class SnapIdMap<Value extends { snapId: SnapId }> {\n readonly #map: CaseInsensitiveMap<Value>;\n\n /**\n * Creates a new `SnapIdMap` object.\n *\n * Example:\n *\n * ```ts\n * const items = [\n * ['foo', { snapId: '1', name: 'foo' }],\n * ['bar', { snapId: '1', name: 'bar' }],\n * ];\n * const map = new SnapIdMap(items);\n * ```\n *\n * @param iterable - An iterable object whose elements are key-value pairs.\n * Each key-value pair will be added to the new map.\n */\n constructor(iterable?: Iterable<readonly [string, Value]>) {\n this.#map = new CaseInsensitiveMap(iterable);\n }\n\n /**\n * Returns a plain object with the same key-value pairs as this map.\n *\n * Example:\n *\n * ```ts\n * const items = [\n * ['foo', { snapId: '1', name: 'foo' }],\n * ['bar', { snapId: '1', name: 'bar' }],\n * ];\n * const map = new SnapIdMap(items);\n * map.toObject();\n * // Returns\n * // {\n * // foo: { snapId: '1', name: 'foo' },\n * // bar: { snapId: '1', name: 'bar' },\n * // }\n * ```\n *\n * @returns A plain object with the same key-value pairs as this map.\n */\n toObject(): Record<string, Value> {\n return this.#map.toObject();\n }\n\n /**\n * Returns a new `SnapIdMap` object from an plain object.\n *\n * Example:\n *\n * ```ts\n * const obj = {\n * foo: { snapId: '1', name: 'foo' },\n * bar: { snapId: '1', name: 'bar' },\n * };\n * const map = SnapIdMap.fromObject(obj);\n * ```\n *\n * @param obj - A plain object whose elements will be added to the new map.\n * @returns A new `SnapIdMap` containing the elements of the given object.\n */\n static fromObject<Value extends { snapId: SnapId }>(\n obj: Record<string, Value>,\n ): SnapIdMap<Value> {\n return new SnapIdMap(Object.entries(obj));\n }\n\n /**\n * Gets a value from the map.\n *\n * If the given key is not present in the map or the Snap ID of the value is\n * different from the given Snap ID, returns `undefined`.\n *\n * Example:\n *\n * ```ts\n * const map = new SnapIdMap();\n * map.set('foo', { snapId: '1', name: 'foo' });\n * map.get('1', 'foo'); // Returns { snapId: '1', name: 'foo' }\n * map.get('2', 'foo'); // Returns `undefined`\n * map.get('1', 'bar'); // Returns `undefined`\n * ```\n *\n * @param snapId - Snap ID present in the value to get.\n * @param key - Key of the element to get.\n * @returns The value associated with the given key and Snap ID.\n */\n get(snapId: SnapId, key: string): Value | undefined {\n const value = this.#map.get(key);\n return value?.snapId === snapId ? value : undefined;\n }\n\n /**\n * Checks if a key is present in the map.\n *\n * If the given key is not present in the map or the Snap ID of the value is\n * different from the given Snap ID, returns `false`.\n *\n * Example:\n *\n * ```ts\n * const map = new SnapIdMap();\n * map.set('foo', { snapId: '1', name: 'foo' });\n * map.has('1', 'foo'); // Returns `true`\n * map.has('2', 'foo'); // Returns `false`\n * map.has('1', 'bar'); // Returns `false`\n * ```\n *\n * @param snapId - Snap ID present in the value to check.\n * @param key - Key of the element to check.\n * @returns `true` if the key is present in the map and the Snap ID of the\n * value is equal to the given Snap ID, `false` otherwise.\n */\n has(snapId: SnapId, key: string): boolean {\n return this.get(snapId, key) !== undefined;\n }\n\n /**\n * Checks if a snap ID exists in the map.\n *\n * @param snapId - Snap ID present in the value to check.\n * @returns `true` if the snap ID is present in the map, `false` otherwise.\n */\n hasSnapId(snapId: SnapId): boolean {\n // We could use a reverse-mapping to map Snap ID to their actual key too, but\n // for now, this will do the trick.\n return [...this.#map.values()].some((value) => value.snapId === snapId);\n }\n\n /**\n * Deletes a key from the map.\n *\n * If the given key is not present in the map or the Snap IDs don't match,\n * returns `false` and does nothing.\n *\n * Example:\n *\n * ```ts\n * const map = new SnapIdMap();\n * map.set('foo', { snapId: '1', name: 'foo' });\n * map.delete('2', 'foo'); // Returns `false`\n * map.delete('1', 'bar'); // Returns `false`\n * map.delete('1', 'foo'); // Returns `true`\n * ```\n *\n * @param snapId - Snap ID present in the value to delete.\n * @param key - Key of the element to delete.\n * @returns `true` if the key was present in the map and the Snap ID of the\n * value was equal to the given Snap ID, `false` otherwise.\n */\n delete(snapId: SnapId, key: string): boolean {\n return this.has(snapId, key) && this.#map.delete(key);\n }\n\n /* eslint-disable jsdoc/check-indentation */\n /**\n * Adds or updates a key-value pair in the map.\n *\n * Note that this method has a different behavior from the `Map.set`.\n *\n * - If the given key is not already present in the map, this method adds the\n * key-value pair to the map.\n *\n * - If the given key is already present in the map and the Snap IDs match,\n * this method updates the value associated with the key.\n *\n * - However, if the given key is already present in the map but the Snap IDs\n * do not match, this method throws an error.\n *\n * @param key - Key of the element to add or update.\n * @param value - Value of the element to add or update.\n * @returns The map itself.\n */\n /* eslint-enable jsdoc/check-indentation */\n set(key: string, value: Value): this {\n // If the key is present in the map but isn't associated with the given\n // Snap ID, it means that the item was added to the map by a different\n // Snap. In this case, throw an error.\n if (this.#map.has(key) && !this.has(value.snapId, key)) {\n throw new InvalidSnapIdError(value.snapId, key);\n }\n this.#map.set(key, value);\n return this;\n }\n\n /**\n * Returns an iterable of the values in the map.\n *\n * Example:\n *\n * ```ts\n * const map = new SnapIdMap([\n * ['foo', { snapId: '1', name: 'foo' }],\n * ['bar', { snapId: '1', name: 'bar' }],\n * ]);\n * const values = [...map.values()];\n * // Returns\n * // [\n * // { snapId: '1', name: 'foo' },\n * // { snapId: '1', name: 'bar' },\n * // ]\n * ```\n *\n * @returns An iterable of the values in the map.\n */\n values(): IterableIterator<Value> {\n return this.#map.values();\n }\n\n /**\n * Returns the number of key-value pairs in the map.\n *\n * @returns The number of key-value pairs in the map.\n */\n get size(): number {\n return this.#map.size;\n }\n}\n"]}
@@ -13,7 +13,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
13
13
  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");
14
14
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
15
15
  };
16
- var _SnapKeyring_instances, _SnapKeyring_messenger, _SnapKeyring_snapClient, _SnapKeyring_accounts, _SnapKeyring_requests, _SnapKeyring_callbacks, _SnapKeyring_handleAccountCreated, _SnapKeyring_handleAccountUpdated, _SnapKeyring_handleAccountDeleted, _SnapKeyring_handleRequestApproved, _SnapKeyring_handleRequestRejected, _SnapKeyring_rePublishAccountEvent, _SnapKeyring_handleAccountBalancesUpdated, _SnapKeyring_handleAccountAssetListUpdated, _SnapKeyring_handleAccountTransactionsUpdated, _SnapKeyring_submitRequest, _SnapKeyring_hasMethod, _SnapKeyring_createRequestPromise, _SnapKeyring_submitSnapRequest, _SnapKeyring_handleSyncResponse, _SnapKeyring_handleAsyncResponse, _SnapKeyring_sanitizeRedirectUrl, _SnapKeyring_validateRedirectUrl, _SnapKeyring_resolveAddress, _SnapKeyring_getSnap, _SnapKeyring_getSnapMetadata, _SnapKeyring_getSnapAllowedOrigins;
16
+ var _SnapKeyring_instances, _SnapKeyring_messenger, _SnapKeyring_snapClient, _SnapKeyring_accounts, _SnapKeyring_requests, _SnapKeyring_callbacks, _SnapKeyring_handleAccountCreated, _SnapKeyring_handleAccountUpdated, _SnapKeyring_handleAccountDeleted, _SnapKeyring_handleRequestApproved, _SnapKeyring_handleRequestRejected, _SnapKeyring_rePublishAccountEvent, _SnapKeyring_handleAccountBalancesUpdated, _SnapKeyring_handleAccountAssetListUpdated, _SnapKeyring_handleAccountTransactionsUpdated, _SnapKeyring_getAccount, _SnapKeyring_submitRequest, _SnapKeyring_submitSnapRequest, _SnapKeyring_hasMethod, _SnapKeyring_createRequestPromise, _SnapKeyring_clearRequestPromise, _SnapKeyring_handleSyncResponse, _SnapKeyring_handleAsyncResponse, _SnapKeyring_sanitizeRedirectUrl, _SnapKeyring_validateRedirectUrl, _SnapKeyring_deleteAccount, _SnapKeyring_resolveAddress, _SnapKeyring_getSnap, _SnapKeyring_getSnapMetadata, _SnapKeyring_getSnapAllowedOrigins;
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
18
  exports.SnapKeyring = exports.SNAP_KEYRING_TYPE = void 0;
19
19
  const tx_1 = require("@ethereumjs/tx");
@@ -189,6 +189,58 @@ class SnapKeyring extends events_1.EventEmitter {
189
189
  .filter(({ snapId: accountSnapId }) => accountSnapId === snapId)
190
190
  .map(({ account }) => normalizeAccountAddress(account)));
191
191
  }
192
+ /**
193
+ * Checks if a Snap ID is known from the keyring.
194
+ *
195
+ * @param snapId - Snap ID.
196
+ * @returns `true` if the Snap ID is known, `false` otherwise.
197
+ */
198
+ hasSnapId(snapId) {
199
+ return __classPrivateFieldGet(this, _SnapKeyring_accounts, "f").hasSnapId(snapId);
200
+ }
201
+ /**
202
+ * Resolve the Snap account's address associated from a signing request.
203
+ *
204
+ * @param snapId - Snap ID.
205
+ * @param scope - CAIP-2 chain ID.
206
+ * @param request - Signing request object.
207
+ * @throws An error if the Snap ID is not known from the keyring.
208
+ * @returns The resolved address if found, `null` otherwise.
209
+ */
210
+ async resolveAccountAddress(snapId, scope, request) {
211
+ // We do check that the incoming Snap ID is known by the keyring.
212
+ if (!this.hasSnapId(snapId)) {
213
+ throw new Error(`Unable to resolve account address: unknown Snap ID: ${snapId}`);
214
+ }
215
+ return await __classPrivateFieldGet(this, _SnapKeyring_snapClient, "f")
216
+ .withSnapId(snapId)
217
+ .resolveAccountAddress(scope, request);
218
+ }
219
+ /**
220
+ * Submit a request to a Snap from an account ID.
221
+ *
222
+ * This request cannot be an asynchronous keyring request.
223
+ *
224
+ * @param opts - Request options.
225
+ * @param opts.account - Account ID.
226
+ * @param opts.method - Method to call.
227
+ * @param opts.params - Method parameters.
228
+ * @param opts.scope - Selected chain ID (CAIP-2).
229
+ * @returns Promise that resolves to the result of the method call.
230
+ */
231
+ async submitRequest({ account: accountId, method, params, scope, }) {
232
+ const { account, snapId } = __classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_getAccount).call(this, accountId);
233
+ return await __classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_submitSnapRequest).call(this, {
234
+ snapId,
235
+ account,
236
+ method: method,
237
+ params,
238
+ scope,
239
+ // For non-EVM (in the context of the multichain API and SIP-26), we enforce responses
240
+ // to be synchronous.
241
+ noPending: true,
242
+ });
243
+ }
192
244
  /**
193
245
  * Sign a transaction.
194
246
  *
@@ -209,7 +261,7 @@ class SnapKeyring extends events_1.EventEmitter {
209
261
  address,
210
262
  method: keyring_api_1.EthMethod.SignTransaction,
211
263
  params: [tx],
212
- chainId: (0, utils_1.toCaipChainId)(utils_1.KnownCaipNamespace.Eip155, `${chainId}`),
264
+ scope: (0, utils_1.toCaipChainId)(utils_1.KnownCaipNamespace.Eip155, `${chainId}`),
213
265
  });
214
266
  // ! It's *** CRITICAL *** that we mask the signature here, otherwise the
215
267
  // ! Snap could overwrite the transaction.
@@ -252,7 +304,7 @@ class SnapKeyring extends events_1.EventEmitter {
252
304
  ...(chainId === undefined
253
305
  ? {}
254
306
  : {
255
- chainId: (0, utils_1.toCaipChainId)(utils_1.KnownCaipNamespace.Eip155, `${chainId}`),
307
+ scope: (0, utils_1.toCaipChainId)(utils_1.KnownCaipNamespace.Eip155, `${chainId}`),
256
308
  }),
257
309
  }), keyring_api_1.EthBytesStruct);
258
310
  }
@@ -302,7 +354,7 @@ class SnapKeyring extends events_1.EventEmitter {
302
354
  params: (0, util_1.toJson)(transactions),
303
355
  noPending: true,
304
356
  // We assume the chain ID is already well formatted
305
- chainId: (0, utils_1.toCaipChainId)(utils_1.KnownCaipNamespace.Eip155, context.chainId),
357
+ scope: (0, utils_1.toCaipChainId)(utils_1.KnownCaipNamespace.Eip155, context.chainId),
306
358
  }), keyring_api_1.EthBaseUserOperationStruct);
307
359
  }
308
360
  /**
@@ -321,7 +373,7 @@ class SnapKeyring extends events_1.EventEmitter {
321
373
  params: (0, util_1.toJson)([userOp]),
322
374
  noPending: true,
323
375
  // We assume the chain ID is already well formatted
324
- chainId: (0, utils_1.toCaipChainId)(utils_1.KnownCaipNamespace.Eip155, context.chainId),
376
+ scope: (0, utils_1.toCaipChainId)(utils_1.KnownCaipNamespace.Eip155, context.chainId),
325
377
  }), keyring_api_1.EthUserOperationPatchStruct);
326
378
  }
327
379
  /**
@@ -338,7 +390,7 @@ class SnapKeyring extends events_1.EventEmitter {
338
390
  method: keyring_api_1.EthMethod.SignUserOperation,
339
391
  params: (0, util_1.toJson)([userOp]),
340
392
  // We assume the chain ID is already well formatted
341
- chainId: (0, utils_1.toCaipChainId)(utils_1.KnownCaipNamespace.Eip155, context.chainId),
393
+ scope: (0, utils_1.toCaipChainId)(utils_1.KnownCaipNamespace.Eip155, context.chainId),
342
394
  }), keyring_api_1.EthBytesStruct);
343
395
  }
344
396
  /**
@@ -362,18 +414,7 @@ class SnapKeyring extends events_1.EventEmitter {
362
414
  */
363
415
  async removeAccount(address) {
364
416
  const { account, snapId } = __classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_resolveAddress).call(this, address);
365
- // Always remove the account from the maps, even if the Snap is going to
366
- // fail to delete it.
367
- __classPrivateFieldGet(this, _SnapKeyring_accounts, "f").delete(snapId, account.id);
368
- try {
369
- await __classPrivateFieldGet(this, _SnapKeyring_snapClient, "f").withSnapId(snapId).deleteAccount(account.id);
370
- }
371
- catch (error) {
372
- // If the Snap failed to delete the account, log the error and continue
373
- // with the account deletion, otherwise the account will be stuck in the
374
- // keyring.
375
- console.error(`Account '${address}' may not have been removed from snap '${snapId}':`, error);
376
- }
417
+ await __classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_deleteAccount).call(this, snapId, account);
377
418
  }
378
419
  /**
379
420
  * Return an internal account object for a given address.
@@ -429,6 +470,10 @@ _SnapKeyring_messenger = new WeakMap(), _SnapKeyring_snapClient = new WeakMap(),
429
470
  async function _SnapKeyring_handleAccountCreated(snapId, message) {
430
471
  (0, superstruct_1.assert)(message, events_2.AccountCreatedEventStruct);
431
472
  const { account: newAccountFromEvent, accountNameSuggestion, displayConfirmation, } = message.params;
473
+ // READ THIS CAREFULLY:
474
+ // ------------------------------------------------------------------------
475
+ // The account creation flow is now asynchronous. We expect the Snap to
476
+ // first create the account data and then fire the "AccountCreated" event.
432
477
  // Potentially migrate the account.
433
478
  const account = (0, account_1.transformAccount)(newAccountFromEvent);
434
479
  // The UI still uses the account address to identify accounts, so we need
@@ -443,10 +488,31 @@ async function _SnapKeyring_handleAccountCreated(snapId, message) {
443
488
  if (__classPrivateFieldGet(this, _SnapKeyring_accounts, "f").has(snapId, account.id)) {
444
489
  throw new Error(`Account '${account.id}' already exists`);
445
490
  }
446
- await __classPrivateFieldGet(this, _SnapKeyring_callbacks, "f").addAccount(address, snapId, async (accepted) => {
491
+ // Add the account to the keyring, but wait for the MetaMask client to
492
+ // approve the account creation first.
493
+ await __classPrivateFieldGet(this, _SnapKeyring_callbacks, "f").addAccount(address, snapId,
494
+ // This callback is passed to the MetaMask client, it will be called whenever
495
+ // the end user will accept or not the account creation.
496
+ async (accepted) => {
447
497
  if (accepted) {
498
+ // We consider the account to be created on the Snap keyring only if
499
+ // the user accepted it. Meaning that the Snap MIGHT HAVE created the
500
+ // account on its own state, but the Snap keyring MIGHT NOT HAVE it yet.
501
+ //
502
+ // e.g The account creation dialog crashed on MetaMask, this callback
503
+ // will never be called, but the Snap still has the account.
448
504
  __classPrivateFieldGet(this, _SnapKeyring_accounts, "f").set(account.id, { account, snapId });
449
- await __classPrivateFieldGet(this, _SnapKeyring_callbacks, "f").saveState();
505
+ // This is the "true async part". We do not `await` for this call, mainly
506
+ // because this callback will persist the account on the client side
507
+ // (through the `AccountsController`).
508
+ //
509
+ // Since this will happen after the Snap account creation and Snap
510
+ // event, if anything goes wrong, we will delete the account by
511
+ // calling `deleteAccount` on the Snap.
512
+ // eslint-disable-next-line no-void
513
+ void __classPrivateFieldGet(this, _SnapKeyring_callbacks, "f").saveState().catch(async () => {
514
+ await __classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_deleteAccount).call(this, snapId, account);
515
+ });
450
516
  }
451
517
  }, accountNameSuggestion, displayConfirmation);
452
518
  return null;
@@ -576,76 +642,61 @@ async function _SnapKeyring_handleAccountAssetListUpdated(message) {
576
642
  async function _SnapKeyring_handleAccountTransactionsUpdated(message) {
577
643
  (0, superstruct_1.assert)(message, keyring_api_1.AccountTransactionsUpdatedEventStruct);
578
644
  return __classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_rePublishAccountEvent).call(this, 'SnapKeyring:accountTransactionsUpdated', message.params);
645
+ }, _SnapKeyring_getAccount = function _SnapKeyring_getAccount(id) {
646
+ const found = [...__classPrivateFieldGet(this, _SnapKeyring_accounts, "f").values()].find((entry) => entry.account.id === id);
647
+ if (!found) {
648
+ throw new Error(`Unable to get account: unknown account ID: '${id}'`);
649
+ }
650
+ return found;
579
651
  }, _SnapKeyring_submitRequest =
580
652
  /**
581
- * Submit a request to a Snap.
653
+ * Submit a request to a Snap from an account address.
582
654
  *
583
655
  * @param opts - Request options.
584
656
  * @param opts.address - Account address.
585
657
  * @param opts.method - Method to call.
586
658
  * @param opts.params - Method parameters.
587
- * @param opts.chainId - Selected chain ID (CAIP-2).
659
+ * @param opts.scope - Selected chain ID (CAIP-2).
588
660
  * @param opts.noPending - Whether the response is allowed to be pending.
589
661
  * @returns Promise that resolves to the result of the method call.
590
662
  */
591
- async function _SnapKeyring_submitRequest({ address, method, params, chainId = '', noPending = false, }) {
663
+ async function _SnapKeyring_submitRequest({ address, method, params, scope = '', noPending = false, }) {
592
664
  const { account, snapId } = __classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_resolveAddress).call(this, address);
593
- if (!__classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_hasMethod).call(this, account, method)) {
594
- throw new Error(`Method '${method}' not supported for account ${account.address}`);
595
- }
596
- const requestId = (0, uuid_1.v4)();
597
- // Create the promise before calling the Snap to prevent a race condition
598
- // where the Snap responds before we have a chance to create it.
599
- const promise = __classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_createRequestPromise).call(this, requestId, snapId);
600
- const response = await __classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_submitSnapRequest).call(this, {
665
+ return await __classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_submitSnapRequest).call(this, {
601
666
  snapId,
602
- requestId,
603
667
  account,
604
668
  method: method,
605
669
  params,
606
- chainId,
670
+ scope,
671
+ noPending,
607
672
  });
608
- // Some methods, like the ones used to prepare and patch user operations,
609
- // require the Snap to answer synchronously in order to work with the
610
- // confirmation flow. This check lets the caller enforce this behavior.
611
- if (noPending && response.pending) {
612
- throw new Error(`Request '${requestId}' to snap '${snapId}' is pending and noPending is true.`);
613
- }
614
- // If the Snap answers synchronously, the promise must be removed from the
615
- // map to prevent a leak.
616
- if (!response.pending) {
617
- return __classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_handleSyncResponse).call(this, response, requestId, snapId);
618
- }
619
- // If the Snap answers asynchronously, we will inform the user with a redirect
620
- if (response.redirect?.message || response.redirect?.url) {
621
- await __classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_handleAsyncResponse).call(this, response.redirect, snapId);
622
- }
623
- return promise.promise;
624
- }, _SnapKeyring_hasMethod = function _SnapKeyring_hasMethod(account, method) {
625
- return account.methods.includes(method);
626
- }, _SnapKeyring_createRequestPromise = function _SnapKeyring_createRequestPromise(requestId, snapId) {
627
- const promise = new DeferredPromise_1.DeferredPromise();
628
- __classPrivateFieldGet(this, _SnapKeyring_requests, "f").set(requestId, { promise, snapId });
629
- return promise;
630
673
  }, _SnapKeyring_submitSnapRequest =
631
674
  /**
632
675
  * Submits a request to a Snap.
633
676
  *
634
677
  * @param options - The options for the Snap request.
635
678
  * @param options.snapId - The Snap ID to submit the request to.
636
- * @param options.requestId - The unique identifier for the request.
637
679
  * @param options.account - The account to use for the request.
638
680
  * @param options.method - The Ethereum method to call.
639
681
  * @param options.params - The parameters to pass to the method, can be undefined.
640
- * @param options.chainId - The chain ID to use for the request, can be an empty string.
682
+ * @param options.scope - The chain ID to use for the request, can be an empty string.
683
+ * @param options.noPending - Whether the response is allowed to be pending.
641
684
  * @returns A promise that resolves to the keyring response from the Snap.
642
685
  * @throws An error if the Snap fails to respond or if there's an issue with the request submission.
643
686
  */
644
- async function _SnapKeyring_submitSnapRequest({ snapId, requestId, account, method, params, chainId, }) {
687
+ async function _SnapKeyring_submitSnapRequest({ snapId, account, method, params, scope, noPending, }) {
688
+ if (!__classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_hasMethod).call(this, account, method)) {
689
+ throw new Error(`Method '${method}' not supported for account ${account.address}`);
690
+ }
691
+ // Generate a new random request ID to keep track of the request execution flow.
692
+ const requestId = (0, uuid_1.v4)();
693
+ // Create the promise before calling the Snap to prevent a race condition
694
+ // where the Snap responds before we have a chance to create it.
695
+ const requestPromise = __classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_createRequestPromise).call(this, requestId, snapId);
645
696
  try {
646
697
  const request = {
647
698
  id: requestId,
648
- scope: chainId,
699
+ scope,
649
700
  account: account.id,
650
701
  request: {
651
702
  method,
@@ -653,16 +704,43 @@ async function _SnapKeyring_submitSnapRequest({ snapId, requestId, account, meth
653
704
  },
654
705
  };
655
706
  (0, logger_1.projectLogger)('Submit Snap request: ', request);
656
- return await __classPrivateFieldGet(this, _SnapKeyring_snapClient, "f").withSnapId(snapId).submitRequest(request);
707
+ const response = await __classPrivateFieldGet(this, _SnapKeyring_snapClient, "f")
708
+ .withSnapId(snapId)
709
+ .submitRequest(request);
710
+ // Some methods, like the ones used to prepare and patch user operations,
711
+ // require the Snap to answer synchronously in order to work with the
712
+ // confirmation flow. This check lets the caller enforce this behavior.
713
+ if (noPending && response.pending) {
714
+ throw new Error(`Request '${requestId}' to Snap '${snapId}' is pending and noPending is true.`);
715
+ }
716
+ // If the Snap answers synchronously, the promise must be removed from the
717
+ // map to prevent a leak.
718
+ if (!response.pending) {
719
+ return __classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_handleSyncResponse).call(this, response, requestId, snapId);
720
+ }
721
+ // If the Snap answers asynchronously, we will inform the user with a redirect
722
+ if (response.redirect?.message || response.redirect?.url) {
723
+ await __classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_handleAsyncResponse).call(this, response.redirect, snapId);
724
+ }
725
+ return requestPromise.promise;
657
726
  }
658
727
  catch (error) {
659
728
  (0, logger_1.projectLogger)('Snap Request failed: ', { requestId });
660
729
  // If the Snap failed to respond, delete the promise to prevent a leak.
661
- __classPrivateFieldGet(this, _SnapKeyring_requests, "f").delete(snapId, requestId);
730
+ __classPrivateFieldGet(this, _SnapKeyring_instances, "m", _SnapKeyring_clearRequestPromise).call(this, requestId, snapId);
662
731
  throw error;
663
732
  }
733
+ }, _SnapKeyring_hasMethod = function _SnapKeyring_hasMethod(account, method) {
734
+ return account.methods.includes(method);
735
+ }, _SnapKeyring_createRequestPromise = function _SnapKeyring_createRequestPromise(requestId, snapId) {
736
+ const promise = new DeferredPromise_1.DeferredPromise();
737
+ __classPrivateFieldGet(this, _SnapKeyring_requests, "f").set(requestId, { promise, snapId });
738
+ return promise;
739
+ }, _SnapKeyring_clearRequestPromise = function _SnapKeyring_clearRequestPromise(requestId, snapId) {
740
+ __classPrivateFieldGet(this, _SnapKeyring_requests, "f").delete(snapId, requestId);
664
741
  }, _SnapKeyring_handleSyncResponse = function _SnapKeyring_handleSyncResponse(response, requestId, snapId) {
665
742
  __classPrivateFieldGet(this, _SnapKeyring_requests, "f").delete(snapId, requestId);
743
+ // We consider `Response` to be compatible with `result` here.
666
744
  return response.result;
667
745
  }, _SnapKeyring_handleAsyncResponse =
668
746
  /**
@@ -694,6 +772,26 @@ async function _SnapKeyring_handleAsyncResponse(redirect, snapId) {
694
772
  if (!allowedOrigins.includes(origin)) {
695
773
  throw new Error(`Redirect URL domain '${origin}' is not an allowed origin by snap '${snapId}'`);
696
774
  }
775
+ }, _SnapKeyring_deleteAccount =
776
+ /**
777
+ * Removes an account.
778
+ *
779
+ * @param snapId - Snap ID.
780
+ * @param account - Account to delete.
781
+ */
782
+ async function _SnapKeyring_deleteAccount(snapId, account) {
783
+ // Always remove the account from the maps, even if the Snap is going to
784
+ // fail to delete it.
785
+ __classPrivateFieldGet(this, _SnapKeyring_accounts, "f").delete(snapId, account.id);
786
+ try {
787
+ await __classPrivateFieldGet(this, _SnapKeyring_snapClient, "f").withSnapId(snapId).deleteAccount(account.id);
788
+ }
789
+ catch (error) {
790
+ // If the Snap failed to delete the account, log the error and continue
791
+ // with the account deletion, otherwise the account will be stuck in the
792
+ // keyring.
793
+ console.error(`Account '${account.address}' may not have been removed from snap '${snapId}':`, error);
794
+ }
697
795
  }, _SnapKeyring_resolveAddress = function _SnapKeyring_resolveAddress(address) {
698
796
  return ([...__classPrivateFieldGet(this, _SnapKeyring_accounts, "f").values()].find(({ account }) => (0, util_1.equalsIgnoreCase)(account.address, address)) ?? (0, util_1.throwError)(`Account '${address}' not found`));
699
797
  }, _SnapKeyring_getSnap = function _SnapKeyring_getSnap(snapId) {