From a94815d7eae2f6cae846f4875708959aa8db042a Mon Sep 17 00:00:00 2001 From: Maarten Zuidhoorn Date: Tue, 7 Jan 2025 14:13:43 +0100 Subject: [PATCH 01/12] Cache snap state in memory --- .../src/snaps/SnapController.test.tsx | 18 ++- .../src/snaps/SnapController.ts | 128 +++++++++++++++--- .../src/test-utils/controller.ts | 31 ++++- packages/snaps-utils/package.json | 1 + packages/snaps-utils/src/index.ts | 1 + packages/snaps-utils/src/mutex.ts | 24 ++++ yarn.lock | 1 + 7 files changed, 186 insertions(+), 18 deletions(-) create mode 100644 packages/snaps-utils/src/mutex.ts diff --git a/packages/snaps-controllers/src/snaps/SnapController.test.tsx b/packages/snaps-controllers/src/snaps/SnapController.test.tsx index a99703d581..0e28c6f207 100644 --- a/packages/snaps-controllers/src/snaps/SnapController.test.tsx +++ b/packages/snaps-controllers/src/snaps/SnapController.test.tsx @@ -97,6 +97,7 @@ import { MOCK_WALLET_SNAP_PERMISSION, MockSnapsRegistry, sleep, + waitForStateChange, } from '../test-utils'; import { delay } from '../utils'; import { LEGACY_ENCRYPTION_KEY_DERIVATION_OPTIONS } from './constants'; @@ -8801,6 +8802,7 @@ describe('SnapController', () => { ); const newState = { myVariable: 2 }; + const promise = waitForStateChange(messenger); await messenger.call( 'SnapController:updateSnapState', @@ -8817,6 +8819,8 @@ describe('SnapController', () => { DEFAULT_ENCRYPTION_KEY_DERIVATION_OPTIONS, ); + await promise; + const result = await messenger.call( 'SnapController:getSnapState', MOCK_SNAP_ID, @@ -8831,7 +8835,7 @@ describe('SnapController', () => { snapController.destroy(); }); - it('different snaps use different encryption keys', async () => { + it('uses different encryption keys for different snaps', async () => { const messenger = getSnapControllerMessenger(); const state = { foo: 'bar' }; @@ -8857,6 +8861,8 @@ describe('SnapController', () => { true, ); + const promise = waitForStateChange(messenger); + await messenger.call( 'SnapController:updateSnapState', MOCK_LOCAL_SNAP_ID, @@ -8864,6 +8870,8 @@ describe('SnapController', () => { true, ); + await promise; + const encryptedState1 = await encrypt( ENCRYPTION_KEY, state, @@ -9073,6 +9081,8 @@ describe('SnapController', () => { undefined, DEFAULT_ENCRYPTION_KEY_DERIVATION_OPTIONS, ); + + const promise = waitForStateChange(messenger); await messenger.call( 'SnapController:updateSnapState', MOCK_SNAP_ID, @@ -9080,6 +9090,8 @@ describe('SnapController', () => { true, ); + await promise; + expect(updateSnapStateSpy).toHaveBeenCalledTimes(1); expect(snapController.state.snapStates[MOCK_SNAP_ID]).toStrictEqual( mockEncryptedState, @@ -9137,6 +9149,8 @@ describe('SnapController', () => { ); const state = { foo: 'bar' }; + + const promise = waitForStateChange(messenger); await messenger.call( 'SnapController:updateSnapState', MOCK_SNAP_ID, @@ -9144,6 +9158,8 @@ describe('SnapController', () => { true, ); + await promise; + expect(pbkdf2Sha512).toHaveBeenCalledTimes(1); snapController.destroy(); diff --git a/packages/snaps-controllers/src/snaps/SnapController.ts b/packages/snaps-controllers/src/snaps/SnapController.ts index 173e8ba0d1..edb3da0efb 100644 --- a/packages/snaps-controllers/src/snaps/SnapController.ts +++ b/packages/snaps-controllers/src/snaps/SnapController.ts @@ -65,6 +65,7 @@ import type { TruncatedSnapFields, } from '@metamask/snaps-utils'; import { + withMutex, logWarning, getPlatformVersion, assertIsSnapManifest, @@ -252,6 +253,16 @@ export interface SnapRuntimeData { * A boolean flag to determine whether the Snap is currently being stopped. */ stopping: boolean; + + /** + * Cached encrypted state of the Snap. + */ + state: Record | null; + + /** + * Cached unencrypted state of the Snap. + */ + unencryptedState: Record | null; } export type SnapError = { @@ -906,6 +917,7 @@ export class SnapController extends BaseController< this._onOutboundResponse = this._onOutboundResponse.bind(this); this.#rollbackSnapshots = new Map(); this.#snapsRuntimeData = new Map(); + this.#pollForLastRequestStatus(); /* eslint-disable @typescript-eslint/unbound-method */ @@ -1860,6 +1872,68 @@ export class SnapController extends BaseController< return JSON.stringify(encryptedState); } + /** + * Get the new Snap state to persist based on the given state and encryption + * flag. + * + * - If the state is null, return null. + * - If the state should not be encrypted, return the JSON stringified state. + * - Otherwise if the state should be encrypted, return the encrypted state. + * + * @param snapId - The Snap ID. + * @param state - The state to persist. + * @param encrypted - A flag to indicate whether to use encrypted storage or + * not. + * @returns The state to persist. + */ + async #getStateToPersist( + snapId: SnapId, + state: Record | null, + encrypted: boolean, + ) { + if (state === null) { + return null; + } + + if (encrypted) { + return await this.#encryptSnapState(snapId, state); + } + + return JSON.stringify(state); + } + + /** + * Persist the state of a Snap. + * + * @param snapId - The Snap ID. + * @param newSnapState - The new state of the Snap. + * @param encrypted - A flag to indicate whether to use encrypted storage or + * not. + */ + #persistSnapState = withMutex( + async ( + snapId: SnapId, + newSnapState: Record | null, + encrypted: boolean, + ) => { + const newState = await this.#getStateToPersist( + snapId, + newSnapState, + encrypted, + ); + + if (encrypted) { + return this.update((state) => { + state.snapStates[snapId] = newState; + }); + } + + return this.update((state) => { + state.unencryptedSnapStates[snapId] = newState; + }); + }, + ); + /** * Updates the own state of the snap with the given id. * This is distinct from the state MetaMask uses to manage snaps. @@ -1873,17 +1947,19 @@ export class SnapController extends BaseController< newSnapState: Record, encrypted: boolean, ) { - if (encrypted) { - const encryptedState = await this.#encryptSnapState(snapId, newSnapState); + const runtime = this.#getRuntimeExpect(snapId); - this.update((state) => { - state.snapStates[snapId] = encryptedState; - }); + if (encrypted) { + runtime.state = newSnapState; } else { - this.update((state) => { - state.unencryptedSnapStates[snapId] = JSON.stringify(newSnapState); - }); + runtime.unencryptedState = newSnapState; } + + // This is intentionally run asynchronously to avoid blocking the main + // thread. + this.#persistSnapState(snapId, newSnapState, encrypted).catch((error) => { + logError(error); + }); } /** @@ -1894,12 +1970,17 @@ export class SnapController extends BaseController< * @param encrypted - A flag to indicate whether to use encrypted storage or not. */ clearSnapState(snapId: SnapId, encrypted: boolean) { - this.update((state) => { - if (encrypted) { - state.snapStates[snapId] = null; - } else { - state.unencryptedSnapStates[snapId] = null; - } + const runtime = this.#getRuntimeExpect(snapId); + if (encrypted) { + runtime.state = null; + } else { + runtime.unencryptedState = null; + } + + // This is intentionally run asynchronously to avoid blocking the main + // thread. + this.#persistSnapState(snapId, null, encrypted).catch((error) => { + logError(error); }); } @@ -1912,6 +1993,13 @@ export class SnapController extends BaseController< * @returns The requested snap state or null if no state exists. */ async getSnapState(snapId: SnapId, encrypted: boolean): Promise { + const runtime = this.#getRuntimeExpect(snapId); + const cachedState = encrypted ? runtime.state : runtime.unencryptedState; + + if (cachedState !== undefined) { + return cachedState; + } + const state = encrypted ? this.state.snapStates[snapId] : this.state.unencryptedSnapStates[snapId]; @@ -1921,11 +2009,17 @@ export class SnapController extends BaseController< } if (!encrypted) { - // For performance reasons, we do not validate that the state is JSON, since we control serialization. - return JSON.parse(state); + // For performance reasons, we do not validate that the state is JSON, + // since we control serialization. + const json = JSON.parse(state); + runtime.unencryptedState = json; + + return json; } const decrypted = await this.#decryptSnapState(snapId, state); + runtime.state = decrypted; + return decrypted; } @@ -3706,6 +3800,8 @@ export class SnapController extends BaseController< pendingOutboundRequests: 0, interpreter, stopping: false, + state: undefined, + unencryptedState: undefined, }); } diff --git a/packages/snaps-controllers/src/test-utils/controller.ts b/packages/snaps-controllers/src/test-utils/controller.ts index d1d12b701c..9040a30d68 100644 --- a/packages/snaps-controllers/src/test-utils/controller.ts +++ b/packages/snaps-controllers/src/test-utils/controller.ts @@ -1,4 +1,8 @@ import type { ApprovalRequest } from '@metamask/approval-controller'; +import type { + ControllerMessenger, + RestrictedControllerMessenger, +} from '@metamask/base-controller'; import { encryptWithKey, decryptWithKey, @@ -48,16 +52,17 @@ import type { SnapInterfaceControllerEvents, StoredInterface, } from '../interface/SnapInterfaceController'; +import { SnapController } from '../snaps'; import type { AllowedActions, AllowedEvents, PersistedSnapControllerState, SnapControllerActions, SnapControllerEvents, + SnapControllerStateChangeEvent, SnapsRegistryActions, SnapsRegistryEvents, } from '../snaps'; -import { SnapController } from '../snaps'; import type { KeyDerivationOptions } from '../types'; import { MOCK_CRONJOB_PERMISSION } from './cronjob'; import { getNodeEES, getNodeEESMessenger } from './execution-environment'; @@ -830,3 +835,27 @@ export const getRestrictedSnapInsightsControllerMessenger = ( return controllerMessenger; }; + +/** + * Wait for the state change event to be emitted by the messenger. + * + * @param messenger - The messenger to listen to. + * @returns A promise that resolves when the state change event is emitted. + */ +export async function waitForStateChange( + messenger: + | ControllerMessenger + | RestrictedControllerMessenger< + 'SnapController', + any, + SnapControllerStateChangeEvent, + any, + 'SnapController:stateChange' + >, +) { + return new Promise((resolve) => { + messenger.subscribe('SnapController:stateChange', () => { + resolve(); + }); + }); +} diff --git a/packages/snaps-utils/package.json b/packages/snaps-utils/package.json index 442d27174e..71e8474e54 100644 --- a/packages/snaps-utils/package.json +++ b/packages/snaps-utils/package.json @@ -90,6 +90,7 @@ "@metamask/utils": "^10.0.0", "@noble/hashes": "^1.3.1", "@scure/base": "^1.1.1", + "async-mutex": "^0.4.0", "chalk": "^4.1.2", "cron-parser": "^4.5.0", "fast-deep-equal": "^3.1.3", diff --git a/packages/snaps-utils/src/index.ts b/packages/snaps-utils/src/index.ts index 7cfb076bdf..e2db82cee4 100644 --- a/packages/snaps-utils/src/index.ts +++ b/packages/snaps-utils/src/index.ts @@ -20,6 +20,7 @@ export * from './json-rpc'; export * from './localization'; export * from './logging'; export * from './manifest'; +export * from './mutex'; export * from './namespace'; export * from './path'; export * from './platform-version'; diff --git a/packages/snaps-utils/src/mutex.ts b/packages/snaps-utils/src/mutex.ts new file mode 100644 index 0000000000..a1899bbc8c --- /dev/null +++ b/packages/snaps-utils/src/mutex.ts @@ -0,0 +1,24 @@ +import { Mutex } from 'async-mutex'; + +/** + * Run a function with an async mutex, ensuring that only one instance of the + * function can run at a time. + * + * @param fn - The function to run with a mutex. + * @returns The wrapped function. + * @template OriginalFunction - The original function type. This is inferred + * from the `fn` argument, and used to determine the return type of the + * wrapped function. + */ +export function withMutex< + OriginalFunction extends (...args: any[]) => Promise, + Type, +>( + fn: OriginalFunction, +): (...args: Parameters) => Promise { + const mutex = new Mutex(); + + return async (...args: Parameters) => { + return await mutex.runExclusive(async () => await fn(...args)); + }; +} diff --git a/yarn.lock b/yarn.lock index 7451b63e19..29065c5256 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6281,6 +6281,7 @@ __metadata: "@wdio/spec-reporter": "npm:^8.19.0" "@wdio/static-server-service": "npm:^8.19.0" "@wdio/types": "npm:^8.19.0" + async-mutex: "npm:^0.4.0" chalk: "npm:^4.1.2" cron-parser: "npm:^4.5.0" deepmerge: "npm:^4.2.2" From 8aa093cf26bcb0f326a1f8265712e43a202dc380 Mon Sep 17 00:00:00 2001 From: Maarten Zuidhoorn Date: Tue, 7 Jan 2025 14:15:02 +0100 Subject: [PATCH 02/12] Make state fields optional in runtime data --- packages/snaps-controllers/src/snaps/SnapController.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/snaps-controllers/src/snaps/SnapController.ts b/packages/snaps-controllers/src/snaps/SnapController.ts index edb3da0efb..bb25357596 100644 --- a/packages/snaps-controllers/src/snaps/SnapController.ts +++ b/packages/snaps-controllers/src/snaps/SnapController.ts @@ -257,12 +257,12 @@ export interface SnapRuntimeData { /** * Cached encrypted state of the Snap. */ - state: Record | null; + state?: Record | null; /** * Cached unencrypted state of the Snap. */ - unencryptedState: Record | null; + unencryptedState?: Record | null; } export type SnapError = { @@ -3800,8 +3800,6 @@ export class SnapController extends BaseController< pendingOutboundRequests: 0, interpreter, stopping: false, - state: undefined, - unencryptedState: undefined, }); } From 8f9b00d297033b14406590e273c615f20980a5d3 Mon Sep 17 00:00:00 2001 From: Maarten Zuidhoorn Date: Wed, 8 Jan 2025 14:05:02 +0100 Subject: [PATCH 03/12] Add more tests --- packages/snaps-controllers/coverage.json | 6 +- .../src/snaps/SnapController.test.tsx | 71 +++++++++++++++++++ .../src/test-utils/controller.ts | 6 +- packages/snaps-utils/coverage.json | 6 +- packages/snaps-utils/src/mutex.test.ts | 38 ++++++++++ 5 files changed, 120 insertions(+), 7 deletions(-) create mode 100644 packages/snaps-utils/src/mutex.test.ts diff --git a/packages/snaps-controllers/coverage.json b/packages/snaps-controllers/coverage.json index 357e17f097..1f48e5bdf6 100644 --- a/packages/snaps-controllers/coverage.json +++ b/packages/snaps-controllers/coverage.json @@ -1,6 +1,6 @@ { - "branches": 93.06, + "branches": 92.41, "functions": 96.54, - "lines": 98.02, - "statements": 97.74 + "lines": 97.99, + "statements": 97.71 } diff --git a/packages/snaps-controllers/src/snaps/SnapController.test.tsx b/packages/snaps-controllers/src/snaps/SnapController.test.tsx index 0e28c6f207..9f6b494f9b 100644 --- a/packages/snaps-controllers/src/snaps/SnapController.test.tsx +++ b/packages/snaps-controllers/src/snaps/SnapController.test.tsx @@ -60,6 +60,7 @@ import { AssertionError, base64ToBytes, stringToBytes, + createDeferredPromise, } from '@metamask/utils'; import { File } from 'buffer'; import { webcrypto } from 'crypto'; @@ -78,6 +79,7 @@ import { getNodeEESMessenger, getPersistedSnapsState, getSnapController, + getSnapControllerEncryptor, getSnapControllerMessenger, getSnapControllerOptions, getSnapControllerWithEES, @@ -9164,6 +9166,40 @@ describe('SnapController', () => { snapController.destroy(); }); + + it('logs an error message if the state fails to persist', async () => { + const messenger = getSnapControllerMessenger(); + + const errorValue = new Error('Failed to persist state.'); + const snapController = getSnapController( + getSnapControllerOptions({ + messenger, + state: { + snaps: getPersistedSnapsState(), + }, + // @ts-expect-error - Missing required properties. + encryptor: { + ...getSnapControllerEncryptor(), + encryptWithKey: jest.fn().mockRejectedValue(errorValue), + }, + }), + ); + + const { promise, resolve } = createDeferredPromise(); + const error = jest.spyOn(console, 'error').mockImplementation(resolve); + + await messenger.call( + 'SnapController:updateSnapState', + MOCK_SNAP_ID, + { foo: 'bar' }, + true, + ); + + await promise; + expect(error).toHaveBeenCalledWith(errorValue); + + snapController.destroy(); + }); }); describe('SnapController:clearSnapState', () => { @@ -9222,6 +9258,41 @@ describe('SnapController', () => { snapController.destroy(); }); + + it('logs an error message if the state fails to persist', async () => { + const messenger = getSnapControllerMessenger(); + + const errorValue = new Error('Failed to persist state.'); + const snapController = getSnapController( + getSnapControllerOptions({ + messenger, + state: { + snaps: getPersistedSnapsState(), + }, + // @ts-expect-error - Missing required properties. + encryptor: { + ...getSnapControllerEncryptor(), + encryptWithKey: jest.fn().mockRejectedValue(errorValue), + }, + }), + ); + + const { promise, resolve } = createDeferredPromise(); + const error = jest.spyOn(console, 'error').mockImplementation(resolve); + + // @ts-expect-error - Property `update` is protected. + // eslint-disable-next-line jest/prefer-spy-on + snapController.update = jest.fn().mockImplementation(() => { + throw errorValue; + }); + + await messenger.call('SnapController:clearSnapState', MOCK_SNAP_ID, true); + + await promise; + expect(error).toHaveBeenCalledWith(errorValue); + + snapController.destroy(); + }); }); describe('SnapController:updateBlockedSnaps', () => { diff --git a/packages/snaps-controllers/src/test-utils/controller.ts b/packages/snaps-controllers/src/test-utils/controller.ts index 9040a30d68..1602db2e66 100644 --- a/packages/snaps-controllers/src/test-utils/controller.ts +++ b/packages/snaps-controllers/src/test-utils/controller.ts @@ -11,6 +11,8 @@ import { exportKey, generateSalt, isVaultUpdated, + encrypt, + decrypt, } from '@metamask/browser-passworder'; import type { PermissionConstraint, @@ -540,8 +542,10 @@ export const DEFAULT_ENCRYPTION_KEY_DERIVATION_OPTIONS = { }, }; -const getSnapControllerEncryptor = () => { +export const getSnapControllerEncryptor = () => { return { + encrypt, + decrypt, encryptWithKey, decryptWithKey, keyFromPassword: async ( diff --git a/packages/snaps-utils/coverage.json b/packages/snaps-utils/coverage.json index a9248b7438..80c85671b3 100644 --- a/packages/snaps-utils/coverage.json +++ b/packages/snaps-utils/coverage.json @@ -1,6 +1,6 @@ { "branches": 99.74, - "functions": 98.93, - "lines": 99.46, - "statements": 96.31 + "functions": 98.95, + "lines": 99.47, + "statements": 96.33 } diff --git a/packages/snaps-utils/src/mutex.test.ts b/packages/snaps-utils/src/mutex.test.ts new file mode 100644 index 0000000000..794b500962 --- /dev/null +++ b/packages/snaps-utils/src/mutex.test.ts @@ -0,0 +1,38 @@ +import { createDeferredPromise } from '@metamask/utils'; + +import { withMutex } from './mutex'; + +describe('withMutex', () => { + it('runs the function with a mutex', async () => { + jest.useFakeTimers(); + + const { promise, resolve: resolveDeferred } = createDeferredPromise(); + + const fn = jest.fn().mockImplementation(async () => { + return await new Promise((resolve) => { + resolveDeferred(); + setTimeout(() => { + resolve(); + }, 1000); + }); + }); + + const wrappedFn = withMutex(fn); + + const first = wrappedFn(); + const second = wrappedFn(); + + await promise; + jest.advanceTimersByTime(1000); + + expect(fn).toHaveBeenCalledTimes(1); + + await first; + + jest.advanceTimersByTime(1000); + + await second; + + expect(fn).toHaveBeenCalledTimes(2); + }); +}); From b8d7edd66114b4335bc8e521ac74039d8e1cd201 Mon Sep 17 00:00:00 2001 From: Maarten Zuidhoorn Date: Wed, 8 Jan 2025 14:26:06 +0100 Subject: [PATCH 04/12] Update coverage --- packages/snaps-controllers/coverage.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/snaps-controllers/coverage.json b/packages/snaps-controllers/coverage.json index 1f48e5bdf6..81b6406aa8 100644 --- a/packages/snaps-controllers/coverage.json +++ b/packages/snaps-controllers/coverage.json @@ -1,6 +1,6 @@ { "branches": 92.41, - "functions": 96.54, + "functions": 96.56, "lines": 97.99, "statements": 97.71 } From cd4b9b3593f39769b0dfd816c4c92d3719ad40c9 Mon Sep 17 00:00:00 2001 From: Maarten Zuidhoorn Date: Fri, 10 Jan 2025 12:31:48 +0100 Subject: [PATCH 05/12] Clear cache on lock --- packages/snaps-controllers/coverage.json | 4 +- packages/snaps-controllers/package.json | 1 + .../src/snaps/SnapController.test.tsx | 54 ++ .../src/snaps/SnapController.ts | 23 +- .../src/test-utils/controller.ts | 1 + yarn.lock | 591 +++++++++++++++++- 6 files changed, 660 insertions(+), 14 deletions(-) diff --git a/packages/snaps-controllers/coverage.json b/packages/snaps-controllers/coverage.json index 81b6406aa8..609decfdbc 100644 --- a/packages/snaps-controllers/coverage.json +++ b/packages/snaps-controllers/coverage.json @@ -1,6 +1,6 @@ { "branches": 92.41, "functions": 96.56, - "lines": 97.99, - "statements": 97.71 + "lines": 98, + "statements": 97.72 } diff --git a/packages/snaps-controllers/package.json b/packages/snaps-controllers/package.json index ec3fd6f31d..0ac124713b 100644 --- a/packages/snaps-controllers/package.json +++ b/packages/snaps-controllers/package.json @@ -84,6 +84,7 @@ "@metamask/json-rpc-engine": "^10.0.1", "@metamask/json-rpc-middleware-stream": "^8.0.5", "@metamask/key-tree": "^10.0.1", + "@metamask/keyring-controller": "^19.0.2", "@metamask/object-multiplex": "^2.0.0", "@metamask/permission-controller": "^11.0.4", "@metamask/phishing-controller": "^12.3.1", diff --git a/packages/snaps-controllers/src/snaps/SnapController.test.tsx b/packages/snaps-controllers/src/snaps/SnapController.test.tsx index 9f6b494f9b..84ce803216 100644 --- a/packages/snaps-controllers/src/snaps/SnapController.test.tsx +++ b/packages/snaps-controllers/src/snaps/SnapController.test.tsx @@ -52,6 +52,7 @@ import { MOCK_SNAP_NAME, DEFAULT_SOURCE_PATH, DEFAULT_ICON_PATH, + TEST_SECRET_RECOVERY_PHRASE_BYTES, } from '@metamask/snaps-utils/test-utils'; import type { SemVerRange, SemVerVersion, Json } from '@metamask/utils'; import { @@ -2120,6 +2121,59 @@ describe('SnapController', () => { await service.terminateAllSnaps(); }); + it('clears encrypted state of Snaps when the client is locked', async () => { + const rootMessenger = getControllerMessenger(); + const messenger = getSnapControllerMessenger(rootMessenger); + + const state = { myVariable: 1 }; + + const mockEncryptedState = await encrypt( + ENCRYPTION_KEY, + state, + undefined, + undefined, + DEFAULT_ENCRYPTION_KEY_DERIVATION_OPTIONS, + ); + + const getMnemonic = jest + .fn() + .mockReturnValue(TEST_SECRET_RECOVERY_PHRASE_BYTES); + + const snapController = getSnapController( + getSnapControllerOptions({ + messenger, + state: { + snaps: { + [MOCK_SNAP_ID]: getPersistedSnapObject(), + }, + snapStates: { + [MOCK_SNAP_ID]: mockEncryptedState, + }, + }, + getMnemonic, + }), + ); + + expect( + await messenger.call('SnapController:getSnapState', MOCK_SNAP_ID, true), + ).toStrictEqual(state); + expect(getMnemonic).toHaveBeenCalledTimes(1); + + rootMessenger.publish('KeyringController:lock'); + + expect( + await messenger.call('SnapController:getSnapState', MOCK_SNAP_ID, true), + ).toStrictEqual(state); + + // We assume `getMnemonic` is called again because the controller needs to + // decrypt the state again. This is not an ideal way to test this, but it + // is the easiest to test this without exposing the internal state of the + // `SnapController`. + expect(getMnemonic).toHaveBeenCalledTimes(2); + + snapController.destroy(); + }); + describe('handleRequest', () => { it.each( Object.keys(handlerEndowments).filter( diff --git a/packages/snaps-controllers/src/snaps/SnapController.ts b/packages/snaps-controllers/src/snaps/SnapController.ts index bb25357596..901128c258 100644 --- a/packages/snaps-controllers/src/snaps/SnapController.ts +++ b/packages/snaps-controllers/src/snaps/SnapController.ts @@ -9,6 +9,7 @@ import type { } from '@metamask/base-controller'; import { BaseController } from '@metamask/base-controller'; import type { CryptographicFunctions } from '@metamask/key-tree'; +import type { KeyringControllerLockEvent } from '@metamask/keyring-controller'; import type { Caveat, GetEndowments, @@ -607,7 +608,8 @@ export type AllowedActions = export type AllowedEvents = | ExecutionServiceEvents | SnapInstalled - | SnapUpdated; + | SnapUpdated + | KeyringControllerLockEvent; type SnapControllerMessenger = RestrictedControllerMessenger< typeof controllerName, @@ -967,6 +969,11 @@ export class SnapController extends BaseController< }, ); + this.messagingSystem.subscribe( + 'KeyringController:lock', + this.#handleLock.bind(this), + ); + this.#initializeStateMachine(); this.#registerMessageHandlers(); @@ -1832,6 +1839,7 @@ export class SnapController extends BaseController< const useCache = this.#hasCachedEncryptionKey(snapId) || this.#encryptor.isVaultUpdated(state); + const { key } = await this.#getSnapEncryptionKey({ snapId, salt, @@ -4007,4 +4015,17 @@ export class SnapController extends BaseController< }, }); } + + /** + * Handle the `KeyringController:lock` event. + * + * Currently this clears the cached encrypted state (if any) for all Snaps. + */ + #handleLock() { + for (const runtime of this.#snapsRuntimeData.values()) { + runtime.encryptionKey = null; + runtime.encryptionSalt = null; + runtime.state = undefined; + } + } } diff --git a/packages/snaps-controllers/src/test-utils/controller.ts b/packages/snaps-controllers/src/test-utils/controller.ts index 1602db2e66..1f2abe725d 100644 --- a/packages/snaps-controllers/src/test-utils/controller.ts +++ b/packages/snaps-controllers/src/test-utils/controller.ts @@ -469,6 +469,7 @@ export const getSnapControllerMessenger = ( 'SnapController:snapUpdated', 'SnapController:stateChange', 'SnapController:snapRolledback', + 'KeyringController:lock', ], allowedActions: [ 'ApprovalController:addRequest', diff --git a/yarn.lock b/yarn.lock index 29065c5256..3cd3d672ea 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3230,7 +3230,7 @@ __metadata: languageName: node linkType: hard -"@ethereumjs/tx@npm:^4.2.0": +"@ethereumjs/tx@npm:^4.0.2, @ethereumjs/tx@npm:^4.2.0": version: 4.2.0 resolution: "@ethereumjs/tx@npm:4.2.0" dependencies: @@ -3242,7 +3242,7 @@ __metadata: languageName: node linkType: hard -"@ethereumjs/util@npm:^8.1.0": +"@ethereumjs/util@npm:^8.0.0, @ethereumjs/util@npm:^8.1.0": version: 8.1.0 resolution: "@ethereumjs/util@npm:8.1.0" dependencies: @@ -3762,6 +3762,64 @@ __metadata: languageName: node linkType: hard +"@keystonehq/alias-sampling@npm:^0.1.1": + version: 0.1.2 + resolution: "@keystonehq/alias-sampling@npm:0.1.2" + checksum: 10/4dfdfb91e070b1d9f28058c92b5b8fad81696ac63bd432cd6bd359f2ab92eb50df75e8c5da1f75a351756387e9902f043b3ecc2cbf662c9c9456ecacc848abfd + languageName: node + linkType: hard + +"@keystonehq/base-eth-keyring@npm:^0.14.1": + version: 0.14.1 + resolution: "@keystonehq/base-eth-keyring@npm:0.14.1" + dependencies: + "@ethereumjs/tx": "npm:^4.0.2" + "@ethereumjs/util": "npm:^8.0.0" + "@keystonehq/bc-ur-registry-eth": "npm:^0.19.1" + hdkey: "npm:^2.0.1" + rlp: "npm:^3.0.0" + uuid: "npm:^8.3.2" + checksum: 10/07516e967fc5c618ef0ce67b155ba69c04f8fd84d5a6fd35f025f989c41256c9e6fa0375cfb0318da42876a61c64839e312d910e4b9fa801f86179df826adc69 + languageName: node + linkType: hard + +"@keystonehq/bc-ur-registry-eth@npm:^0.19.1": + version: 0.19.1 + resolution: "@keystonehq/bc-ur-registry-eth@npm:0.19.1" + dependencies: + "@ethereumjs/util": "npm:^8.0.0" + "@keystonehq/bc-ur-registry": "npm:^0.6.0" + hdkey: "npm:^2.0.1" + uuid: "npm:^8.3.2" + checksum: 10/7e64e6a754e6b66fc83a8f3880b54828c5b37f4eaaea3287eee31bd9d9b5ac0ba4cd4b8e751af9bd2f66e6f19291eaf02f46cd177d05ed9b30c1349cdd04572f + languageName: node + linkType: hard + +"@keystonehq/bc-ur-registry@npm:^0.6.0": + version: 0.6.4 + resolution: "@keystonehq/bc-ur-registry@npm:0.6.4" + dependencies: + "@ngraveio/bc-ur": "npm:^1.1.5" + bs58check: "npm:^2.1.2" + tslib: "npm:^2.3.0" + checksum: 10/d4cdbefc14f3305543340d509564e1a795eb458327d46aad8665927999150df7e282939dcb714b81fea386061019e3b9f41eedbbb09a59d404355711c33159b2 + languageName: node + linkType: hard + +"@keystonehq/metamask-airgapped-keyring@npm:^0.14.1": + version: 0.14.1 + resolution: "@keystonehq/metamask-airgapped-keyring@npm:0.14.1" + dependencies: + "@ethereumjs/tx": "npm:^4.0.2" + "@keystonehq/base-eth-keyring": "npm:^0.14.1" + "@keystonehq/bc-ur-registry-eth": "npm:^0.19.1" + "@metamask/obs-store": "npm:^9.0.0" + rlp: "npm:^2.2.6" + uuid: "npm:^8.3.2" + checksum: 10/8e34be8813c51488c7dc9b641ed17258740dda45fb72fe48670b077ecfb92273e0c5a2fbbab121b01d7e0906a3ec512f261fceb95da8089550021ab6a0c89c6b + languageName: node + linkType: hard + "@lavamoat/aa@npm:^4.1.0, @lavamoat/aa@npm:^4.2.0": version: 4.2.0 resolution: "@lavamoat/aa@npm:4.2.0" @@ -3853,6 +3911,16 @@ __metadata: languageName: node linkType: hard +"@metamask/abi-utils@npm:^3.0.0": + version: 3.0.0 + resolution: "@metamask/abi-utils@npm:3.0.0" + dependencies: + "@metamask/superstruct": "npm:^3.1.0" + "@metamask/utils": "npm:^11.0.1" + checksum: 10/068b98185148b9e185b4af4392c6a6f82f1d4b1ff60013c57679c618f37afe9030e3ccc940e1a8b690be6f62ea91115ab18b73f3c3c09f4eff1794e31ababb9b + languageName: node + linkType: hard + "@metamask/action-utils@npm:^1.0.0": version: 1.1.1 resolution: "@metamask/action-utils@npm:1.1.1" @@ -3998,6 +4066,15 @@ __metadata: languageName: unknown linkType: soft +"@metamask/browser-passworder@npm:^4.3.0": + version: 4.3.0 + resolution: "@metamask/browser-passworder@npm:4.3.0" + dependencies: + "@metamask/utils": "npm:^8.2.0" + checksum: 10/8ba5c50cd6274b0cc0f90a1ee16b960ee150f14c29083f3515f4abe018a28ead32c21f5f4a62a6e27a946b1228adc2ff1f195e71e38782fa39fa8fff116173e6 + languageName: node + linkType: hard + "@metamask/browser-passworder@npm:^5.0.1": version: 5.0.1 resolution: "@metamask/browser-passworder@npm:5.0.1" @@ -4476,6 +4553,19 @@ __metadata: languageName: node linkType: hard +"@metamask/eth-hd-keyring@npm:^7.0.4": + version: 7.0.4 + resolution: "@metamask/eth-hd-keyring@npm:7.0.4" + dependencies: + "@ethereumjs/util": "npm:^8.1.0" + "@metamask/eth-sig-util": "npm:^7.0.3" + "@metamask/scure-bip39": "npm:^2.1.1" + "@metamask/utils": "npm:^9.2.1" + ethereum-cryptography: "npm:^2.1.2" + checksum: 10/493d06f55225b6f9da48ee001486e18898d6a4a3afd2cf40ff1dcae2ece42d5e96174f6a05b7c39419cb3531b530c8af294d9422195661788c5e0b687a328874 + languageName: node + linkType: hard + "@metamask/eth-json-rpc-middleware@npm:^15.0.0": version: 15.0.0 resolution: "@metamask/eth-json-rpc-middleware@npm:15.0.0" @@ -4532,6 +4622,33 @@ __metadata: languageName: node linkType: hard +"@metamask/eth-sig-util@npm:^8.0.0": + version: 8.1.2 + resolution: "@metamask/eth-sig-util@npm:8.1.2" + dependencies: + "@ethereumjs/util": "npm:^8.1.0" + "@metamask/abi-utils": "npm:^3.0.0" + "@metamask/utils": "npm:^11.0.1" + "@scure/base": "npm:~1.1.3" + ethereum-cryptography: "npm:^2.1.2" + tweetnacl: "npm:^1.0.3" + checksum: 10/32b284fc8c3229e3741b1c21f44ca3f55c2215ef8ad700775cd9501bbaab56a4e861827bef24ed263734d28c899eb3b34a9646e9d21ec3fce12204b7eb58bfed + languageName: node + linkType: hard + +"@metamask/eth-simple-keyring@npm:^6.0.5": + version: 6.0.5 + resolution: "@metamask/eth-simple-keyring@npm:6.0.5" + dependencies: + "@ethereumjs/util": "npm:^8.1.0" + "@metamask/eth-sig-util": "npm:^7.0.3" + "@metamask/utils": "npm:^9.2.1" + ethereum-cryptography: "npm:^2.1.2" + randombytes: "npm:^2.1.0" + checksum: 10/98b7bd00df25e7630324e2c762e3a03a7f199108a4dfe22e5a1938f1d01c9b2cd64ab4bb6fd242bf898624903d5a68a2e1f61c95f94a141266ab23dae8d97d21 + languageName: node + linkType: hard + "@metamask/ethereum-provider-example-snap@workspace:^, @metamask/ethereum-provider-example-snap@workspace:packages/examples/packages/ethereum-provider": version: 0.0.0-use.local resolution: "@metamask/ethereum-provider-example-snap@workspace:packages/examples/packages/ethereum-provider" @@ -5058,6 +5175,74 @@ __metadata: languageName: node linkType: hard +"@metamask/keyring-api@npm:^12.0.0": + version: 12.0.0 + resolution: "@metamask/keyring-api@npm:12.0.0" + dependencies: + "@metamask/keyring-utils": "npm:^1.0.0" + "@metamask/superstruct": "npm:^3.1.0" + "@metamask/utils": "npm:^9.3.0" + bech32: "npm:^2.0.0" + checksum: 10/ba8b75c55d3fcb9f8b52c58ff141cba81f7c416c3fa684e089965717ea129d50e8df7a73e7ab1c96eaf59d70b6e2dd8a618434939b75ef0d3402b547b5196877 + languageName: node + linkType: hard + +"@metamask/keyring-api@npm:^13.0.0": + version: 13.0.0 + resolution: "@metamask/keyring-api@npm:13.0.0" + dependencies: + "@metamask/keyring-utils": "npm:^1.0.0" + "@metamask/superstruct": "npm:^3.1.0" + "@metamask/utils": "npm:^11.0.1" + bech32: "npm:^2.0.0" + checksum: 10/f7e8982112a2813790354267af8f79cbf241d7ca9d733fe5e8de1a13993203b154ac6d358a92bd4340cbd4d25f4bac48681d17a25e3dc6f2336c95c00c371686 + languageName: node + linkType: hard + +"@metamask/keyring-controller@npm:^19.0.2": + version: 19.0.2 + resolution: "@metamask/keyring-controller@npm:19.0.2" + dependencies: + "@ethereumjs/util": "npm:^8.1.0" + "@keystonehq/metamask-airgapped-keyring": "npm:^0.14.1" + "@metamask/base-controller": "npm:^7.0.2" + "@metamask/browser-passworder": "npm:^4.3.0" + "@metamask/eth-hd-keyring": "npm:^7.0.4" + "@metamask/eth-sig-util": "npm:^8.0.0" + "@metamask/eth-simple-keyring": "npm:^6.0.5" + "@metamask/keyring-api": "npm:^12.0.0" + "@metamask/keyring-internal-api": "npm:^1.0.0" + "@metamask/message-manager": "npm:^11.0.3" + "@metamask/utils": "npm:^10.0.0" + async-mutex: "npm:^0.5.0" + ethereumjs-wallet: "npm:^1.0.1" + immer: "npm:^9.0.6" + checksum: 10/bbc140db91902dc8a9b8e1220d3c07e91858d68b9888d26df74fe300b1baf1331c5df62dde4ea71be77b485a0bca314bfc899358375490c7dbce594810bc74b6 + languageName: node + linkType: hard + +"@metamask/keyring-internal-api@npm:^1.0.0": + version: 1.1.0 + resolution: "@metamask/keyring-internal-api@npm:1.1.0" + dependencies: + "@metamask/keyring-api": "npm:^13.0.0" + "@metamask/keyring-utils": "npm:^1.0.0" + "@metamask/superstruct": "npm:^3.1.0" + "@metamask/utils": "npm:^11.0.1" + checksum: 10/5db127cfe319c289b95d55d08b6737820f00761564e219d756a55e030b83043fb43d18bac3b63fcc69cd2e03129f831410ce960ba954d8183d976d87a6781b8c + languageName: node + linkType: hard + +"@metamask/keyring-utils@npm:^1.0.0": + version: 1.0.0 + resolution: "@metamask/keyring-utils@npm:1.0.0" + dependencies: + "@metamask/superstruct": "npm:^3.1.0" + "@metamask/utils": "npm:^9.3.0" + checksum: 10/f74f7343a7154b029e0fa4c25735c589eba4dc25a9e323d43b7c733ce5dbb23ce603a4f02aac455163993649ceeaf714b8b843985ba7a9cb00b926b3b8dc6b51 + languageName: node + linkType: hard + "@metamask/lifecycle-hooks-example-snap@workspace:^, @metamask/lifecycle-hooks-example-snap@workspace:packages/examples/packages/lifecycle-hooks": version: 0.0.0-use.local resolution: "@metamask/lifecycle-hooks-example-snap@workspace:packages/examples/packages/lifecycle-hooks" @@ -5170,6 +5355,21 @@ __metadata: languageName: unknown linkType: soft +"@metamask/message-manager@npm:^11.0.3": + version: 11.0.3 + resolution: "@metamask/message-manager@npm:11.0.3" + dependencies: + "@metamask/base-controller": "npm:^7.0.2" + "@metamask/controller-utils": "npm:^11.4.4" + "@metamask/eth-sig-util": "npm:^8.0.0" + "@metamask/utils": "npm:^10.0.0" + "@types/uuid": "npm:^8.3.0" + jsonschema: "npm:^1.4.1" + uuid: "npm:^8.3.2" + checksum: 10/18f5b0091474fc45e854db9c91ad8f5f898017ba9a08b01c770904ad3929b004c65298fe246b989f3c92fd2f03b5932dfa31adadb95ca81ce6940cb28a2ec793 + languageName: node + linkType: hard + "@metamask/name-lookup-example-snap@workspace:^, @metamask/name-lookup-example-snap@workspace:packages/examples/packages/name-lookup": version: 0.0.0-use.local resolution: "@metamask/name-lookup-example-snap@workspace:packages/examples/packages/name-lookup" @@ -5303,6 +5503,16 @@ __metadata: languageName: node linkType: hard +"@metamask/obs-store@npm:^9.0.0": + version: 9.1.0 + resolution: "@metamask/obs-store@npm:9.1.0" + dependencies: + "@metamask/safe-event-emitter": "npm:^3.0.0" + readable-stream: "npm:^3.6.2" + checksum: 10/fa37a0b9e1e25f54c93a8f16449b3fc771c15b70d79d590cf41e22f871a19d673bcfd0d08b044093235d10d0bb031b47b8209d89997def0cb256abe3e371064f + languageName: node + linkType: hard + "@metamask/permission-controller@npm:^11.0.4": version: 11.0.4 resolution: "@metamask/permission-controller@npm:11.0.4" @@ -5719,6 +5929,7 @@ __metadata: "@metamask/json-rpc-engine": "npm:^10.0.1" "@metamask/json-rpc-middleware-stream": "npm:^8.0.5" "@metamask/key-tree": "npm:^10.0.1" + "@metamask/keyring-controller": "npm:^19.0.2" "@metamask/object-multiplex": "npm:^2.0.0" "@metamask/permission-controller": "npm:^11.0.4" "@metamask/phishing-controller": "npm:^12.3.1" @@ -6365,7 +6576,7 @@ __metadata: languageName: unknown linkType: soft -"@metamask/superstruct@npm:^3.1.0": +"@metamask/superstruct@npm:^3.0.0, @metamask/superstruct@npm:^3.1.0": version: 3.1.0 resolution: "@metamask/superstruct@npm:3.1.0" checksum: 10/5066fe228d5f11da387606d7f9545de2b473ab5a9e0f1bb8aea2f52d3e2c9d25e427151acde61f4a2de80a07a9871fe9505ad06abca6a61b7c3b54ed5c403b01 @@ -6499,6 +6710,23 @@ __metadata: languageName: node linkType: hard +"@metamask/utils@npm:^8.2.0": + version: 8.5.0 + resolution: "@metamask/utils@npm:8.5.0" + dependencies: + "@ethereumjs/tx": "npm:^4.2.0" + "@metamask/superstruct": "npm:^3.0.0" + "@noble/hashes": "npm:^1.3.1" + "@scure/base": "npm:^1.1.3" + "@types/debug": "npm:^4.1.7" + debug: "npm:^4.3.4" + pony-cause: "npm:^2.1.10" + semver: "npm:^7.5.4" + uuid: "npm:^9.0.1" + checksum: 10/68a42a55f7dc750b75467fb7c05a496c20dac073a2753e0f4d9642c4d8dcb3f9ddf51a09d30337e11637f1777f3dfe22e15b5159dbafb0fdb7bd8c9236056153 + languageName: node + linkType: hard + "@metamask/utils@npm:^9.0.0, @metamask/utils@npm:^9.1.0": version: 9.2.1 resolution: "@metamask/utils@npm:9.2.1" @@ -6516,6 +6744,23 @@ __metadata: languageName: node linkType: hard +"@metamask/utils@npm:^9.2.1, @metamask/utils@npm:^9.3.0": + version: 9.3.0 + resolution: "@metamask/utils@npm:9.3.0" + dependencies: + "@ethereumjs/tx": "npm:^4.2.0" + "@metamask/superstruct": "npm:^3.1.0" + "@noble/hashes": "npm:^1.3.1" + "@scure/base": "npm:^1.1.3" + "@types/debug": "npm:^4.1.7" + debug: "npm:^4.3.4" + pony-cause: "npm:^2.1.10" + semver: "npm:^7.5.4" + uuid: "npm:^9.0.1" + checksum: 10/ed6648cd973bbf3b4eb0e862903b795a99d27784c820e19f62f0bc0ddf353e98c2858d7e9aaebc0249a586391b344e35b9249d13c08e3ea0c74b23dc1c6b1558 + languageName: node + linkType: hard + "@metamask/wasm-example-snap@workspace:^, @metamask/wasm-example-snap@workspace:packages/examples/packages/wasm": version: 0.0.0-use.local resolution: "@metamask/wasm-example-snap@workspace:packages/examples/packages/wasm" @@ -6679,6 +6924,21 @@ __metadata: languageName: node linkType: hard +"@ngraveio/bc-ur@npm:^1.1.5": + version: 1.1.13 + resolution: "@ngraveio/bc-ur@npm:1.1.13" + dependencies: + "@keystonehq/alias-sampling": "npm:^0.1.1" + assert: "npm:^2.0.0" + bignumber.js: "npm:^9.0.1" + cbor-sync: "npm:^1.0.4" + crc: "npm:^3.8.0" + jsbi: "npm:^3.1.5" + sha.js: "npm:^2.4.11" + checksum: 10/0d3301b673a0bd9a069dae1f017cfd03010fddf19c1449d1a9e986b9b879ee4611f5af690ace9f59b75707573d1d3d6a4983166207db743425974a736689c6a0 + languageName: node + linkType: hard + "@noble/bls12-381@npm:^1.2.0": version: 1.4.0 resolution: "@noble/bls12-381@npm:1.4.0" @@ -7594,6 +7854,15 @@ __metadata: languageName: node linkType: hard +"@types/bn.js@npm:^5.1.0": + version: 5.1.6 + resolution: "@types/bn.js@npm:5.1.6" + dependencies: + "@types/node": "npm:*" + checksum: 10/db565b5a2af59b09459d74441153bf23a0e80f1fb2d070330786054e7ce1a7285dc40afcd8f289426c61a83166bdd70814f70e2d439744686aac5d3ea75daf13 + languageName: node + linkType: hard + "@types/bn.js@npm:^5.1.5": version: 5.1.5 resolution: "@types/bn.js@npm:5.1.5" @@ -8033,6 +8302,15 @@ __metadata: languageName: node linkType: hard +"@types/pbkdf2@npm:^3.0.0": + version: 3.1.2 + resolution: "@types/pbkdf2@npm:3.1.2" + dependencies: + "@types/node": "npm:*" + checksum: 10/bebe1e596cbbe5f7d2726a58859e61986c5a42459048e29cb7f2d4d764be6bbb0844572fd5d70ca8955a8a17e8b4ed80984fc4903e165d9efb8807a3fbb051aa + languageName: node + linkType: hard + "@types/prettier@npm:^2.1.5": version: 2.3.2 resolution: "@types/prettier@npm:2.3.2" @@ -8128,6 +8406,15 @@ __metadata: languageName: node linkType: hard +"@types/secp256k1@npm:^4.0.1": + version: 4.0.6 + resolution: "@types/secp256k1@npm:4.0.6" + dependencies: + "@types/node": "npm:*" + checksum: 10/211f823be990b55612e604d620acf0dc3bc942d3836bdd8da604269effabc86d98161e5947487b4e4e128f9180fc1682daae2f89ea7a4d9648fdfe52fba365fc + languageName: node + linkType: hard + "@types/semver@npm:^7.3.12, @types/semver@npm:^7.3.6, @types/semver@npm:^7.5.0": version: 7.5.0 resolution: "@types/semver@npm:7.5.0" @@ -8219,6 +8506,13 @@ __metadata: languageName: node linkType: hard +"@types/uuid@npm:^8.3.0": + version: 8.3.4 + resolution: "@types/uuid@npm:8.3.4" + checksum: 10/6f11f3ff70f30210edaa8071422d405e9c1d4e53abbe50fdce365150d3c698fe7bbff65c1e71ae080cbfb8fded860dbb5e174da96fdbbdfcaa3fb3daa474d20f + languageName: node + linkType: hard + "@types/validate-npm-package-name@npm:^4.0.0": version: 4.0.0 resolution: "@types/validate-npm-package-name@npm:4.0.0" @@ -9166,6 +9460,13 @@ __metadata: languageName: node linkType: hard +"aes-js@npm:^3.1.2": + version: 3.1.2 + resolution: "aes-js@npm:3.1.2" + checksum: 10/b65916767034a51375a3ac5aad62af452d89a386c1ae7b607bb9145d0bb8b8823bf2f3eba85bdfa52d61c65d5aed90ba90f677b8c826bfa1a8b7ae2fa3b54d91 + languageName: node + linkType: hard + "agent-base@npm:6, agent-base@npm:^6.0.2": version: 6.0.2 resolution: "agent-base@npm:6.0.2" @@ -9655,6 +9956,15 @@ __metadata: languageName: node linkType: hard +"async-mutex@npm:^0.5.0": + version: 0.5.0 + resolution: "async-mutex@npm:0.5.0" + dependencies: + tslib: "npm:^2.4.0" + checksum: 10/4c6bfce1cc9cd43f723c4d96403ac5f4757f885c953b839cde6956ec8817ff39623b82d67614de10c7933e21626925882fb9bac367db7d15d7cb4f84228722c9 + languageName: node + linkType: hard + "async@npm:^3.2.3, async@npm:^3.2.4": version: 3.2.4 resolution: "async@npm:3.2.4" @@ -9863,6 +10173,15 @@ __metadata: languageName: node linkType: hard +"base-x@npm:^3.0.2": + version: 3.0.10 + resolution: "base-x@npm:3.0.10" + dependencies: + safe-buffer: "npm:^5.0.1" + checksum: 10/52307739559e81d9980889de2359cb4f816cc0eb9a463028fa3ab239ab913d9044a1b47b4520f98e68453df32a457b8ba58b8d0ee7e757fc3fb971f3fa7a1482 + languageName: node + linkType: hard + "base64-js@npm:^1.0.2, base64-js@npm:^1.3.1": version: 1.5.1 resolution: "base64-js@npm:1.5.1" @@ -9893,6 +10212,13 @@ __metadata: languageName: node linkType: hard +"bech32@npm:^2.0.0": + version: 2.0.0 + resolution: "bech32@npm:2.0.0" + checksum: 10/fa15acb270b59aa496734a01f9155677b478987b773bf701f465858bf1606c6a970085babd43d71ce61895f1baa594cb41a2cd1394bd2c6698f03cc2d811300e + languageName: node + linkType: hard + "big-integer@npm:^1.6.17": version: 1.6.51 resolution: "big-integer@npm:1.6.51" @@ -9907,7 +10233,7 @@ __metadata: languageName: node linkType: hard -"bignumber.js@npm:^9.1.2": +"bignumber.js@npm:^9.0.1, bignumber.js@npm:^9.1.2": version: 9.1.2 resolution: "bignumber.js@npm:9.1.2" checksum: 10/d89b8800a987225d2c00dcbf8a69dc08e92aa0880157c851c287b307d31ceb2fc2acb0c62c3e3a3d42b6c5fcae9b004035f13eb4386e56d529d7edac18d5c9d8 @@ -9973,6 +10299,13 @@ __metadata: languageName: node linkType: hard +"blakejs@npm:^1.1.0": + version: 1.2.1 + resolution: "blakejs@npm:1.2.1" + checksum: 10/0638b1bd058b21892633929c43005aa6a4cc4b2ac5b338a146c3c076622f1b360795bd7a4d1f077c9b01863ed2df0c1504a81c5b520d164179120434847e6cd7 + languageName: node + linkType: hard + "bluebird@npm:~3.4.1": version: 3.4.7 resolution: "bluebird@npm:3.4.7" @@ -9980,7 +10313,7 @@ __metadata: languageName: node linkType: hard -"bn.js@npm:5.2.1, bn.js@npm:^5.0.0, bn.js@npm:^5.2.1": +"bn.js@npm:5.2.1, bn.js@npm:^5.0.0, bn.js@npm:^5.1.2, bn.js@npm:^5.2.0, bn.js@npm:^5.2.1": version: 5.2.1 resolution: "bn.js@npm:5.2.1" checksum: 10/7a7e8764d7a6e9708b8b9841b2b3d6019cc154d2fc23716d0efecfe1e16921b7533c6f7361fb05471eab47986c4aa310c270f88e3507172104632ac8df2cfd84 @@ -10109,7 +10442,7 @@ __metadata: languageName: node linkType: hard -"browserify-aes@npm:^1.0.0, browserify-aes@npm:^1.0.4": +"browserify-aes@npm:^1.0.0, browserify-aes@npm:^1.0.4, browserify-aes@npm:^1.2.0": version: 1.2.0 resolution: "browserify-aes@npm:1.2.0" dependencies: @@ -10263,6 +10596,26 @@ __metadata: languageName: node linkType: hard +"bs58@npm:^4.0.0": + version: 4.0.1 + resolution: "bs58@npm:4.0.1" + dependencies: + base-x: "npm:^3.0.2" + checksum: 10/b3c5365bb9e0c561e1a82f1a2d809a1a692059fae016be233a6127ad2f50a6b986467c3a50669ce4c18929dcccb297c5909314dd347a25a68c21b68eb3e95ac2 + languageName: node + linkType: hard + +"bs58check@npm:^2.1.2": + version: 2.1.2 + resolution: "bs58check@npm:2.1.2" + dependencies: + bs58: "npm:^4.0.0" + create-hash: "npm:^1.1.0" + safe-buffer: "npm:^5.1.2" + checksum: 10/43bdf08a5dd04581b78f040bc4169480e17008da482ffe2a6507327bbc4fc5c28de0501f7faf22901cfe57fbca79cbb202ca529003fedb4cb8dccd265b38e54d + languageName: node + linkType: hard + "bser@npm:2.1.1": version: 2.1.1 resolution: "bser@npm:2.1.1" @@ -10300,7 +10653,7 @@ __metadata: languageName: node linkType: hard -"buffer@npm:^5.2.1, buffer@npm:^5.5.0": +"buffer@npm:^5.1.0, buffer@npm:^5.2.1, buffer@npm:^5.5.0": version: 5.7.1 resolution: "buffer@npm:5.7.1" dependencies: @@ -10539,6 +10892,13 @@ __metadata: languageName: node linkType: hard +"cbor-sync@npm:^1.0.4": + version: 1.0.4 + resolution: "cbor-sync@npm:1.0.4" + checksum: 10/bdad5fbf442b5b2478ba59433cab145ad823f963f674ec42f3b730689e679327ec8a6dfab97724b63295badac915574139984e702475ff8025d7cb175e50e9ae + languageName: node + linkType: hard + "chainsaw@npm:~0.1.0": version: 0.1.0 resolution: "chainsaw@npm:0.1.0" @@ -11258,6 +11618,15 @@ __metadata: languageName: node linkType: hard +"crc@npm:^3.8.0": + version: 3.8.0 + resolution: "crc@npm:3.8.0" + dependencies: + buffer: "npm:^5.1.0" + checksum: 10/3a43061e692113d60fbaf5e438c5f6aa3374fe2368244a75cc083ecee6762513bcee8583f67c2c56feea0b0c72b41b7304fbd3c1e26cfcfaec310b9a18543fa8 + languageName: node + linkType: hard + "create-ecdh@npm:^4.0.0": version: 4.0.4 resolution: "create-ecdh@npm:4.0.4" @@ -12258,6 +12627,21 @@ __metadata: languageName: node linkType: hard +"elliptic@npm:^6.5.7": + version: 6.6.1 + resolution: "elliptic@npm:6.6.1" + dependencies: + bn.js: "npm:^4.11.9" + brorand: "npm:^1.1.0" + hash.js: "npm:^1.0.0" + hmac-drbg: "npm:^1.0.1" + inherits: "npm:^2.0.4" + minimalistic-assert: "npm:^1.0.1" + minimalistic-crypto-utils: "npm:^1.0.1" + checksum: 10/dc678c9febd89a219c4008ba3a9abb82237be853d9fd171cd602c8fb5ec39927e65c6b5e7a1b2a4ea82ee8e0ded72275e7932bb2da04a5790c2638b818e4e1c5 + languageName: node + linkType: hard + "emittery@npm:^0.13.1": version: 0.13.1 resolution: "emittery@npm:0.13.1" @@ -13296,6 +13680,29 @@ __metadata: languageName: node linkType: hard +"ethereum-cryptography@npm:^0.1.3": + version: 0.1.3 + resolution: "ethereum-cryptography@npm:0.1.3" + dependencies: + "@types/pbkdf2": "npm:^3.0.0" + "@types/secp256k1": "npm:^4.0.1" + blakejs: "npm:^1.1.0" + browserify-aes: "npm:^1.2.0" + bs58check: "npm:^2.1.2" + create-hash: "npm:^1.2.0" + create-hmac: "npm:^1.1.7" + hash.js: "npm:^1.1.7" + keccak: "npm:^3.0.0" + pbkdf2: "npm:^3.0.17" + randombytes: "npm:^2.1.0" + safe-buffer: "npm:^5.1.2" + scrypt-js: "npm:^3.0.0" + secp256k1: "npm:^4.0.1" + setimmediate: "npm:^1.0.5" + checksum: 10/975e476782746acd97d5b37366801ae622a52fb31e5d83f600804be230a61ef7b9d289dcecd9c308fb441967caf3a6e3768dd7c8add6441fcc60c398175d5a96 + languageName: node + linkType: hard + "ethereum-cryptography@npm:^2.0.0, ethereum-cryptography@npm:^2.1.2": version: 2.1.2 resolution: "ethereum-cryptography@npm:2.1.2" @@ -13308,6 +13715,35 @@ __metadata: languageName: node linkType: hard +"ethereumjs-util@npm:^7.1.2": + version: 7.1.5 + resolution: "ethereumjs-util@npm:7.1.5" + dependencies: + "@types/bn.js": "npm:^5.1.0" + bn.js: "npm:^5.1.2" + create-hash: "npm:^1.1.2" + ethereum-cryptography: "npm:^0.1.3" + rlp: "npm:^2.2.4" + checksum: 10/f28fc1ebb8f35bf9e418f76f51be737d94d603b912c3e014c4e87cd45ccd1b10bdfef764c8f152574b57e9faa260a18773cbc110f9e0a754d6b3730699e54dc9 + languageName: node + linkType: hard + +"ethereumjs-wallet@npm:^1.0.1": + version: 1.0.2 + resolution: "ethereumjs-wallet@npm:1.0.2" + dependencies: + aes-js: "npm:^3.1.2" + bs58check: "npm:^2.1.2" + ethereum-cryptography: "npm:^0.1.3" + ethereumjs-util: "npm:^7.1.2" + randombytes: "npm:^2.1.0" + scrypt-js: "npm:^3.0.1" + utf8: "npm:^3.0.0" + uuid: "npm:^8.3.2" + checksum: 10/0a9ad0ac0930627c15e6fa6115dde8d1dd81160e57fbdf81ef0bcd1e0edd750fe9e8b00992651e694cabefee29eef2ad651dce8b24ae5253861927d0e19b6c90 + languageName: node + linkType: hard + "ethers@npm:^6.3.0": version: 6.13.4 resolution: "ethers@npm:6.13.4" @@ -14694,7 +15130,7 @@ __metadata: languageName: node linkType: hard -"hash.js@npm:^1.0.0, hash.js@npm:^1.0.3": +"hash.js@npm:^1.0.0, hash.js@npm:^1.0.3, hash.js@npm:^1.1.7": version: 1.1.7 resolution: "hash.js@npm:1.1.7" dependencies: @@ -14713,6 +15149,18 @@ __metadata: languageName: node linkType: hard +"hdkey@npm:^2.0.1": + version: 2.1.0 + resolution: "hdkey@npm:2.1.0" + dependencies: + bs58check: "npm:^2.1.2" + ripemd160: "npm:^2.0.2" + safe-buffer: "npm:^5.1.1" + secp256k1: "npm:^4.0.0" + checksum: 10/c4ee2189ea3d87070ebd14ad7368e292b1e0b30e4d8a107eb8f33624634df6e57b8a3b2cda65b3bd97e88474f6798cfdbe7b63b6037429f0e169321d84a0db58 + languageName: node + linkType: hard + "he@npm:1.2.0, he@npm:^1.2.0": version: 1.2.0 resolution: "he@npm:1.2.0" @@ -16480,6 +16928,13 @@ __metadata: languageName: node linkType: hard +"jsbi@npm:^3.1.5": + version: 3.2.5 + resolution: "jsbi@npm:3.2.5" + checksum: 10/2cceb3a06dcb16493e936aa22384d912dd5f0a1fd474b97b5c6705011bd0aac8214d9a392a730b3f3ffb61a8fbe910a34d0fe881329be6a02857520d7a61ace6 + languageName: node + linkType: hard + "jsbn@npm:1.1.0": version: 1.1.0 resolution: "jsbn@npm:1.1.0" @@ -16678,6 +17133,13 @@ __metadata: languageName: node linkType: hard +"jsonschema@npm:^1.4.1": + version: 1.5.0 + resolution: "jsonschema@npm:1.5.0" + checksum: 10/46bf49b388ba922073bcb3c8d5e90af9d29fc8303dc866fd440182c88d6b4fd2807679fd39cdefb4113156d104ea47da9c0ff4bbcb0032c9fa29461cb1a92182 + languageName: node + linkType: hard + "jsx-ast-utils@npm:^2.4.1 || ^3.0.0": version: 3.3.3 resolution: "jsx-ast-utils@npm:3.3.3" @@ -16688,6 +17150,18 @@ __metadata: languageName: node linkType: hard +"keccak@npm:^3.0.0": + version: 3.0.4 + resolution: "keccak@npm:3.0.4" + dependencies: + node-addon-api: "npm:^2.0.0" + node-gyp: "npm:latest" + node-gyp-build: "npm:^4.2.0" + readable-stream: "npm:^3.6.0" + checksum: 10/45478bb0a57e44d0108646499b8360914b0fbc8b0e088f1076659cb34faaa9eb829c40f6dd9dadb3460bb86cc33153c41fed37fe5ce09465a60e71e78c23fa55 + languageName: node + linkType: hard + "keyv@npm:^4.5.3": version: 4.5.3 resolution: "keyv@npm:4.5.3" @@ -18068,6 +18542,24 @@ __metadata: languageName: node linkType: hard +"node-addon-api@npm:^2.0.0": + version: 2.0.2 + resolution: "node-addon-api@npm:2.0.2" + dependencies: + node-gyp: "npm:latest" + checksum: 10/e4ce4daac5b2fefa6b94491b86979a9c12d9cceba571d2c6df1eb5859f9da68e5dc198f128798e1785a88aafee6e11f4992dcccd4bf86bec90973927d158bd60 + languageName: node + linkType: hard + +"node-addon-api@npm:^5.0.0": + version: 5.1.0 + resolution: "node-addon-api@npm:5.1.0" + dependencies: + node-gyp: "npm:latest" + checksum: 10/595f59ffb4630564f587c502119cbd980d302e482781021f3b479f5fc7e41cf8f2f7280fdc2795f32d148e4f3259bd15043c52d4a3442796aa6f1ae97b959636 + languageName: node + linkType: hard + "node-addon-api@npm:^6.1.0": version: 6.1.0 resolution: "node-addon-api@npm:6.1.0" @@ -18134,6 +18626,17 @@ __metadata: languageName: node linkType: hard +"node-gyp-build@npm:^4.2.0": + version: 4.8.4 + resolution: "node-gyp-build@npm:4.8.4" + bin: + node-gyp-build: bin.js + node-gyp-build-optional: optional.js + node-gyp-build-test: build-test.js + checksum: 10/6a7d62289d1afc419fc8fc9bd00aa4e554369e50ca0acbc215cb91446148b75ff7e2a3b53c2c5b2c09a39d416d69f3d3237937860373104b5fe429bf30ad9ac5 + languageName: node + linkType: hard + "node-gyp@npm:^10.0.0": version: 10.0.1 resolution: "node-gyp@npm:10.0.1" @@ -18925,6 +19428,19 @@ __metadata: languageName: node linkType: hard +"pbkdf2@npm:^3.0.17": + version: 3.1.2 + resolution: "pbkdf2@npm:3.1.2" + dependencies: + create-hash: "npm:^1.1.2" + create-hmac: "npm:^1.1.4" + ripemd160: "npm:^2.0.1" + safe-buffer: "npm:^5.0.1" + sha.js: "npm:^2.4.8" + checksum: 10/40bdf30df1c9bb1ae41ec50c11e480cf0d36484b7c7933bf55e4451d1d0e3f09589df70935c56e7fccc5702779a0d7b842d012be8c08a187b44eb24d55bb9460 + languageName: node + linkType: hard + "pbkdf2@npm:^3.0.3": version: 3.1.1 resolution: "pbkdf2@npm:3.1.1" @@ -20421,7 +20937,7 @@ __metadata: languageName: node linkType: hard -"ripemd160@npm:^2.0.0, ripemd160@npm:^2.0.1": +"ripemd160@npm:^2.0.0, ripemd160@npm:^2.0.1, ripemd160@npm:^2.0.2": version: 2.0.2 resolution: "ripemd160@npm:2.0.2" dependencies: @@ -20431,6 +20947,26 @@ __metadata: languageName: node linkType: hard +"rlp@npm:^2.2.4, rlp@npm:^2.2.6": + version: 2.2.7 + resolution: "rlp@npm:2.2.7" + dependencies: + bn.js: "npm:^5.2.0" + bin: + rlp: bin/rlp + checksum: 10/cf1919a2dc99f336191b3363b76299db567c192b7ee3c6f5c722728c34f65577883c9c88eeb7a1bfcbc26693c8a4f1fb0662e79ee86f0c98dd258d6987303498 + languageName: node + linkType: hard + +"rlp@npm:^3.0.0": + version: 3.0.0 + resolution: "rlp@npm:3.0.0" + bin: + rlp: bin/rlp + checksum: 10/c85549fa5368ef029707d02f0937c0c503b69fb330c5941508c9eef537a4f179fbeecd17149aeb795d430ed5249b68d7c66383a9863068712a191d388786cfc1 + languageName: node + linkType: hard + "rollup-plugin-inject@npm:^3.0.0": version: 3.0.2 resolution: "rollup-plugin-inject@npm:3.0.2" @@ -20662,6 +21198,25 @@ __metadata: languageName: node linkType: hard +"scrypt-js@npm:^3.0.0, scrypt-js@npm:^3.0.1": + version: 3.0.1 + resolution: "scrypt-js@npm:3.0.1" + checksum: 10/2f8aa72b7f76a6f9c446bbec5670f80d47497bccce98474203d89b5667717223eeb04a50492ae685ed7adc5a060fc2d8f9fd988f8f7ebdaf3341967f3aeff116 + languageName: node + linkType: hard + +"secp256k1@npm:^4.0.0, secp256k1@npm:^4.0.1": + version: 4.0.4 + resolution: "secp256k1@npm:4.0.4" + dependencies: + elliptic: "npm:^6.5.7" + node-addon-api: "npm:^5.0.0" + node-gyp: "npm:latest" + node-gyp-build: "npm:^4.2.0" + checksum: 10/45000f348c853df7c1e2b67c48efb062ae78c0620ab1a5cfb02fa20d3aad39c641f4e7a18b3de3b54a7c0cc1e0addeb8ecd9d88bc332e92df17a92b60c36122a + languageName: node + linkType: hard + "select-hose@npm:^2.0.0": version: 2.0.0 resolution: "select-hose@npm:2.0.0" @@ -20832,7 +21387,7 @@ __metadata: languageName: node linkType: hard -"setimmediate@npm:^1.0.4, setimmediate@npm:~1.0.4": +"setimmediate@npm:^1.0.4, setimmediate@npm:^1.0.5, setimmediate@npm:~1.0.4": version: 1.0.5 resolution: "setimmediate@npm:1.0.5" checksum: 10/76e3f5d7f4b581b6100ff819761f04a984fa3f3990e72a6554b57188ded53efce2d3d6c0932c10f810b7c59414f85e2ab3c11521877d1dea1ce0b56dc906f485 @@ -20853,7 +21408,7 @@ __metadata: languageName: node linkType: hard -"sha.js@npm:^2.4.0, sha.js@npm:^2.4.8": +"sha.js@npm:^2.4.0, sha.js@npm:^2.4.11, sha.js@npm:^2.4.8": version: 2.4.11 resolution: "sha.js@npm:2.4.11" dependencies: @@ -22177,6 +22732,13 @@ __metadata: languageName: node linkType: hard +"tslib@npm:^2.3.0": + version: 2.8.1 + resolution: "tslib@npm:2.8.1" + checksum: 10/3e2e043d5c2316461cb54e5c7fe02c30ef6dccb3384717ca22ae5c6b5bc95232a6241df19c622d9c73b809bea33b187f6dbc73030963e29950c2141bc32a79f7 + languageName: node + linkType: hard + "tsutils@npm:^3.21.0": version: 3.21.0 resolution: "tsutils@npm:3.21.0" @@ -22640,6 +23202,13 @@ __metadata: languageName: node linkType: hard +"utf8@npm:^3.0.0": + version: 3.0.0 + resolution: "utf8@npm:3.0.0" + checksum: 10/31d19c4faacbb65b09ebc1c21c32b20bdb0919c6f6773cee5001b99bb83f8e503e7233c08fc71ebb34f7cfebd95cec3243b81d90176097aa2f286cccb4ce866e + languageName: node + linkType: hard + "util-deprecate@npm:^1.0.1, util-deprecate@npm:^1.0.2, util-deprecate@npm:~1.0.1": version: 1.0.2 resolution: "util-deprecate@npm:1.0.2" From bf37ae23be0b5da0b0b4eb80661de36e10838544 Mon Sep 17 00:00:00 2001 From: Maarten Zuidhoorn Date: Fri, 10 Jan 2025 12:39:10 +0100 Subject: [PATCH 06/12] Remove dependency on `@metamask/keyring-controller` --- packages/snaps-controllers/package.json | 1 - .../src/snaps/SnapController.ts | 8 +- yarn.lock | 591 +----------------- 3 files changed, 17 insertions(+), 583 deletions(-) diff --git a/packages/snaps-controllers/package.json b/packages/snaps-controllers/package.json index 0ac124713b..ec3fd6f31d 100644 --- a/packages/snaps-controllers/package.json +++ b/packages/snaps-controllers/package.json @@ -84,7 +84,6 @@ "@metamask/json-rpc-engine": "^10.0.1", "@metamask/json-rpc-middleware-stream": "^8.0.5", "@metamask/key-tree": "^10.0.1", - "@metamask/keyring-controller": "^19.0.2", "@metamask/object-multiplex": "^2.0.0", "@metamask/permission-controller": "^11.0.4", "@metamask/phishing-controller": "^12.3.1", diff --git a/packages/snaps-controllers/src/snaps/SnapController.ts b/packages/snaps-controllers/src/snaps/SnapController.ts index 901128c258..b261b6390d 100644 --- a/packages/snaps-controllers/src/snaps/SnapController.ts +++ b/packages/snaps-controllers/src/snaps/SnapController.ts @@ -9,7 +9,6 @@ import type { } from '@metamask/base-controller'; import { BaseController } from '@metamask/base-controller'; import type { CryptographicFunctions } from '@metamask/key-tree'; -import type { KeyringControllerLockEvent } from '@metamask/keyring-controller'; import type { Caveat, GetEndowments, @@ -564,6 +563,11 @@ export type SnapControllerStateChangeEvent = ControllerStateChangeEvent< SnapControllerState >; +type KeyringControllerLock = { + type: 'KeyringController:lock'; + payload: []; +}; + export type SnapControllerEvents = | SnapBlocked | SnapInstalled @@ -609,7 +613,7 @@ export type AllowedEvents = | ExecutionServiceEvents | SnapInstalled | SnapUpdated - | KeyringControllerLockEvent; + | KeyringControllerLock; type SnapControllerMessenger = RestrictedControllerMessenger< typeof controllerName, diff --git a/yarn.lock b/yarn.lock index 3cd3d672ea..29065c5256 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3230,7 +3230,7 @@ __metadata: languageName: node linkType: hard -"@ethereumjs/tx@npm:^4.0.2, @ethereumjs/tx@npm:^4.2.0": +"@ethereumjs/tx@npm:^4.2.0": version: 4.2.0 resolution: "@ethereumjs/tx@npm:4.2.0" dependencies: @@ -3242,7 +3242,7 @@ __metadata: languageName: node linkType: hard -"@ethereumjs/util@npm:^8.0.0, @ethereumjs/util@npm:^8.1.0": +"@ethereumjs/util@npm:^8.1.0": version: 8.1.0 resolution: "@ethereumjs/util@npm:8.1.0" dependencies: @@ -3762,64 +3762,6 @@ __metadata: languageName: node linkType: hard -"@keystonehq/alias-sampling@npm:^0.1.1": - version: 0.1.2 - resolution: "@keystonehq/alias-sampling@npm:0.1.2" - checksum: 10/4dfdfb91e070b1d9f28058c92b5b8fad81696ac63bd432cd6bd359f2ab92eb50df75e8c5da1f75a351756387e9902f043b3ecc2cbf662c9c9456ecacc848abfd - languageName: node - linkType: hard - -"@keystonehq/base-eth-keyring@npm:^0.14.1": - version: 0.14.1 - resolution: "@keystonehq/base-eth-keyring@npm:0.14.1" - dependencies: - "@ethereumjs/tx": "npm:^4.0.2" - "@ethereumjs/util": "npm:^8.0.0" - "@keystonehq/bc-ur-registry-eth": "npm:^0.19.1" - hdkey: "npm:^2.0.1" - rlp: "npm:^3.0.0" - uuid: "npm:^8.3.2" - checksum: 10/07516e967fc5c618ef0ce67b155ba69c04f8fd84d5a6fd35f025f989c41256c9e6fa0375cfb0318da42876a61c64839e312d910e4b9fa801f86179df826adc69 - languageName: node - linkType: hard - -"@keystonehq/bc-ur-registry-eth@npm:^0.19.1": - version: 0.19.1 - resolution: "@keystonehq/bc-ur-registry-eth@npm:0.19.1" - dependencies: - "@ethereumjs/util": "npm:^8.0.0" - "@keystonehq/bc-ur-registry": "npm:^0.6.0" - hdkey: "npm:^2.0.1" - uuid: "npm:^8.3.2" - checksum: 10/7e64e6a754e6b66fc83a8f3880b54828c5b37f4eaaea3287eee31bd9d9b5ac0ba4cd4b8e751af9bd2f66e6f19291eaf02f46cd177d05ed9b30c1349cdd04572f - languageName: node - linkType: hard - -"@keystonehq/bc-ur-registry@npm:^0.6.0": - version: 0.6.4 - resolution: "@keystonehq/bc-ur-registry@npm:0.6.4" - dependencies: - "@ngraveio/bc-ur": "npm:^1.1.5" - bs58check: "npm:^2.1.2" - tslib: "npm:^2.3.0" - checksum: 10/d4cdbefc14f3305543340d509564e1a795eb458327d46aad8665927999150df7e282939dcb714b81fea386061019e3b9f41eedbbb09a59d404355711c33159b2 - languageName: node - linkType: hard - -"@keystonehq/metamask-airgapped-keyring@npm:^0.14.1": - version: 0.14.1 - resolution: "@keystonehq/metamask-airgapped-keyring@npm:0.14.1" - dependencies: - "@ethereumjs/tx": "npm:^4.0.2" - "@keystonehq/base-eth-keyring": "npm:^0.14.1" - "@keystonehq/bc-ur-registry-eth": "npm:^0.19.1" - "@metamask/obs-store": "npm:^9.0.0" - rlp: "npm:^2.2.6" - uuid: "npm:^8.3.2" - checksum: 10/8e34be8813c51488c7dc9b641ed17258740dda45fb72fe48670b077ecfb92273e0c5a2fbbab121b01d7e0906a3ec512f261fceb95da8089550021ab6a0c89c6b - languageName: node - linkType: hard - "@lavamoat/aa@npm:^4.1.0, @lavamoat/aa@npm:^4.2.0": version: 4.2.0 resolution: "@lavamoat/aa@npm:4.2.0" @@ -3911,16 +3853,6 @@ __metadata: languageName: node linkType: hard -"@metamask/abi-utils@npm:^3.0.0": - version: 3.0.0 - resolution: "@metamask/abi-utils@npm:3.0.0" - dependencies: - "@metamask/superstruct": "npm:^3.1.0" - "@metamask/utils": "npm:^11.0.1" - checksum: 10/068b98185148b9e185b4af4392c6a6f82f1d4b1ff60013c57679c618f37afe9030e3ccc940e1a8b690be6f62ea91115ab18b73f3c3c09f4eff1794e31ababb9b - languageName: node - linkType: hard - "@metamask/action-utils@npm:^1.0.0": version: 1.1.1 resolution: "@metamask/action-utils@npm:1.1.1" @@ -4066,15 +3998,6 @@ __metadata: languageName: unknown linkType: soft -"@metamask/browser-passworder@npm:^4.3.0": - version: 4.3.0 - resolution: "@metamask/browser-passworder@npm:4.3.0" - dependencies: - "@metamask/utils": "npm:^8.2.0" - checksum: 10/8ba5c50cd6274b0cc0f90a1ee16b960ee150f14c29083f3515f4abe018a28ead32c21f5f4a62a6e27a946b1228adc2ff1f195e71e38782fa39fa8fff116173e6 - languageName: node - linkType: hard - "@metamask/browser-passworder@npm:^5.0.1": version: 5.0.1 resolution: "@metamask/browser-passworder@npm:5.0.1" @@ -4553,19 +4476,6 @@ __metadata: languageName: node linkType: hard -"@metamask/eth-hd-keyring@npm:^7.0.4": - version: 7.0.4 - resolution: "@metamask/eth-hd-keyring@npm:7.0.4" - dependencies: - "@ethereumjs/util": "npm:^8.1.0" - "@metamask/eth-sig-util": "npm:^7.0.3" - "@metamask/scure-bip39": "npm:^2.1.1" - "@metamask/utils": "npm:^9.2.1" - ethereum-cryptography: "npm:^2.1.2" - checksum: 10/493d06f55225b6f9da48ee001486e18898d6a4a3afd2cf40ff1dcae2ece42d5e96174f6a05b7c39419cb3531b530c8af294d9422195661788c5e0b687a328874 - languageName: node - linkType: hard - "@metamask/eth-json-rpc-middleware@npm:^15.0.0": version: 15.0.0 resolution: "@metamask/eth-json-rpc-middleware@npm:15.0.0" @@ -4622,33 +4532,6 @@ __metadata: languageName: node linkType: hard -"@metamask/eth-sig-util@npm:^8.0.0": - version: 8.1.2 - resolution: "@metamask/eth-sig-util@npm:8.1.2" - dependencies: - "@ethereumjs/util": "npm:^8.1.0" - "@metamask/abi-utils": "npm:^3.0.0" - "@metamask/utils": "npm:^11.0.1" - "@scure/base": "npm:~1.1.3" - ethereum-cryptography: "npm:^2.1.2" - tweetnacl: "npm:^1.0.3" - checksum: 10/32b284fc8c3229e3741b1c21f44ca3f55c2215ef8ad700775cd9501bbaab56a4e861827bef24ed263734d28c899eb3b34a9646e9d21ec3fce12204b7eb58bfed - languageName: node - linkType: hard - -"@metamask/eth-simple-keyring@npm:^6.0.5": - version: 6.0.5 - resolution: "@metamask/eth-simple-keyring@npm:6.0.5" - dependencies: - "@ethereumjs/util": "npm:^8.1.0" - "@metamask/eth-sig-util": "npm:^7.0.3" - "@metamask/utils": "npm:^9.2.1" - ethereum-cryptography: "npm:^2.1.2" - randombytes: "npm:^2.1.0" - checksum: 10/98b7bd00df25e7630324e2c762e3a03a7f199108a4dfe22e5a1938f1d01c9b2cd64ab4bb6fd242bf898624903d5a68a2e1f61c95f94a141266ab23dae8d97d21 - languageName: node - linkType: hard - "@metamask/ethereum-provider-example-snap@workspace:^, @metamask/ethereum-provider-example-snap@workspace:packages/examples/packages/ethereum-provider": version: 0.0.0-use.local resolution: "@metamask/ethereum-provider-example-snap@workspace:packages/examples/packages/ethereum-provider" @@ -5175,74 +5058,6 @@ __metadata: languageName: node linkType: hard -"@metamask/keyring-api@npm:^12.0.0": - version: 12.0.0 - resolution: "@metamask/keyring-api@npm:12.0.0" - dependencies: - "@metamask/keyring-utils": "npm:^1.0.0" - "@metamask/superstruct": "npm:^3.1.0" - "@metamask/utils": "npm:^9.3.0" - bech32: "npm:^2.0.0" - checksum: 10/ba8b75c55d3fcb9f8b52c58ff141cba81f7c416c3fa684e089965717ea129d50e8df7a73e7ab1c96eaf59d70b6e2dd8a618434939b75ef0d3402b547b5196877 - languageName: node - linkType: hard - -"@metamask/keyring-api@npm:^13.0.0": - version: 13.0.0 - resolution: "@metamask/keyring-api@npm:13.0.0" - dependencies: - "@metamask/keyring-utils": "npm:^1.0.0" - "@metamask/superstruct": "npm:^3.1.0" - "@metamask/utils": "npm:^11.0.1" - bech32: "npm:^2.0.0" - checksum: 10/f7e8982112a2813790354267af8f79cbf241d7ca9d733fe5e8de1a13993203b154ac6d358a92bd4340cbd4d25f4bac48681d17a25e3dc6f2336c95c00c371686 - languageName: node - linkType: hard - -"@metamask/keyring-controller@npm:^19.0.2": - version: 19.0.2 - resolution: "@metamask/keyring-controller@npm:19.0.2" - dependencies: - "@ethereumjs/util": "npm:^8.1.0" - "@keystonehq/metamask-airgapped-keyring": "npm:^0.14.1" - "@metamask/base-controller": "npm:^7.0.2" - "@metamask/browser-passworder": "npm:^4.3.0" - "@metamask/eth-hd-keyring": "npm:^7.0.4" - "@metamask/eth-sig-util": "npm:^8.0.0" - "@metamask/eth-simple-keyring": "npm:^6.0.5" - "@metamask/keyring-api": "npm:^12.0.0" - "@metamask/keyring-internal-api": "npm:^1.0.0" - "@metamask/message-manager": "npm:^11.0.3" - "@metamask/utils": "npm:^10.0.0" - async-mutex: "npm:^0.5.0" - ethereumjs-wallet: "npm:^1.0.1" - immer: "npm:^9.0.6" - checksum: 10/bbc140db91902dc8a9b8e1220d3c07e91858d68b9888d26df74fe300b1baf1331c5df62dde4ea71be77b485a0bca314bfc899358375490c7dbce594810bc74b6 - languageName: node - linkType: hard - -"@metamask/keyring-internal-api@npm:^1.0.0": - version: 1.1.0 - resolution: "@metamask/keyring-internal-api@npm:1.1.0" - dependencies: - "@metamask/keyring-api": "npm:^13.0.0" - "@metamask/keyring-utils": "npm:^1.0.0" - "@metamask/superstruct": "npm:^3.1.0" - "@metamask/utils": "npm:^11.0.1" - checksum: 10/5db127cfe319c289b95d55d08b6737820f00761564e219d756a55e030b83043fb43d18bac3b63fcc69cd2e03129f831410ce960ba954d8183d976d87a6781b8c - languageName: node - linkType: hard - -"@metamask/keyring-utils@npm:^1.0.0": - version: 1.0.0 - resolution: "@metamask/keyring-utils@npm:1.0.0" - dependencies: - "@metamask/superstruct": "npm:^3.1.0" - "@metamask/utils": "npm:^9.3.0" - checksum: 10/f74f7343a7154b029e0fa4c25735c589eba4dc25a9e323d43b7c733ce5dbb23ce603a4f02aac455163993649ceeaf714b8b843985ba7a9cb00b926b3b8dc6b51 - languageName: node - linkType: hard - "@metamask/lifecycle-hooks-example-snap@workspace:^, @metamask/lifecycle-hooks-example-snap@workspace:packages/examples/packages/lifecycle-hooks": version: 0.0.0-use.local resolution: "@metamask/lifecycle-hooks-example-snap@workspace:packages/examples/packages/lifecycle-hooks" @@ -5355,21 +5170,6 @@ __metadata: languageName: unknown linkType: soft -"@metamask/message-manager@npm:^11.0.3": - version: 11.0.3 - resolution: "@metamask/message-manager@npm:11.0.3" - dependencies: - "@metamask/base-controller": "npm:^7.0.2" - "@metamask/controller-utils": "npm:^11.4.4" - "@metamask/eth-sig-util": "npm:^8.0.0" - "@metamask/utils": "npm:^10.0.0" - "@types/uuid": "npm:^8.3.0" - jsonschema: "npm:^1.4.1" - uuid: "npm:^8.3.2" - checksum: 10/18f5b0091474fc45e854db9c91ad8f5f898017ba9a08b01c770904ad3929b004c65298fe246b989f3c92fd2f03b5932dfa31adadb95ca81ce6940cb28a2ec793 - languageName: node - linkType: hard - "@metamask/name-lookup-example-snap@workspace:^, @metamask/name-lookup-example-snap@workspace:packages/examples/packages/name-lookup": version: 0.0.0-use.local resolution: "@metamask/name-lookup-example-snap@workspace:packages/examples/packages/name-lookup" @@ -5503,16 +5303,6 @@ __metadata: languageName: node linkType: hard -"@metamask/obs-store@npm:^9.0.0": - version: 9.1.0 - resolution: "@metamask/obs-store@npm:9.1.0" - dependencies: - "@metamask/safe-event-emitter": "npm:^3.0.0" - readable-stream: "npm:^3.6.2" - checksum: 10/fa37a0b9e1e25f54c93a8f16449b3fc771c15b70d79d590cf41e22f871a19d673bcfd0d08b044093235d10d0bb031b47b8209d89997def0cb256abe3e371064f - languageName: node - linkType: hard - "@metamask/permission-controller@npm:^11.0.4": version: 11.0.4 resolution: "@metamask/permission-controller@npm:11.0.4" @@ -5929,7 +5719,6 @@ __metadata: "@metamask/json-rpc-engine": "npm:^10.0.1" "@metamask/json-rpc-middleware-stream": "npm:^8.0.5" "@metamask/key-tree": "npm:^10.0.1" - "@metamask/keyring-controller": "npm:^19.0.2" "@metamask/object-multiplex": "npm:^2.0.0" "@metamask/permission-controller": "npm:^11.0.4" "@metamask/phishing-controller": "npm:^12.3.1" @@ -6576,7 +6365,7 @@ __metadata: languageName: unknown linkType: soft -"@metamask/superstruct@npm:^3.0.0, @metamask/superstruct@npm:^3.1.0": +"@metamask/superstruct@npm:^3.1.0": version: 3.1.0 resolution: "@metamask/superstruct@npm:3.1.0" checksum: 10/5066fe228d5f11da387606d7f9545de2b473ab5a9e0f1bb8aea2f52d3e2c9d25e427151acde61f4a2de80a07a9871fe9505ad06abca6a61b7c3b54ed5c403b01 @@ -6710,23 +6499,6 @@ __metadata: languageName: node linkType: hard -"@metamask/utils@npm:^8.2.0": - version: 8.5.0 - resolution: "@metamask/utils@npm:8.5.0" - dependencies: - "@ethereumjs/tx": "npm:^4.2.0" - "@metamask/superstruct": "npm:^3.0.0" - "@noble/hashes": "npm:^1.3.1" - "@scure/base": "npm:^1.1.3" - "@types/debug": "npm:^4.1.7" - debug: "npm:^4.3.4" - pony-cause: "npm:^2.1.10" - semver: "npm:^7.5.4" - uuid: "npm:^9.0.1" - checksum: 10/68a42a55f7dc750b75467fb7c05a496c20dac073a2753e0f4d9642c4d8dcb3f9ddf51a09d30337e11637f1777f3dfe22e15b5159dbafb0fdb7bd8c9236056153 - languageName: node - linkType: hard - "@metamask/utils@npm:^9.0.0, @metamask/utils@npm:^9.1.0": version: 9.2.1 resolution: "@metamask/utils@npm:9.2.1" @@ -6744,23 +6516,6 @@ __metadata: languageName: node linkType: hard -"@metamask/utils@npm:^9.2.1, @metamask/utils@npm:^9.3.0": - version: 9.3.0 - resolution: "@metamask/utils@npm:9.3.0" - dependencies: - "@ethereumjs/tx": "npm:^4.2.0" - "@metamask/superstruct": "npm:^3.1.0" - "@noble/hashes": "npm:^1.3.1" - "@scure/base": "npm:^1.1.3" - "@types/debug": "npm:^4.1.7" - debug: "npm:^4.3.4" - pony-cause: "npm:^2.1.10" - semver: "npm:^7.5.4" - uuid: "npm:^9.0.1" - checksum: 10/ed6648cd973bbf3b4eb0e862903b795a99d27784c820e19f62f0bc0ddf353e98c2858d7e9aaebc0249a586391b344e35b9249d13c08e3ea0c74b23dc1c6b1558 - languageName: node - linkType: hard - "@metamask/wasm-example-snap@workspace:^, @metamask/wasm-example-snap@workspace:packages/examples/packages/wasm": version: 0.0.0-use.local resolution: "@metamask/wasm-example-snap@workspace:packages/examples/packages/wasm" @@ -6924,21 +6679,6 @@ __metadata: languageName: node linkType: hard -"@ngraveio/bc-ur@npm:^1.1.5": - version: 1.1.13 - resolution: "@ngraveio/bc-ur@npm:1.1.13" - dependencies: - "@keystonehq/alias-sampling": "npm:^0.1.1" - assert: "npm:^2.0.0" - bignumber.js: "npm:^9.0.1" - cbor-sync: "npm:^1.0.4" - crc: "npm:^3.8.0" - jsbi: "npm:^3.1.5" - sha.js: "npm:^2.4.11" - checksum: 10/0d3301b673a0bd9a069dae1f017cfd03010fddf19c1449d1a9e986b9b879ee4611f5af690ace9f59b75707573d1d3d6a4983166207db743425974a736689c6a0 - languageName: node - linkType: hard - "@noble/bls12-381@npm:^1.2.0": version: 1.4.0 resolution: "@noble/bls12-381@npm:1.4.0" @@ -7854,15 +7594,6 @@ __metadata: languageName: node linkType: hard -"@types/bn.js@npm:^5.1.0": - version: 5.1.6 - resolution: "@types/bn.js@npm:5.1.6" - dependencies: - "@types/node": "npm:*" - checksum: 10/db565b5a2af59b09459d74441153bf23a0e80f1fb2d070330786054e7ce1a7285dc40afcd8f289426c61a83166bdd70814f70e2d439744686aac5d3ea75daf13 - languageName: node - linkType: hard - "@types/bn.js@npm:^5.1.5": version: 5.1.5 resolution: "@types/bn.js@npm:5.1.5" @@ -8302,15 +8033,6 @@ __metadata: languageName: node linkType: hard -"@types/pbkdf2@npm:^3.0.0": - version: 3.1.2 - resolution: "@types/pbkdf2@npm:3.1.2" - dependencies: - "@types/node": "npm:*" - checksum: 10/bebe1e596cbbe5f7d2726a58859e61986c5a42459048e29cb7f2d4d764be6bbb0844572fd5d70ca8955a8a17e8b4ed80984fc4903e165d9efb8807a3fbb051aa - languageName: node - linkType: hard - "@types/prettier@npm:^2.1.5": version: 2.3.2 resolution: "@types/prettier@npm:2.3.2" @@ -8406,15 +8128,6 @@ __metadata: languageName: node linkType: hard -"@types/secp256k1@npm:^4.0.1": - version: 4.0.6 - resolution: "@types/secp256k1@npm:4.0.6" - dependencies: - "@types/node": "npm:*" - checksum: 10/211f823be990b55612e604d620acf0dc3bc942d3836bdd8da604269effabc86d98161e5947487b4e4e128f9180fc1682daae2f89ea7a4d9648fdfe52fba365fc - languageName: node - linkType: hard - "@types/semver@npm:^7.3.12, @types/semver@npm:^7.3.6, @types/semver@npm:^7.5.0": version: 7.5.0 resolution: "@types/semver@npm:7.5.0" @@ -8506,13 +8219,6 @@ __metadata: languageName: node linkType: hard -"@types/uuid@npm:^8.3.0": - version: 8.3.4 - resolution: "@types/uuid@npm:8.3.4" - checksum: 10/6f11f3ff70f30210edaa8071422d405e9c1d4e53abbe50fdce365150d3c698fe7bbff65c1e71ae080cbfb8fded860dbb5e174da96fdbbdfcaa3fb3daa474d20f - languageName: node - linkType: hard - "@types/validate-npm-package-name@npm:^4.0.0": version: 4.0.0 resolution: "@types/validate-npm-package-name@npm:4.0.0" @@ -9460,13 +9166,6 @@ __metadata: languageName: node linkType: hard -"aes-js@npm:^3.1.2": - version: 3.1.2 - resolution: "aes-js@npm:3.1.2" - checksum: 10/b65916767034a51375a3ac5aad62af452d89a386c1ae7b607bb9145d0bb8b8823bf2f3eba85bdfa52d61c65d5aed90ba90f677b8c826bfa1a8b7ae2fa3b54d91 - languageName: node - linkType: hard - "agent-base@npm:6, agent-base@npm:^6.0.2": version: 6.0.2 resolution: "agent-base@npm:6.0.2" @@ -9956,15 +9655,6 @@ __metadata: languageName: node linkType: hard -"async-mutex@npm:^0.5.0": - version: 0.5.0 - resolution: "async-mutex@npm:0.5.0" - dependencies: - tslib: "npm:^2.4.0" - checksum: 10/4c6bfce1cc9cd43f723c4d96403ac5f4757f885c953b839cde6956ec8817ff39623b82d67614de10c7933e21626925882fb9bac367db7d15d7cb4f84228722c9 - languageName: node - linkType: hard - "async@npm:^3.2.3, async@npm:^3.2.4": version: 3.2.4 resolution: "async@npm:3.2.4" @@ -10173,15 +9863,6 @@ __metadata: languageName: node linkType: hard -"base-x@npm:^3.0.2": - version: 3.0.10 - resolution: "base-x@npm:3.0.10" - dependencies: - safe-buffer: "npm:^5.0.1" - checksum: 10/52307739559e81d9980889de2359cb4f816cc0eb9a463028fa3ab239ab913d9044a1b47b4520f98e68453df32a457b8ba58b8d0ee7e757fc3fb971f3fa7a1482 - languageName: node - linkType: hard - "base64-js@npm:^1.0.2, base64-js@npm:^1.3.1": version: 1.5.1 resolution: "base64-js@npm:1.5.1" @@ -10212,13 +9893,6 @@ __metadata: languageName: node linkType: hard -"bech32@npm:^2.0.0": - version: 2.0.0 - resolution: "bech32@npm:2.0.0" - checksum: 10/fa15acb270b59aa496734a01f9155677b478987b773bf701f465858bf1606c6a970085babd43d71ce61895f1baa594cb41a2cd1394bd2c6698f03cc2d811300e - languageName: node - linkType: hard - "big-integer@npm:^1.6.17": version: 1.6.51 resolution: "big-integer@npm:1.6.51" @@ -10233,7 +9907,7 @@ __metadata: languageName: node linkType: hard -"bignumber.js@npm:^9.0.1, bignumber.js@npm:^9.1.2": +"bignumber.js@npm:^9.1.2": version: 9.1.2 resolution: "bignumber.js@npm:9.1.2" checksum: 10/d89b8800a987225d2c00dcbf8a69dc08e92aa0880157c851c287b307d31ceb2fc2acb0c62c3e3a3d42b6c5fcae9b004035f13eb4386e56d529d7edac18d5c9d8 @@ -10299,13 +9973,6 @@ __metadata: languageName: node linkType: hard -"blakejs@npm:^1.1.0": - version: 1.2.1 - resolution: "blakejs@npm:1.2.1" - checksum: 10/0638b1bd058b21892633929c43005aa6a4cc4b2ac5b338a146c3c076622f1b360795bd7a4d1f077c9b01863ed2df0c1504a81c5b520d164179120434847e6cd7 - languageName: node - linkType: hard - "bluebird@npm:~3.4.1": version: 3.4.7 resolution: "bluebird@npm:3.4.7" @@ -10313,7 +9980,7 @@ __metadata: languageName: node linkType: hard -"bn.js@npm:5.2.1, bn.js@npm:^5.0.0, bn.js@npm:^5.1.2, bn.js@npm:^5.2.0, bn.js@npm:^5.2.1": +"bn.js@npm:5.2.1, bn.js@npm:^5.0.0, bn.js@npm:^5.2.1": version: 5.2.1 resolution: "bn.js@npm:5.2.1" checksum: 10/7a7e8764d7a6e9708b8b9841b2b3d6019cc154d2fc23716d0efecfe1e16921b7533c6f7361fb05471eab47986c4aa310c270f88e3507172104632ac8df2cfd84 @@ -10442,7 +10109,7 @@ __metadata: languageName: node linkType: hard -"browserify-aes@npm:^1.0.0, browserify-aes@npm:^1.0.4, browserify-aes@npm:^1.2.0": +"browserify-aes@npm:^1.0.0, browserify-aes@npm:^1.0.4": version: 1.2.0 resolution: "browserify-aes@npm:1.2.0" dependencies: @@ -10596,26 +10263,6 @@ __metadata: languageName: node linkType: hard -"bs58@npm:^4.0.0": - version: 4.0.1 - resolution: "bs58@npm:4.0.1" - dependencies: - base-x: "npm:^3.0.2" - checksum: 10/b3c5365bb9e0c561e1a82f1a2d809a1a692059fae016be233a6127ad2f50a6b986467c3a50669ce4c18929dcccb297c5909314dd347a25a68c21b68eb3e95ac2 - languageName: node - linkType: hard - -"bs58check@npm:^2.1.2": - version: 2.1.2 - resolution: "bs58check@npm:2.1.2" - dependencies: - bs58: "npm:^4.0.0" - create-hash: "npm:^1.1.0" - safe-buffer: "npm:^5.1.2" - checksum: 10/43bdf08a5dd04581b78f040bc4169480e17008da482ffe2a6507327bbc4fc5c28de0501f7faf22901cfe57fbca79cbb202ca529003fedb4cb8dccd265b38e54d - languageName: node - linkType: hard - "bser@npm:2.1.1": version: 2.1.1 resolution: "bser@npm:2.1.1" @@ -10653,7 +10300,7 @@ __metadata: languageName: node linkType: hard -"buffer@npm:^5.1.0, buffer@npm:^5.2.1, buffer@npm:^5.5.0": +"buffer@npm:^5.2.1, buffer@npm:^5.5.0": version: 5.7.1 resolution: "buffer@npm:5.7.1" dependencies: @@ -10892,13 +10539,6 @@ __metadata: languageName: node linkType: hard -"cbor-sync@npm:^1.0.4": - version: 1.0.4 - resolution: "cbor-sync@npm:1.0.4" - checksum: 10/bdad5fbf442b5b2478ba59433cab145ad823f963f674ec42f3b730689e679327ec8a6dfab97724b63295badac915574139984e702475ff8025d7cb175e50e9ae - languageName: node - linkType: hard - "chainsaw@npm:~0.1.0": version: 0.1.0 resolution: "chainsaw@npm:0.1.0" @@ -11618,15 +11258,6 @@ __metadata: languageName: node linkType: hard -"crc@npm:^3.8.0": - version: 3.8.0 - resolution: "crc@npm:3.8.0" - dependencies: - buffer: "npm:^5.1.0" - checksum: 10/3a43061e692113d60fbaf5e438c5f6aa3374fe2368244a75cc083ecee6762513bcee8583f67c2c56feea0b0c72b41b7304fbd3c1e26cfcfaec310b9a18543fa8 - languageName: node - linkType: hard - "create-ecdh@npm:^4.0.0": version: 4.0.4 resolution: "create-ecdh@npm:4.0.4" @@ -12627,21 +12258,6 @@ __metadata: languageName: node linkType: hard -"elliptic@npm:^6.5.7": - version: 6.6.1 - resolution: "elliptic@npm:6.6.1" - dependencies: - bn.js: "npm:^4.11.9" - brorand: "npm:^1.1.0" - hash.js: "npm:^1.0.0" - hmac-drbg: "npm:^1.0.1" - inherits: "npm:^2.0.4" - minimalistic-assert: "npm:^1.0.1" - minimalistic-crypto-utils: "npm:^1.0.1" - checksum: 10/dc678c9febd89a219c4008ba3a9abb82237be853d9fd171cd602c8fb5ec39927e65c6b5e7a1b2a4ea82ee8e0ded72275e7932bb2da04a5790c2638b818e4e1c5 - languageName: node - linkType: hard - "emittery@npm:^0.13.1": version: 0.13.1 resolution: "emittery@npm:0.13.1" @@ -13680,29 +13296,6 @@ __metadata: languageName: node linkType: hard -"ethereum-cryptography@npm:^0.1.3": - version: 0.1.3 - resolution: "ethereum-cryptography@npm:0.1.3" - dependencies: - "@types/pbkdf2": "npm:^3.0.0" - "@types/secp256k1": "npm:^4.0.1" - blakejs: "npm:^1.1.0" - browserify-aes: "npm:^1.2.0" - bs58check: "npm:^2.1.2" - create-hash: "npm:^1.2.0" - create-hmac: "npm:^1.1.7" - hash.js: "npm:^1.1.7" - keccak: "npm:^3.0.0" - pbkdf2: "npm:^3.0.17" - randombytes: "npm:^2.1.0" - safe-buffer: "npm:^5.1.2" - scrypt-js: "npm:^3.0.0" - secp256k1: "npm:^4.0.1" - setimmediate: "npm:^1.0.5" - checksum: 10/975e476782746acd97d5b37366801ae622a52fb31e5d83f600804be230a61ef7b9d289dcecd9c308fb441967caf3a6e3768dd7c8add6441fcc60c398175d5a96 - languageName: node - linkType: hard - "ethereum-cryptography@npm:^2.0.0, ethereum-cryptography@npm:^2.1.2": version: 2.1.2 resolution: "ethereum-cryptography@npm:2.1.2" @@ -13715,35 +13308,6 @@ __metadata: languageName: node linkType: hard -"ethereumjs-util@npm:^7.1.2": - version: 7.1.5 - resolution: "ethereumjs-util@npm:7.1.5" - dependencies: - "@types/bn.js": "npm:^5.1.0" - bn.js: "npm:^5.1.2" - create-hash: "npm:^1.1.2" - ethereum-cryptography: "npm:^0.1.3" - rlp: "npm:^2.2.4" - checksum: 10/f28fc1ebb8f35bf9e418f76f51be737d94d603b912c3e014c4e87cd45ccd1b10bdfef764c8f152574b57e9faa260a18773cbc110f9e0a754d6b3730699e54dc9 - languageName: node - linkType: hard - -"ethereumjs-wallet@npm:^1.0.1": - version: 1.0.2 - resolution: "ethereumjs-wallet@npm:1.0.2" - dependencies: - aes-js: "npm:^3.1.2" - bs58check: "npm:^2.1.2" - ethereum-cryptography: "npm:^0.1.3" - ethereumjs-util: "npm:^7.1.2" - randombytes: "npm:^2.1.0" - scrypt-js: "npm:^3.0.1" - utf8: "npm:^3.0.0" - uuid: "npm:^8.3.2" - checksum: 10/0a9ad0ac0930627c15e6fa6115dde8d1dd81160e57fbdf81ef0bcd1e0edd750fe9e8b00992651e694cabefee29eef2ad651dce8b24ae5253861927d0e19b6c90 - languageName: node - linkType: hard - "ethers@npm:^6.3.0": version: 6.13.4 resolution: "ethers@npm:6.13.4" @@ -15130,7 +14694,7 @@ __metadata: languageName: node linkType: hard -"hash.js@npm:^1.0.0, hash.js@npm:^1.0.3, hash.js@npm:^1.1.7": +"hash.js@npm:^1.0.0, hash.js@npm:^1.0.3": version: 1.1.7 resolution: "hash.js@npm:1.1.7" dependencies: @@ -15149,18 +14713,6 @@ __metadata: languageName: node linkType: hard -"hdkey@npm:^2.0.1": - version: 2.1.0 - resolution: "hdkey@npm:2.1.0" - dependencies: - bs58check: "npm:^2.1.2" - ripemd160: "npm:^2.0.2" - safe-buffer: "npm:^5.1.1" - secp256k1: "npm:^4.0.0" - checksum: 10/c4ee2189ea3d87070ebd14ad7368e292b1e0b30e4d8a107eb8f33624634df6e57b8a3b2cda65b3bd97e88474f6798cfdbe7b63b6037429f0e169321d84a0db58 - languageName: node - linkType: hard - "he@npm:1.2.0, he@npm:^1.2.0": version: 1.2.0 resolution: "he@npm:1.2.0" @@ -16928,13 +16480,6 @@ __metadata: languageName: node linkType: hard -"jsbi@npm:^3.1.5": - version: 3.2.5 - resolution: "jsbi@npm:3.2.5" - checksum: 10/2cceb3a06dcb16493e936aa22384d912dd5f0a1fd474b97b5c6705011bd0aac8214d9a392a730b3f3ffb61a8fbe910a34d0fe881329be6a02857520d7a61ace6 - languageName: node - linkType: hard - "jsbn@npm:1.1.0": version: 1.1.0 resolution: "jsbn@npm:1.1.0" @@ -17133,13 +16678,6 @@ __metadata: languageName: node linkType: hard -"jsonschema@npm:^1.4.1": - version: 1.5.0 - resolution: "jsonschema@npm:1.5.0" - checksum: 10/46bf49b388ba922073bcb3c8d5e90af9d29fc8303dc866fd440182c88d6b4fd2807679fd39cdefb4113156d104ea47da9c0ff4bbcb0032c9fa29461cb1a92182 - languageName: node - linkType: hard - "jsx-ast-utils@npm:^2.4.1 || ^3.0.0": version: 3.3.3 resolution: "jsx-ast-utils@npm:3.3.3" @@ -17150,18 +16688,6 @@ __metadata: languageName: node linkType: hard -"keccak@npm:^3.0.0": - version: 3.0.4 - resolution: "keccak@npm:3.0.4" - dependencies: - node-addon-api: "npm:^2.0.0" - node-gyp: "npm:latest" - node-gyp-build: "npm:^4.2.0" - readable-stream: "npm:^3.6.0" - checksum: 10/45478bb0a57e44d0108646499b8360914b0fbc8b0e088f1076659cb34faaa9eb829c40f6dd9dadb3460bb86cc33153c41fed37fe5ce09465a60e71e78c23fa55 - languageName: node - linkType: hard - "keyv@npm:^4.5.3": version: 4.5.3 resolution: "keyv@npm:4.5.3" @@ -18542,24 +18068,6 @@ __metadata: languageName: node linkType: hard -"node-addon-api@npm:^2.0.0": - version: 2.0.2 - resolution: "node-addon-api@npm:2.0.2" - dependencies: - node-gyp: "npm:latest" - checksum: 10/e4ce4daac5b2fefa6b94491b86979a9c12d9cceba571d2c6df1eb5859f9da68e5dc198f128798e1785a88aafee6e11f4992dcccd4bf86bec90973927d158bd60 - languageName: node - linkType: hard - -"node-addon-api@npm:^5.0.0": - version: 5.1.0 - resolution: "node-addon-api@npm:5.1.0" - dependencies: - node-gyp: "npm:latest" - checksum: 10/595f59ffb4630564f587c502119cbd980d302e482781021f3b479f5fc7e41cf8f2f7280fdc2795f32d148e4f3259bd15043c52d4a3442796aa6f1ae97b959636 - languageName: node - linkType: hard - "node-addon-api@npm:^6.1.0": version: 6.1.0 resolution: "node-addon-api@npm:6.1.0" @@ -18626,17 +18134,6 @@ __metadata: languageName: node linkType: hard -"node-gyp-build@npm:^4.2.0": - version: 4.8.4 - resolution: "node-gyp-build@npm:4.8.4" - bin: - node-gyp-build: bin.js - node-gyp-build-optional: optional.js - node-gyp-build-test: build-test.js - checksum: 10/6a7d62289d1afc419fc8fc9bd00aa4e554369e50ca0acbc215cb91446148b75ff7e2a3b53c2c5b2c09a39d416d69f3d3237937860373104b5fe429bf30ad9ac5 - languageName: node - linkType: hard - "node-gyp@npm:^10.0.0": version: 10.0.1 resolution: "node-gyp@npm:10.0.1" @@ -19428,19 +18925,6 @@ __metadata: languageName: node linkType: hard -"pbkdf2@npm:^3.0.17": - version: 3.1.2 - resolution: "pbkdf2@npm:3.1.2" - dependencies: - create-hash: "npm:^1.1.2" - create-hmac: "npm:^1.1.4" - ripemd160: "npm:^2.0.1" - safe-buffer: "npm:^5.0.1" - sha.js: "npm:^2.4.8" - checksum: 10/40bdf30df1c9bb1ae41ec50c11e480cf0d36484b7c7933bf55e4451d1d0e3f09589df70935c56e7fccc5702779a0d7b842d012be8c08a187b44eb24d55bb9460 - languageName: node - linkType: hard - "pbkdf2@npm:^3.0.3": version: 3.1.1 resolution: "pbkdf2@npm:3.1.1" @@ -20937,7 +20421,7 @@ __metadata: languageName: node linkType: hard -"ripemd160@npm:^2.0.0, ripemd160@npm:^2.0.1, ripemd160@npm:^2.0.2": +"ripemd160@npm:^2.0.0, ripemd160@npm:^2.0.1": version: 2.0.2 resolution: "ripemd160@npm:2.0.2" dependencies: @@ -20947,26 +20431,6 @@ __metadata: languageName: node linkType: hard -"rlp@npm:^2.2.4, rlp@npm:^2.2.6": - version: 2.2.7 - resolution: "rlp@npm:2.2.7" - dependencies: - bn.js: "npm:^5.2.0" - bin: - rlp: bin/rlp - checksum: 10/cf1919a2dc99f336191b3363b76299db567c192b7ee3c6f5c722728c34f65577883c9c88eeb7a1bfcbc26693c8a4f1fb0662e79ee86f0c98dd258d6987303498 - languageName: node - linkType: hard - -"rlp@npm:^3.0.0": - version: 3.0.0 - resolution: "rlp@npm:3.0.0" - bin: - rlp: bin/rlp - checksum: 10/c85549fa5368ef029707d02f0937c0c503b69fb330c5941508c9eef537a4f179fbeecd17149aeb795d430ed5249b68d7c66383a9863068712a191d388786cfc1 - languageName: node - linkType: hard - "rollup-plugin-inject@npm:^3.0.0": version: 3.0.2 resolution: "rollup-plugin-inject@npm:3.0.2" @@ -21198,25 +20662,6 @@ __metadata: languageName: node linkType: hard -"scrypt-js@npm:^3.0.0, scrypt-js@npm:^3.0.1": - version: 3.0.1 - resolution: "scrypt-js@npm:3.0.1" - checksum: 10/2f8aa72b7f76a6f9c446bbec5670f80d47497bccce98474203d89b5667717223eeb04a50492ae685ed7adc5a060fc2d8f9fd988f8f7ebdaf3341967f3aeff116 - languageName: node - linkType: hard - -"secp256k1@npm:^4.0.0, secp256k1@npm:^4.0.1": - version: 4.0.4 - resolution: "secp256k1@npm:4.0.4" - dependencies: - elliptic: "npm:^6.5.7" - node-addon-api: "npm:^5.0.0" - node-gyp: "npm:latest" - node-gyp-build: "npm:^4.2.0" - checksum: 10/45000f348c853df7c1e2b67c48efb062ae78c0620ab1a5cfb02fa20d3aad39c641f4e7a18b3de3b54a7c0cc1e0addeb8ecd9d88bc332e92df17a92b60c36122a - languageName: node - linkType: hard - "select-hose@npm:^2.0.0": version: 2.0.0 resolution: "select-hose@npm:2.0.0" @@ -21387,7 +20832,7 @@ __metadata: languageName: node linkType: hard -"setimmediate@npm:^1.0.4, setimmediate@npm:^1.0.5, setimmediate@npm:~1.0.4": +"setimmediate@npm:^1.0.4, setimmediate@npm:~1.0.4": version: 1.0.5 resolution: "setimmediate@npm:1.0.5" checksum: 10/76e3f5d7f4b581b6100ff819761f04a984fa3f3990e72a6554b57188ded53efce2d3d6c0932c10f810b7c59414f85e2ab3c11521877d1dea1ce0b56dc906f485 @@ -21408,7 +20853,7 @@ __metadata: languageName: node linkType: hard -"sha.js@npm:^2.4.0, sha.js@npm:^2.4.11, sha.js@npm:^2.4.8": +"sha.js@npm:^2.4.0, sha.js@npm:^2.4.8": version: 2.4.11 resolution: "sha.js@npm:2.4.11" dependencies: @@ -22732,13 +22177,6 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^2.3.0": - version: 2.8.1 - resolution: "tslib@npm:2.8.1" - checksum: 10/3e2e043d5c2316461cb54e5c7fe02c30ef6dccb3384717ca22ae5c6b5bc95232a6241df19c622d9c73b809bea33b187f6dbc73030963e29950c2141bc32a79f7 - languageName: node - linkType: hard - "tsutils@npm:^3.21.0": version: 3.21.0 resolution: "tsutils@npm:3.21.0" @@ -23202,13 +22640,6 @@ __metadata: languageName: node linkType: hard -"utf8@npm:^3.0.0": - version: 3.0.0 - resolution: "utf8@npm:3.0.0" - checksum: 10/31d19c4faacbb65b09ebc1c21c32b20bdb0919c6f6773cee5001b99bb83f8e503e7233c08fc71ebb34f7cfebd95cec3243b81d90176097aa2f286cccb4ce866e - languageName: node - linkType: hard - "util-deprecate@npm:^1.0.1, util-deprecate@npm:^1.0.2, util-deprecate@npm:~1.0.1": version: 1.0.2 resolution: "util-deprecate@npm:1.0.2" From b7558660409e0de64bc3add80976842e54caf52f Mon Sep 17 00:00:00 2001 From: Maarten Zuidhoorn Date: Fri, 10 Jan 2025 14:34:36 +0100 Subject: [PATCH 07/12] Bump `async-mutex` to `0.5.0` --- .../invoke-snap/packages/core-signer/package.json | 2 +- packages/snaps-utils/package.json | 2 +- yarn.lock | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/examples/packages/invoke-snap/packages/core-signer/package.json b/packages/examples/packages/invoke-snap/packages/core-signer/package.json index 1f11726d56..57edf5722f 100644 --- a/packages/examples/packages/invoke-snap/packages/core-signer/package.json +++ b/packages/examples/packages/invoke-snap/packages/core-signer/package.json @@ -47,7 +47,7 @@ "@metamask/snaps-sdk": "workspace:^", "@metamask/utils": "^10.0.0", "@noble/curves": "^1.1.0", - "async-mutex": "^0.4.0" + "async-mutex": "^0.5.0" }, "devDependencies": { "@jest/globals": "^29.5.0", diff --git a/packages/snaps-utils/package.json b/packages/snaps-utils/package.json index 71e8474e54..aac7ff5a26 100644 --- a/packages/snaps-utils/package.json +++ b/packages/snaps-utils/package.json @@ -90,7 +90,7 @@ "@metamask/utils": "^10.0.0", "@noble/hashes": "^1.3.1", "@scure/base": "^1.1.1", - "async-mutex": "^0.4.0", + "async-mutex": "^0.5.0", "chalk": "^4.1.2", "cron-parser": "^4.5.0", "fast-deep-equal": "^3.1.3", diff --git a/yarn.lock b/yarn.lock index 29065c5256..71a8f9b18d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4204,7 +4204,7 @@ __metadata: "@swc/jest": "npm:^0.2.26" "@typescript-eslint/eslint-plugin": "npm:^5.42.1" "@typescript-eslint/parser": "npm:^6.21.0" - async-mutex: "npm:^0.4.0" + async-mutex: "npm:^0.5.0" deepmerge: "npm:^4.2.2" depcheck: "npm:^1.4.7" eslint: "npm:^8.27.0" @@ -6281,7 +6281,7 @@ __metadata: "@wdio/spec-reporter": "npm:^8.19.0" "@wdio/static-server-service": "npm:^8.19.0" "@wdio/types": "npm:^8.19.0" - async-mutex: "npm:^0.4.0" + async-mutex: "npm:^0.5.0" chalk: "npm:^4.1.2" cron-parser: "npm:^4.5.0" deepmerge: "npm:^4.2.2" @@ -9646,12 +9646,12 @@ __metadata: languageName: node linkType: hard -"async-mutex@npm:^0.4.0": - version: 0.4.0 - resolution: "async-mutex@npm:0.4.0" +"async-mutex@npm:^0.5.0": + version: 0.5.0 + resolution: "async-mutex@npm:0.5.0" dependencies: tslib: "npm:^2.4.0" - checksum: 10/4a55065aae8c7283e45e2a8ac38ba9812f030696640d650c4ec62cfd67e5d61bd698e67b758a81fcb845e2d5ea1d857106f9235cc4282ad40cd1944b26fde1b2 + checksum: 10/4c6bfce1cc9cd43f723c4d96403ac5f4757f885c953b839cde6956ec8817ff39623b82d67614de10c7933e21626925882fb9bac367db7d15d7cb4f84228722c9 languageName: node linkType: hard From 8d0922d0d712f8457b0cbf6b883d200261ac2fb9 Mon Sep 17 00:00:00 2001 From: Maarten Zuidhoorn Date: Fri, 10 Jan 2025 14:36:54 +0100 Subject: [PATCH 08/12] Remove unused properties --- packages/snaps-controllers/src/test-utils/controller.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/snaps-controllers/src/test-utils/controller.ts b/packages/snaps-controllers/src/test-utils/controller.ts index 1f2abe725d..c4b33a9f8b 100644 --- a/packages/snaps-controllers/src/test-utils/controller.ts +++ b/packages/snaps-controllers/src/test-utils/controller.ts @@ -11,8 +11,6 @@ import { exportKey, generateSalt, isVaultUpdated, - encrypt, - decrypt, } from '@metamask/browser-passworder'; import type { PermissionConstraint, @@ -545,8 +543,6 @@ export const DEFAULT_ENCRYPTION_KEY_DERIVATION_OPTIONS = { export const getSnapControllerEncryptor = () => { return { - encrypt, - decrypt, encryptWithKey, decryptWithKey, keyFromPassword: async ( From b6104da9cf7e6a545a8d59cf9394db2da0f84fdd Mon Sep 17 00:00:00 2001 From: Maarten Zuidhoorn Date: Mon, 13 Jan 2025 11:35:15 +0100 Subject: [PATCH 09/12] Use mutex per Snap and add test --- packages/snaps-controllers/coverage.json | 6 +- packages/snaps-controllers/package.json | 1 + .../src/snaps/SnapController.test.tsx | 67 +++++++++++++++++++ .../src/snaps/SnapController.ts | 28 +++++--- packages/snaps-utils/coverage.json | 6 +- packages/snaps-utils/src/index.ts | 1 - packages/snaps-utils/src/mutex.test.ts | 38 ----------- packages/snaps-utils/src/mutex.ts | 24 ------- yarn.lock | 1 + 9 files changed, 94 insertions(+), 78 deletions(-) delete mode 100644 packages/snaps-utils/src/mutex.test.ts delete mode 100644 packages/snaps-utils/src/mutex.ts diff --git a/packages/snaps-controllers/coverage.json b/packages/snaps-controllers/coverage.json index 609decfdbc..7a462547c5 100644 --- a/packages/snaps-controllers/coverage.json +++ b/packages/snaps-controllers/coverage.json @@ -1,6 +1,6 @@ { - "branches": 92.41, + "branches": 92.96, "functions": 96.56, - "lines": 98, - "statements": 97.72 + "lines": 98.05, + "statements": 97.77 } diff --git a/packages/snaps-controllers/package.json b/packages/snaps-controllers/package.json index ec3fd6f31d..464a5fb805 100644 --- a/packages/snaps-controllers/package.json +++ b/packages/snaps-controllers/package.json @@ -95,6 +95,7 @@ "@metamask/snaps-utils": "workspace:^", "@metamask/utils": "^10.0.0", "@xstate/fsm": "^2.0.0", + "async-mutex": "^0.5.0", "browserify-zlib": "^0.2.0", "concat-stream": "^2.0.0", "fast-deep-equal": "^3.1.3", diff --git a/packages/snaps-controllers/src/snaps/SnapController.test.tsx b/packages/snaps-controllers/src/snaps/SnapController.test.tsx index 84ce803216..ade910a6dc 100644 --- a/packages/snaps-controllers/src/snaps/SnapController.test.tsx +++ b/packages/snaps-controllers/src/snaps/SnapController.test.tsx @@ -9221,6 +9221,73 @@ describe('SnapController', () => { snapController.destroy(); }); + it('queues multiple state updates', async () => { + const messenger = getSnapControllerMessenger(); + + jest.useFakeTimers(); + + const encryptor = getSnapControllerEncryptor(); + const { promise, resolve } = createDeferredPromise(); + const encryptWithKey = jest + .fn< + ReturnType, + Parameters + >() + .mockImplementation(async (...args) => { + resolve(); + await sleep(1); + return await encryptor.encryptWithKey(...args); + }); + + const snapController = getSnapController( + getSnapControllerOptions({ + messenger, + state: { + snaps: getPersistedSnapsState(), + }, + encryptor: { + ...getSnapControllerEncryptor(), + // @ts-expect-error - Missing required properties. + encryptWithKey, + }, + }), + ); + + const firstStateChange = waitForStateChange(messenger); + await messenger.call( + 'SnapController:updateSnapState', + MOCK_SNAP_ID, + { foo: 'bar' }, + true, + ); + + await messenger.call( + 'SnapController:updateSnapState', + MOCK_SNAP_ID, + { bar: 'baz' }, + true, + ); + + // We await this promise to ensure the timer is queued. + await promise; + jest.advanceTimersByTime(1); + + // After this point the second update should be queued. + await firstStateChange; + const secondStateChange = waitForStateChange(messenger); + + expect(encryptWithKey).toHaveBeenCalledTimes(1); + + // This is a bit hacky, but we can't simply advance the timer by 1ms + // because the second timer is not running yet. + jest.useRealTimers(); + await secondStateChange; + + expect(encryptWithKey).toHaveBeenCalledTimes(2); + + snapController.destroy(); + }); + it('logs an error message if the state fails to persist', async () => { const messenger = getSnapControllerMessenger(); diff --git a/packages/snaps-controllers/src/snaps/SnapController.ts b/packages/snaps-controllers/src/snaps/SnapController.ts index b261b6390d..040bc643b3 100644 --- a/packages/snaps-controllers/src/snaps/SnapController.ts +++ b/packages/snaps-controllers/src/snaps/SnapController.ts @@ -65,7 +65,6 @@ import type { TruncatedSnapFields, } from '@metamask/snaps-utils'; import { - withMutex, logWarning, getPlatformVersion, assertIsSnapManifest, @@ -110,6 +109,7 @@ import { } from '@metamask/utils'; import type { StateMachine } from '@xstate/fsm'; import { createMachine, interpret } from '@xstate/fsm'; +import { Mutex } from 'async-mutex'; import type { Patch } from 'immer'; import { nanoid } from 'nanoid'; import semver from 'semver'; @@ -263,6 +263,11 @@ export interface SnapRuntimeData { * Cached unencrypted state of the Snap. */ unencryptedState?: Record | null; + + /** + * A mutex to prevent concurrent state updates. + */ + stateMutex: Mutex; } export type SnapError = { @@ -1917,17 +1922,21 @@ export class SnapController extends BaseController< /** * Persist the state of a Snap. * + * This is run with a mutex to ensure that only one state update per Snap is + * processed at a time, avoiding possible race conditions. + * * @param snapId - The Snap ID. * @param newSnapState - The new state of the Snap. * @param encrypted - A flag to indicate whether to use encrypted storage or * not. */ - #persistSnapState = withMutex( - async ( - snapId: SnapId, - newSnapState: Record | null, - encrypted: boolean, - ) => { + async #persistSnapState( + snapId: SnapId, + newSnapState: Record | null, + encrypted: boolean, + ) { + const runtime = this.#getRuntimeExpect(snapId); + await runtime.stateMutex.runExclusive(async () => { const newState = await this.#getStateToPersist( snapId, newSnapState, @@ -1943,8 +1952,8 @@ export class SnapController extends BaseController< return this.update((state) => { state.unencryptedSnapStates[snapId] = newState; }); - }, - ); + }); + } /** * Updates the own state of the snap with the given id. @@ -3812,6 +3821,7 @@ export class SnapController extends BaseController< pendingOutboundRequests: 0, interpreter, stopping: false, + stateMutex: new Mutex(), }); } diff --git a/packages/snaps-utils/coverage.json b/packages/snaps-utils/coverage.json index 80c85671b3..a9248b7438 100644 --- a/packages/snaps-utils/coverage.json +++ b/packages/snaps-utils/coverage.json @@ -1,6 +1,6 @@ { "branches": 99.74, - "functions": 98.95, - "lines": 99.47, - "statements": 96.33 + "functions": 98.93, + "lines": 99.46, + "statements": 96.31 } diff --git a/packages/snaps-utils/src/index.ts b/packages/snaps-utils/src/index.ts index e2db82cee4..7cfb076bdf 100644 --- a/packages/snaps-utils/src/index.ts +++ b/packages/snaps-utils/src/index.ts @@ -20,7 +20,6 @@ export * from './json-rpc'; export * from './localization'; export * from './logging'; export * from './manifest'; -export * from './mutex'; export * from './namespace'; export * from './path'; export * from './platform-version'; diff --git a/packages/snaps-utils/src/mutex.test.ts b/packages/snaps-utils/src/mutex.test.ts deleted file mode 100644 index 794b500962..0000000000 --- a/packages/snaps-utils/src/mutex.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { createDeferredPromise } from '@metamask/utils'; - -import { withMutex } from './mutex'; - -describe('withMutex', () => { - it('runs the function with a mutex', async () => { - jest.useFakeTimers(); - - const { promise, resolve: resolveDeferred } = createDeferredPromise(); - - const fn = jest.fn().mockImplementation(async () => { - return await new Promise((resolve) => { - resolveDeferred(); - setTimeout(() => { - resolve(); - }, 1000); - }); - }); - - const wrappedFn = withMutex(fn); - - const first = wrappedFn(); - const second = wrappedFn(); - - await promise; - jest.advanceTimersByTime(1000); - - expect(fn).toHaveBeenCalledTimes(1); - - await first; - - jest.advanceTimersByTime(1000); - - await second; - - expect(fn).toHaveBeenCalledTimes(2); - }); -}); diff --git a/packages/snaps-utils/src/mutex.ts b/packages/snaps-utils/src/mutex.ts deleted file mode 100644 index a1899bbc8c..0000000000 --- a/packages/snaps-utils/src/mutex.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Mutex } from 'async-mutex'; - -/** - * Run a function with an async mutex, ensuring that only one instance of the - * function can run at a time. - * - * @param fn - The function to run with a mutex. - * @returns The wrapped function. - * @template OriginalFunction - The original function type. This is inferred - * from the `fn` argument, and used to determine the return type of the - * wrapped function. - */ -export function withMutex< - OriginalFunction extends (...args: any[]) => Promise, - Type, ->( - fn: OriginalFunction, -): (...args: Parameters) => Promise { - const mutex = new Mutex(); - - return async (...args: Parameters) => { - return await mutex.runExclusive(async () => await fn(...args)); - }; -} diff --git a/yarn.lock b/yarn.lock index 71a8f9b18d..5eb3ce5055 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5752,6 +5752,7 @@ __metadata: "@wdio/spec-reporter": "npm:^8.19.0" "@wdio/static-server-service": "npm:^8.19.0" "@xstate/fsm": "npm:^2.0.0" + async-mutex: "npm:^0.5.0" browserify-zlib: "npm:^0.2.0" concat-stream: "npm:^2.0.0" deepmerge: "npm:^4.2.2" From 0f482ff43408b506aaa1d20ca54169a459d55579 Mon Sep 17 00:00:00 2001 From: Maarten Zuidhoorn Date: Mon, 13 Jan 2025 11:35:44 +0100 Subject: [PATCH 10/12] Remove dependency from `snaps-utils` --- packages/snaps-utils/package.json | 1 - yarn.lock | 1 - 2 files changed, 2 deletions(-) diff --git a/packages/snaps-utils/package.json b/packages/snaps-utils/package.json index aac7ff5a26..442d27174e 100644 --- a/packages/snaps-utils/package.json +++ b/packages/snaps-utils/package.json @@ -90,7 +90,6 @@ "@metamask/utils": "^10.0.0", "@noble/hashes": "^1.3.1", "@scure/base": "^1.1.1", - "async-mutex": "^0.5.0", "chalk": "^4.1.2", "cron-parser": "^4.5.0", "fast-deep-equal": "^3.1.3", diff --git a/yarn.lock b/yarn.lock index 5eb3ce5055..b5e59c6899 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6282,7 +6282,6 @@ __metadata: "@wdio/spec-reporter": "npm:^8.19.0" "@wdio/static-server-service": "npm:^8.19.0" "@wdio/types": "npm:^8.19.0" - async-mutex: "npm:^0.5.0" chalk: "npm:^4.1.2" cron-parser: "npm:^4.5.0" deepmerge: "npm:^4.2.2" From c137331ee1d96ca2b52e647f548a36b5622c1f9c Mon Sep 17 00:00:00 2001 From: Maarten Zuidhoorn Date: Mon, 13 Jan 2025 11:57:42 +0100 Subject: [PATCH 11/12] Update shasum --- .../invoke-snap/packages/core-signer/snap.manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/examples/packages/invoke-snap/packages/core-signer/snap.manifest.json b/packages/examples/packages/invoke-snap/packages/core-signer/snap.manifest.json index e90d64456b..1e901ffa31 100644 --- a/packages/examples/packages/invoke-snap/packages/core-signer/snap.manifest.json +++ b/packages/examples/packages/invoke-snap/packages/core-signer/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/snaps.git" }, "source": { - "shasum": "9+79ZuJLehTDLvMMK/dR0C29/5Q/GRdvTq8EaxTwQkU=", + "shasum": "5YpYX3b3wdRQEjPd2lUeNsNK7FwiflxMLCCPYtDeLnQ=", "location": { "npm": { "filePath": "dist/bundle.js", From 6d5edf738ef1ee270642562c6ef045ef1f963db0 Mon Sep 17 00:00:00 2001 From: Maarten Zuidhoorn Date: Mon, 13 Jan 2025 12:25:06 +0100 Subject: [PATCH 12/12] Add assertion to test and update JSDoc --- packages/snaps-controllers/src/snaps/SnapController.test.tsx | 4 ++++ packages/snaps-controllers/src/snaps/SnapController.ts | 5 +++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/snaps-controllers/src/snaps/SnapController.test.tsx b/packages/snaps-controllers/src/snaps/SnapController.test.tsx index ade910a6dc..aaa463bfed 100644 --- a/packages/snaps-controllers/src/snaps/SnapController.test.tsx +++ b/packages/snaps-controllers/src/snaps/SnapController.test.tsx @@ -9285,6 +9285,10 @@ describe('SnapController', () => { expect(encryptWithKey).toHaveBeenCalledTimes(2); + expect( + await messenger.call('SnapController:getSnapState', MOCK_SNAP_ID, true), + ).toStrictEqual({ bar: 'baz' }); + snapController.destroy(); }); diff --git a/packages/snaps-controllers/src/snaps/SnapController.ts b/packages/snaps-controllers/src/snaps/SnapController.ts index 040bc643b3..655c1bcdec 100644 --- a/packages/snaps-controllers/src/snaps/SnapController.ts +++ b/packages/snaps-controllers/src/snaps/SnapController.ts @@ -1894,8 +1894,9 @@ export class SnapController extends BaseController< * flag. * * - If the state is null, return null. - * - If the state should not be encrypted, return the JSON stringified state. - * - Otherwise if the state should be encrypted, return the encrypted state. + * - If the state should be encrypted, return the encrypted state. + * - Otherwise, if the state should not be encrypted, return the JSON- + * stringified state. * * @param snapId - The Snap ID. * @param state - The state to persist.