diff --git a/packages/snaps-controllers/CHANGELOG.md b/packages/snaps-controllers/CHANGELOG.md index cc6ae41682..bf8ed73804 100644 --- a/packages/snaps-controllers/CHANGELOG.md +++ b/packages/snaps-controllers/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- **BREAKING:** All action types were renamed from `DoSomething` to `ControllerNameDoSomethingAction` ([#3907](https://github.com/MetaMask/snaps/pull/3907), [#3911](https://github.com/MetaMask/snaps/pull/3911), [#3912](https://github.com/MetaMask/snaps/pull/3912), [#3916](https://github.com/MetaMask/snaps/pull/3916)) +- **BREAKING:** All action types were renamed from `DoSomething` to `ControllerNameDoSomethingAction` ([#3907](https://github.com/MetaMask/snaps/pull/3907), [#3911](https://github.com/MetaMask/snaps/pull/3911), [#3912](https://github.com/MetaMask/snaps/pull/3912), [#3916](https://github.com/MetaMask/snaps/pull/3916), [#3918](https://github.com/MetaMask/snaps/pull/3918)) - `SnapController` actions: - `GetSnap` is now `SnapControllerGetSnapAction`. - Note: The method is now called `getSnap` instead of `get`. @@ -56,6 +56,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `HandleRequest` is now `ExecutionServiceHandleRequestAction`. - `TerminateSnap` is now `ExecutionServiceTerminateSnapAction`. - `GetExecutionStatus` is now `ExecutionServiceGetExecutionStatusAction`. + - `SnapRegistryController` actions: + - `GetResult` is now `SnapRegistryControllerGetAction`. + - `GetMetadata` is now `SnapRegistryControllerGetMetadataAction`. + - `ResolveVersion` is now `SnapRegistryControllerResolveVersionAction`. + - `Update` is now `SnapRegistryControllerRequestUpdateAction`. + - Note: The method is now called `requestUpdate` instead of `update`. - **BREAKING:** All event types were renamed from `OnSomething` to `ControllerOnSomethingEvent` ([#3907](https://github.com/MetaMask/snaps/pull/3907), [#3916](https://github.com/MetaMask/snaps/pull/3916)) - `SnapController` events: - `SnapStateChange` was removed in favour of `SnapControllerStateChangeEvent`. @@ -76,6 +82,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `OutboundResponse` is now `ExecutionServiceOutboundResponseEvent`. - **BREAKING:**: Rename `MultichainRouter` to `MultichainRoutingService` and update action types accordingly ([#3913](https://github.com/MetaMask/snaps/pull/3913)) - This is consistent with the naming of other services. +- **BREAKING:** Rename `JsonSnapsRegistry` to `SnapRegistryController` and update action types accordingly ([#3918](https://github.com/MetaMask/snaps/pull/3918)) + - This is consistent with the naming of other controllers. + - The controller name is now `SnapRegistryController` instead of `SnapsRegistry` as well. - **BREAKING:** `MultichainRoutingService` now requires `SnapController:getRunnableSnaps` instead of `SnapController:getAllSnaps` ([#3913](https://github.com/MetaMask/snaps/pull/3913)) - **BREAKING:** `SnapInsightsController` now requires `SnapController:getRunnableSnaps` instead of `SnapController:getAllSnaps` ([#3915](https://github.com/MetaMask/snaps/pull/3915)) - **RREAKING:** Replace `ExecutionService` interface with abstract class ([#3916](https://github.com/MetaMask/snaps/pull/3916)) diff --git a/packages/snaps-controllers/coverage.json b/packages/snaps-controllers/coverage.json index b63ccf012e..428f6713f9 100644 --- a/packages/snaps-controllers/coverage.json +++ b/packages/snaps-controllers/coverage.json @@ -2,5 +2,5 @@ "branches": 94.97, "functions": 98.78, "lines": 98.63, - "statements": 98.43 + "statements": 98.32 } diff --git a/packages/snaps-controllers/src/snaps/SnapController.test.tsx b/packages/snaps-controllers/src/snaps/SnapController.test.tsx index e81c630ae1..13a438111d 100644 --- a/packages/snaps-controllers/src/snaps/SnapController.test.tsx +++ b/packages/snaps-controllers/src/snaps/SnapController.test.tsx @@ -87,7 +87,7 @@ import { METAMASK_ORIGIN, STATE_DEBOUNCE_TIMEOUT, } from './constants'; -import { SnapsRegistryStatus } from './registry'; +import { SnapRegistryStatus } from './registry'; import type { SnapControllerState } from './SnapController'; import { controllerName, @@ -127,7 +127,7 @@ import { MOCK_SNAP_PERMISSIONS, MOCK_SNAP_SUBJECT_METADATA, MOCK_WALLET_SNAP_PERMISSION, - MockSnapsRegistry, + MockSnapRegistryController, sleep, waitForStateChange, } from '../test-utils'; @@ -817,7 +817,7 @@ describe('SnapController', () => { expect(options.messenger.call).toHaveBeenNthCalledWith( 2, - 'SnapsRegistry:get', + 'SnapRegistryController:get', { [MOCK_SNAP_ID]: { version: '1.0.0', @@ -1048,7 +1048,7 @@ describe('SnapController', () => { it('throws an error if snap is not on allowlist and allowlisting is required but resolve succeeds', async () => { const rootMessenger = getRootMessenger(); - const registry = new MockSnapsRegistry(rootMessenger); + const registry = new MockSnapRegistryController(rootMessenger); const controller = await getSnapController( getSnapControllerOptions({ @@ -1077,7 +1077,7 @@ describe('SnapController', () => { it('throws an error if the registry is unavailable and allowlisting is required but resolve succeeds', async () => { const rootMessenger = getRootMessenger(); - const registry = new MockSnapsRegistry(rootMessenger); + const registry = new MockSnapRegistryController(rootMessenger); const controller = await getSnapController( getSnapControllerOptions({ @@ -1091,7 +1091,7 @@ describe('SnapController', () => { // Mock resolve to succeed, but registry.get() will fail later registry.resolveVersion.mockReturnValue('1.0.0'); registry.get.mockReturnValue({ - [MOCK_SNAP_ID]: { status: SnapsRegistryStatus.Unavailable }, + [MOCK_SNAP_ID]: { status: SnapRegistryStatus.Unavailable }, }); await expect( @@ -1141,7 +1141,7 @@ describe('SnapController', () => { it('resolves to allowlisted version when allowlisting is required', async () => { const rootMessenger = getRootMessenger(); - const registry = new MockSnapsRegistry(rootMessenger); + const registry = new MockSnapRegistryController(rootMessenger); const { manifest, sourceCode, svgIcon } = await getMockSnapFilesWithUpdatedChecksum({ @@ -1151,7 +1151,7 @@ describe('SnapController', () => { }); registry.get.mockResolvedValueOnce({ - [MOCK_SNAP_ID]: { status: SnapsRegistryStatus.Verified }, + [MOCK_SNAP_ID]: { status: SnapRegistryStatus.Verified }, }); registry.resolveVersion.mockReturnValue('1.1.0'); @@ -1181,7 +1181,7 @@ describe('SnapController', () => { it('does not use registry resolving when allowlist is not required', async () => { const rootMessenger = getRootMessenger(); - const registry = new MockSnapsRegistry(rootMessenger); + const registry = new MockSnapRegistryController(rootMessenger); const controller = await getSnapController( getSnapControllerOptions({ @@ -8918,7 +8918,7 @@ describe('SnapController', () => { it('throws an error if the new version of the snap is blocked', async () => { const rootMessenger = getRootMessenger(); - const registry = new MockSnapsRegistry(rootMessenger); + const registry = new MockSnapRegistryController(rootMessenger); const { manifest } = await getMockSnapFilesWithUpdatedChecksum({ manifest: getSnapManifest({ @@ -8940,7 +8940,7 @@ describe('SnapController', () => { ); registry.get.mockResolvedValueOnce({ - [MOCK_SNAP_ID]: { status: SnapsRegistryStatus.Blocked }, + [MOCK_SNAP_ID]: { status: SnapRegistryStatus.Blocked }, }); await expect( @@ -10519,7 +10519,7 @@ describe('SnapController', () => { describe('updateRegistry', () => { it('updates the registry database', async () => { const rootMessenger = getRootMessenger(); - const registry = new MockSnapsRegistry(rootMessenger); + const registry = new MockSnapRegistryController(rootMessenger); const snapController = await getSnapController( getSnapControllerOptions({ @@ -10531,14 +10531,14 @@ describe('SnapController', () => { ); await snapController.updateRegistry(); - expect(registry.update).toHaveBeenCalled(); + expect(registry.requestUpdate).toHaveBeenCalled(); snapController.destroy(); }); it('blocks snaps as expected', async () => { const rootMessenger = getRootMessenger(); - const registry = new MockSnapsRegistry(rootMessenger); + const registry = new MockSnapRegistryController(rootMessenger); const mockSnapA = getMockSnapData({ id: 'npm:exampleA' as SnapId, @@ -10569,7 +10569,7 @@ describe('SnapController', () => { // Block snap A, ignore B. registry.get.mockResolvedValueOnce({ [mockSnapA.id]: { - status: SnapsRegistryStatus.Blocked, + status: SnapRegistryStatus.Blocked, reason: { explanation, infoUrl }, }, }); @@ -10609,7 +10609,7 @@ describe('SnapController', () => { it('stops running snaps when they are blocked', async () => { const rootMessenger = getRootMessenger(); - const registry = new MockSnapsRegistry(rootMessenger); + const registry = new MockSnapRegistryController(rootMessenger); const mockSnap = getMockSnapData({ id: 'npm:example' as SnapId, @@ -10629,7 +10629,7 @@ describe('SnapController', () => { // Block the snap registry.get.mockResolvedValueOnce({ - [mockSnap.id]: { status: SnapsRegistryStatus.Blocked }, + [mockSnap.id]: { status: SnapRegistryStatus.Blocked }, }); await snapController.updateRegistry(); await waitForStateChange(options.messenger); @@ -10644,7 +10644,7 @@ describe('SnapController', () => { it('unblocks snaps as expected', async () => { const rootMessenger = getRootMessenger(); - const registry = new MockSnapsRegistry(rootMessenger); + const registry = new MockSnapRegistryController(rootMessenger); const mockSnapA = getMockSnapData({ id: 'npm:exampleA' as SnapId, @@ -10683,8 +10683,8 @@ describe('SnapController', () => { // Indicate that both snaps A and B are unblocked, and update blocked // states. registry.get.mockResolvedValueOnce({ - [mockSnapA.id]: { status: SnapsRegistryStatus.Unverified }, - [mockSnapB.id]: { status: SnapsRegistryStatus.Unverified }, + [mockSnapA.id]: { status: SnapRegistryStatus.Unverified }, + [mockSnapB.id]: { status: SnapRegistryStatus.Unverified }, }); await snapController.updateRegistry(); @@ -10707,7 +10707,7 @@ describe('SnapController', () => { it('updating blocked snaps does not throw if a snap is removed while fetching the blocklist', async () => { const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(); const rootMessenger = getRootMessenger(); - const registry = new MockSnapsRegistry(rootMessenger); + const registry = new MockSnapRegistryController(rootMessenger); const mockSnap = getMockSnapData({ id: 'npm:example' as SnapId, @@ -10736,7 +10736,7 @@ describe('SnapController', () => { // Resolve the blocklist and wait for the call to complete resolveBlockListPromise({ - [mockSnap.id]: { status: SnapsRegistryStatus.Blocked }, + [mockSnap.id]: { status: SnapRegistryStatus.Blocked }, }); await updateBlockList; @@ -10750,7 +10750,7 @@ describe('SnapController', () => { it('logs but does not throw unexpected errors while blocking', async () => { const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(); const rootMessenger = getRootMessenger(); - const registry = new MockSnapsRegistry(rootMessenger); + const registry = new MockSnapRegistryController(rootMessenger); const mockSnap = getMockSnapData({ id: 'npm:example' as SnapId, @@ -10774,7 +10774,7 @@ describe('SnapController', () => { // Block the snap registry.get.mockResolvedValueOnce({ - [mockSnap.id]: { status: SnapsRegistryStatus.Blocked }, + [mockSnap.id]: { status: SnapRegistryStatus.Blocked }, }); await snapController.updateRegistry(); @@ -10793,7 +10793,7 @@ describe('SnapController', () => { it('updates preinstalled Snaps', async () => { const rootMessenger = getRootMessenger(); - const registry = new MockSnapsRegistry(rootMessenger); + const registry = new MockSnapRegistryController(rootMessenger); // Simulate previous permissions, some of which will be removed rootMessenger.registerActionHandler( @@ -10889,7 +10889,7 @@ describe('SnapController', () => { it('does not update preinstalled Snaps when the feature flag is off', async () => { const rootMessenger = getRootMessenger(); - const registry = new MockSnapsRegistry(rootMessenger); + const registry = new MockSnapRegistryController(rootMessenger); const snapId = 'npm:@metamask/jsx-example-snap' as SnapId; diff --git a/packages/snaps-controllers/src/snaps/SnapController.ts b/packages/snaps-controllers/src/snaps/SnapController.ts index 2e47e79aad..3257562723 100644 --- a/packages/snaps-controllers/src/snaps/SnapController.ts +++ b/packages/snaps-controllers/src/snaps/SnapController.ts @@ -161,15 +161,15 @@ import { import type { SnapLocation } from './location'; import { detectSnapLocation } from './location'; import type { - GetMetadata, - GetResult, - ResolveVersion, - SnapsRegistryInfo, - SnapsRegistryRequest, - SnapsRegistryStateChangeEvent, - Update, + SnapRegistryControllerGetAction, + SnapRegistryControllerGetMetadataAction, + SnapRegistryControllerResolveVersionAction, + SnapRegistryControllerRequestUpdateAction, + SnapRegistryInfo, + SnapRegistryRequest, + SnapRegistryControllerStateChangeEvent, } from './registry'; -import { SnapsRegistryStatus } from './registry'; +import { SnapRegistryStatus } from './registry'; import { getRunnableSnaps } from './selectors'; import type { SnapControllerMethodActions } from './SnapController-method-action-types'; import { Timer } from './Timer'; @@ -535,10 +535,10 @@ export type AllowedActions = | ExecutionServiceTerminateSnapAction | UpdateCaveat | ApprovalControllerUpdateRequestStateAction - | GetResult - | GetMetadata - | Update - | ResolveVersion + | SnapRegistryControllerGetAction + | SnapRegistryControllerGetMetadataAction + | SnapRegistryControllerResolveVersionAction + | SnapRegistryControllerRequestUpdateAction | SnapInterfaceControllerCreateInterfaceAction | SnapInterfaceControllerGetInterfaceAction | SnapInterfaceControllerSetInterfaceDisplayedAction @@ -552,7 +552,7 @@ export type AllowedEvents = | SnapControllerSnapInstalledEvent | SnapControllerSnapUpdatedEvent | KeyringControllerLockEvent - | SnapsRegistryStateChangeEvent; + | SnapRegistryControllerStateChangeEvent; export type SnapControllerMessenger = Messenger< typeof controllerName, @@ -980,7 +980,7 @@ export class SnapController extends BaseController< ); this.messenger.subscribe( - 'SnapsRegistry:stateChange', + 'SnapRegistryController:stateChange', () => { this.#handleRegistryUpdate().catch((error) => { logError( @@ -1003,7 +1003,7 @@ export class SnapController extends BaseController< this.#trackSnapExport = throttleTracking( (snapId: SnapId, handler: string, success: boolean, origin: string) => { const snapMetadata = this.messenger.call( - 'SnapsRegistry:getMetadata', + 'SnapRegistryController:getMetadata', snapId, ); this.#trackEvent({ @@ -1457,7 +1457,7 @@ export class SnapController extends BaseController< */ async updateRegistry(): Promise { await this.#ensureCanUsePlatform(); - await this.messenger.call('SnapsRegistry:update'); + await this.messenger.call('SnapRegistryController:requestUpdate'); } /** @@ -1469,8 +1469,8 @@ export class SnapController extends BaseController< */ async #handleRegistryUpdate() { const blockedSnaps = await this.messenger.call( - 'SnapsRegistry:get', - Object.values(this.state.snaps).reduce( + 'SnapRegistryController:get', + Object.values(this.state.snaps).reduce( (blockListArg, snap) => { blockListArg[snap.id] = { version: snap.version, @@ -1484,7 +1484,7 @@ export class SnapController extends BaseController< await Promise.all( Object.entries(blockedSnaps).map(async ([snapId, { status, reason }]) => { - if (status === SnapsRegistryStatus.Blocked) { + if (status === SnapRegistryStatus.Blocked) { return this.#blockSnap(snapId as SnapId, reason); } @@ -1591,17 +1591,17 @@ export class SnapController extends BaseController< { platformVersion, ...snapInfo - }: SnapsRegistryInfo & { + }: SnapRegistryInfo & { permissions: SnapPermissions; platformVersion: string | undefined; }, ) { - const results = await this.messenger.call('SnapsRegistry:get', { + const results = await this.messenger.call('SnapRegistryController:get', { [snapId]: snapInfo, }); const result = results[snapId]; - if (result.status === SnapsRegistryStatus.Blocked) { + if (result.status === SnapRegistryStatus.Blocked) { throw new Error( `Cannot install version "${ snapInfo.version @@ -1618,11 +1618,11 @@ export class SnapController extends BaseController< if ( this.#featureFlags.requireAllowlist && isAllowlistingRequired && - result.status !== SnapsRegistryStatus.Verified + result.status !== SnapRegistryStatus.Verified ) { throw new Error( `Cannot install version "${snapInfo.version}" of snap "${snapId}": ${ - result.status === SnapsRegistryStatus.Unavailable + result.status === SnapRegistryStatus.Unavailable ? 'The registry is temporarily unavailable.' : 'The snap is not on the allowlist.' }`, @@ -3137,7 +3137,7 @@ export class SnapController extends BaseController< versionRange: SemVerRange, ): Promise { return await this.messenger.call( - 'SnapsRegistry:resolveVersion', + 'SnapRegistryController:resolveVersion', snapId, versionRange, ); diff --git a/packages/snaps-controllers/src/snaps/registry/SnapRegistryController-method-action-types.ts b/packages/snaps-controllers/src/snaps/registry/SnapRegistryController-method-action-types.ts new file mode 100644 index 0000000000..5fcac09d6e --- /dev/null +++ b/packages/snaps-controllers/src/snaps/registry/SnapRegistryController-method-action-types.ts @@ -0,0 +1,55 @@ +/** + * This file is auto generated by `scripts/generate-method-action-types.ts`. + * Do not edit manually. + */ + +import type { SnapRegistryController } from './SnapRegistryController'; + +/** + * Triggers an update of the registry database. + * + * If an existing update is in progress this function will await that update. + */ +export type SnapRegistryControllerRequestUpdateAction = { + type: `SnapRegistryController:requestUpdate`; + handler: SnapRegistryController['requestUpdate']; +}; + +export type SnapRegistryControllerGetAction = { + type: `SnapRegistryController:get`; + handler: SnapRegistryController['get']; +}; + +/** + * Find an allowlisted version within a specified version range. Otherwise return the version range itself. + * + * @param snapId - The ID of the snap we are trying to resolve a version for. + * @param versionRange - The version range. + * @param refetch - An optional flag used to determine if we are refetching the registry. + * @returns An allowlisted version within the specified version range if available otherwise returns the input version range. + */ +export type SnapRegistryControllerResolveVersionAction = { + type: `SnapRegistryController:resolveVersion`; + handler: SnapRegistryController['resolveVersion']; +}; + +/** + * Get metadata for the given snap ID, if available, without updating registry. + * + * @param snapId - The ID of the snap to get metadata for. + * @returns The metadata for the given snap ID, or `null` if the snap is not + * verified. + */ +export type SnapRegistryControllerGetMetadataAction = { + type: `SnapRegistryController:getMetadata`; + handler: SnapRegistryController['getMetadata']; +}; + +/** + * Union of all SnapRegistryController action types. + */ +export type SnapRegistryControllerMethodActions = + | SnapRegistryControllerRequestUpdateAction + | SnapRegistryControllerGetAction + | SnapRegistryControllerResolveVersionAction + | SnapRegistryControllerGetMetadataAction; diff --git a/packages/snaps-controllers/src/snaps/registry/json.test.ts b/packages/snaps-controllers/src/snaps/registry/SnapRegistryController.test.ts similarity index 83% rename from packages/snaps-controllers/src/snaps/registry/json.test.ts rename to packages/snaps-controllers/src/snaps/registry/SnapRegistryController.test.ts index 3aaada161b..fb77965395 100644 --- a/packages/snaps-controllers/src/snaps/registry/json.test.ts +++ b/packages/snaps-controllers/src/snaps/registry/SnapRegistryController.test.ts @@ -7,20 +7,20 @@ import { import type { SemVerRange, SemVerVersion } from '@metamask/utils'; import fetchMock from 'jest-fetch-mock'; -import type { JsonSnapsRegistryArgs } from './json'; -import { JsonSnapsRegistry } from './json'; -import { SnapsRegistryStatus } from './registry'; -import { getRestrictedSnapsRegistryControllerMessenger } from '../../test-utils'; +import type { SnapRegistryControllerArgs } from './SnapRegistryController'; +import { SnapRegistryController } from './SnapRegistryController'; +import { SnapRegistryStatus } from './types'; +import { getRestrictedSnapRegistryControllerMessenger } from '../../test-utils'; // Public key for the private key: // `0x541c6759fd86c69eceb8d792d7174623db139d81a5b560aa026afcb2dd1bb21c`. const MOCK_PUBLIC_KEY = '0x03a885324b8520fba54a173999629952cfa1f97930c20902ec389f9c32c6ffbc40'; -const getRegistry = (args?: Partial) => { - const messenger = getRestrictedSnapsRegistryControllerMessenger(); +const getRegistry = (args?: Partial) => { + const messenger = getRestrictedSnapRegistryControllerMessenger(); return { - registry: new JsonSnapsRegistry({ + registry: new SnapRegistryController({ messenger, publicKey: MOCK_PUBLIC_KEY, clientConfig: { @@ -125,7 +125,7 @@ const MOCK_COMPATIBILITY_SIGNATURE_FILE = { format: 'DER', }; -describe('JsonSnapsRegistry', () => { +describe('SnapRegistryController', () => { fetchMock.enableMocks(); afterEach(() => { @@ -138,7 +138,7 @@ describe('JsonSnapsRegistry', () => { .mockResponseOnce(JSON.stringify(MOCK_SIGNATURE_FILE)); const { messenger } = getRegistry(); - const result = await messenger.call('SnapsRegistry:get', { + const result = await messenger.call('SnapRegistryController:get', { [MOCK_SNAP_ID]: { version: '1.0.0' as SemVerVersion, checksum: DEFAULT_SNAP_SHASUM, @@ -147,7 +147,7 @@ describe('JsonSnapsRegistry', () => { expect(result).toStrictEqual({ [MOCK_SNAP_ID]: { - status: SnapsRegistryStatus.Verified, + status: SnapRegistryStatus.Verified, }, }); }); @@ -159,7 +159,7 @@ describe('JsonSnapsRegistry', () => { .mockResponseOnce(JSON.stringify(MOCK_EMPTY_SIGNATURE_FILE)); const { messenger } = getRegistry(); - const result = await messenger.call('SnapsRegistry:get', { + const result = await messenger.call('SnapRegistryController:get', { [MOCK_SNAP_ID]: { version: '1.0.0' as SemVerVersion, checksum: DEFAULT_SNAP_SHASUM, @@ -168,7 +168,7 @@ describe('JsonSnapsRegistry', () => { expect(result).toStrictEqual({ [MOCK_SNAP_ID]: { - status: SnapsRegistryStatus.Unverified, + status: SnapRegistryStatus.Unverified, }, }); }); @@ -179,7 +179,7 @@ describe('JsonSnapsRegistry', () => { .mockResponseOnce(JSON.stringify(MOCK_SIGNATURE_FILE)); const { messenger } = getRegistry(); - const result = await messenger.call('SnapsRegistry:get', { + const result = await messenger.call('SnapRegistryController:get', { [MOCK_SNAP_ID]: { version: '1.0.1' as SemVerVersion, checksum: DEFAULT_SNAP_SHASUM, @@ -188,7 +188,7 @@ describe('JsonSnapsRegistry', () => { expect(result).toStrictEqual({ [MOCK_SNAP_ID]: { - status: SnapsRegistryStatus.Unverified, + status: SnapRegistryStatus.Unverified, }, }); }); @@ -199,7 +199,7 @@ describe('JsonSnapsRegistry', () => { .mockResponseOnce(JSON.stringify(MOCK_SIGNATURE_FILE)); const { messenger } = getRegistry(); - const result = await messenger.call('SnapsRegistry:get', { + const result = await messenger.call('SnapRegistryController:get', { [MOCK_SNAP_ID]: { version: '1.0.0' as SemVerVersion, checksum: 'bar', @@ -208,7 +208,7 @@ describe('JsonSnapsRegistry', () => { expect(result).toStrictEqual({ [MOCK_SNAP_ID]: { - status: SnapsRegistryStatus.Unverified, + status: SnapRegistryStatus.Unverified, }, }); }); @@ -219,7 +219,7 @@ describe('JsonSnapsRegistry', () => { .mockResponseOnce(JSON.stringify(MOCK_SIGNATURE_FILE)); const { messenger } = getRegistry(); - const result = await messenger.call('SnapsRegistry:get', { + const result = await messenger.call('SnapRegistryController:get', { [MOCK_SNAP_ID]: { version: '1.0.0' as SemVerVersion, checksum: 'foo', @@ -228,7 +228,7 @@ describe('JsonSnapsRegistry', () => { expect(result).toStrictEqual({ [MOCK_SNAP_ID]: { - status: SnapsRegistryStatus.Blocked, + status: SnapRegistryStatus.Blocked, reason: { explanation: 'malicious' }, }, }); @@ -240,7 +240,7 @@ describe('JsonSnapsRegistry', () => { .mockResponseOnce(JSON.stringify(MOCK_SIGNATURE_FILE)); const { messenger } = getRegistry(); - const result = await messenger.call('SnapsRegistry:get', { + const result = await messenger.call('SnapRegistryController:get', { 'npm:@consensys/starknet-snap': { version: '0.1.10' as SemVerVersion, checksum: DEFAULT_SNAP_SHASUM, @@ -249,7 +249,7 @@ describe('JsonSnapsRegistry', () => { expect(result).toStrictEqual({ 'npm:@consensys/starknet-snap': { - status: SnapsRegistryStatus.Blocked, + status: SnapRegistryStatus.Blocked, reason: { explanation: 'vuln' }, }, }); @@ -267,7 +267,7 @@ describe('JsonSnapsRegistry', () => { database: { verifiedSnaps: {}, blockedSnaps: [] }, }, }); - const result = await messenger.call('SnapsRegistry:get', { + const result = await messenger.call('SnapRegistryController:get', { [MOCK_SNAP_ID]: { version: '1.0.0' as SemVerVersion, checksum: DEFAULT_SNAP_SHASUM, @@ -276,7 +276,7 @@ describe('JsonSnapsRegistry', () => { expect(result).toStrictEqual({ [MOCK_SNAP_ID]: { - status: SnapsRegistryStatus.Verified, + status: SnapRegistryStatus.Verified, }, }); }); @@ -287,7 +287,7 @@ describe('JsonSnapsRegistry', () => { .mockResponseOnce(JSON.stringify(MOCK_COMPATIBILITY_SIGNATURE_FILE)); const { messenger } = getRegistry(); - const result = await messenger.call('SnapsRegistry:get', { + const result = await messenger.call('SnapRegistryController:get', { [MOCK_SNAP_ID]: { version: '1.0.0' as SemVerVersion, checksum: DEFAULT_SNAP_SHASUM, @@ -296,7 +296,7 @@ describe('JsonSnapsRegistry', () => { expect(result).toStrictEqual({ [MOCK_SNAP_ID]: { - status: SnapsRegistryStatus.Verified, + status: SnapRegistryStatus.Verified, }, }); }); @@ -307,7 +307,7 @@ describe('JsonSnapsRegistry', () => { .mockResponseOnce(JSON.stringify(MOCK_COMPATIBILITY_SIGNATURE_FILE)); const { messenger } = getRegistry(); - const result = await messenger.call('SnapsRegistry:get', { + const result = await messenger.call('SnapRegistryController:get', { [MOCK_SNAP_ID]: { version: '1.1.0' as SemVerVersion, checksum: DEFAULT_SNAP_SHASUM, @@ -316,7 +316,7 @@ describe('JsonSnapsRegistry', () => { expect(result).toStrictEqual({ [MOCK_SNAP_ID]: { - status: SnapsRegistryStatus.Unverified, + status: SnapRegistryStatus.Unverified, }, }); }); @@ -332,7 +332,7 @@ describe('JsonSnapsRegistry', () => { }, }); - const result = await messenger.call('SnapsRegistry:get', { + const result = await messenger.call('SnapRegistryController:get', { [MOCK_SNAP_ID]: { version: '1.0.0' as SemVerVersion, checksum: DEFAULT_SNAP_SHASUM, @@ -341,7 +341,7 @@ describe('JsonSnapsRegistry', () => { expect(result).toStrictEqual({ [MOCK_SNAP_ID]: { - status: SnapsRegistryStatus.Verified, + status: SnapRegistryStatus.Verified, }, }); }); @@ -357,7 +357,7 @@ describe('JsonSnapsRegistry', () => { }, }); - const result = await messenger.call('SnapsRegistry:get', { + const result = await messenger.call('SnapRegistryController:get', { [MOCK_SNAP_ID]: { version: '1.0.1' as SemVerVersion, checksum: DEFAULT_SNAP_SHASUM, @@ -366,7 +366,7 @@ describe('JsonSnapsRegistry', () => { expect(result).toStrictEqual({ [MOCK_SNAP_ID]: { - status: SnapsRegistryStatus.Unavailable, + status: SnapRegistryStatus.Unavailable, }, }); }); @@ -376,7 +376,7 @@ describe('JsonSnapsRegistry', () => { const { messenger } = getRegistry(); - const result = await messenger.call('SnapsRegistry:get', { + const result = await messenger.call('SnapRegistryController:get', { [MOCK_SNAP_ID]: { version: '1.0.0' as SemVerVersion, checksum: DEFAULT_SNAP_SHASUM, @@ -385,7 +385,7 @@ describe('JsonSnapsRegistry', () => { expect(result).toStrictEqual({ [MOCK_SNAP_ID]: { - status: SnapsRegistryStatus.Unavailable, + status: SnapRegistryStatus.Unavailable, }, }); }); @@ -399,7 +399,7 @@ describe('JsonSnapsRegistry', () => { const { messenger } = getRegistry(); - const result = await messenger.call('SnapsRegistry:get', { + const result = await messenger.call('SnapRegistryController:get', { [MOCK_SNAP_ID]: { version: '1.0.0' as SemVerVersion, checksum: DEFAULT_SNAP_SHASUM, @@ -408,7 +408,7 @@ describe('JsonSnapsRegistry', () => { expect(result).toStrictEqual({ [MOCK_SNAP_ID]: { - status: SnapsRegistryStatus.Unavailable, + status: SnapRegistryStatus.Unavailable, }, }); }); @@ -423,7 +423,7 @@ describe('JsonSnapsRegistry', () => { '0x034ca27b046507d1a9997bddc991b56d96b93d4adac3a96dfe01ce450bfb661455', }); - const result = await messenger.call('SnapsRegistry:get', { + const result = await messenger.call('SnapRegistryController:get', { [MOCK_SNAP_ID]: { version: '1.0.0' as SemVerVersion, checksum: DEFAULT_SNAP_SHASUM, @@ -432,7 +432,7 @@ describe('JsonSnapsRegistry', () => { expect(result).toStrictEqual({ [MOCK_SNAP_ID]: { - status: SnapsRegistryStatus.Unavailable, + status: SnapRegistryStatus.Unavailable, }, }); }); @@ -445,7 +445,7 @@ describe('JsonSnapsRegistry', () => { const { messenger } = getRegistry(); const result = await messenger.call( - 'SnapsRegistry:resolveVersion', + 'SnapRegistryController:resolveVersion', MOCK_SNAP_ID, '^1.0.0' as SemVerRange, ); @@ -460,7 +460,7 @@ describe('JsonSnapsRegistry', () => { const { messenger } = getRegistry(); const result = await messenger.call( - 'SnapsRegistry:resolveVersion', + 'SnapRegistryController:resolveVersion', MOCK_SNAP_ID, '^1.0.0' as SemVerRange, ); @@ -477,7 +477,7 @@ describe('JsonSnapsRegistry', () => { clientConfig: { type: 'extension', version: '15.0.0' as SemVerVersion }, }); const result = await messenger.call( - 'SnapsRegistry:resolveVersion', + 'SnapRegistryController:resolveVersion', MOCK_SNAP_ID, '^1.0.0' as SemVerRange, ); @@ -496,7 +496,7 @@ describe('JsonSnapsRegistry', () => { const { messenger } = getRegistry(); expect( await messenger.call( - 'SnapsRegistry:resolveVersion', + 'SnapRegistryController:resolveVersion', MOCK_SNAP_ID, range, ), @@ -512,7 +512,7 @@ describe('JsonSnapsRegistry', () => { const { messenger } = getRegistry(); expect( await messenger.call( - 'SnapsRegistry:resolveVersion', + 'SnapRegistryController:resolveVersion', MOCK_SNAP_ID, range, ), @@ -532,7 +532,7 @@ describe('JsonSnapsRegistry', () => { }, }); const result = await messenger.call( - 'SnapsRegistry:resolveVersion', + 'SnapRegistryController:resolveVersion', MOCK_SNAP_ID, '^1.0.0' as SemVerRange, ); @@ -568,7 +568,7 @@ describe('JsonSnapsRegistry', () => { }, }); const result = await messenger.call( - 'SnapsRegistry:resolveVersion', + 'SnapRegistryController:resolveVersion', MOCK_SNAP_ID, '^1.0.0' as SemVerRange, ); @@ -584,8 +584,11 @@ describe('JsonSnapsRegistry', () => { .mockResponseOnce(JSON.stringify(MOCK_SIGNATURE_FILE)); const { messenger } = getRegistry(); - await messenger.call('SnapsRegistry:update'); - const result = messenger.call('SnapsRegistry:getMetadata', MOCK_SNAP_ID); + await messenger.call('SnapRegistryController:requestUpdate'); + const result = messenger.call( + 'SnapRegistryController:getMetadata', + MOCK_SNAP_ID, + ); expect(result).toStrictEqual({ name: 'Mock Snap', @@ -598,8 +601,11 @@ describe('JsonSnapsRegistry', () => { .mockResponseOnce(JSON.stringify(MOCK_SIGNATURE_FILE)); const { messenger } = getRegistry(); - await messenger.call('SnapsRegistry:update'); - const result = messenger.call('SnapsRegistry:getMetadata', 'foo'); + await messenger.call('SnapRegistryController:requestUpdate'); + const result = messenger.call( + 'SnapRegistryController:getMetadata', + 'foo', + ); expect(result).toBeNull(); }); @@ -612,7 +618,7 @@ describe('JsonSnapsRegistry', () => { .mockResponseOnce(JSON.stringify(MOCK_SIGNATURE_FILE)); const { messenger } = getRegistry(); - await messenger.call('SnapsRegistry:update'); + await messenger.call('SnapRegistryController:requestUpdate'); expect(fetchMock).toHaveBeenCalledTimes(2); }); @@ -632,7 +638,7 @@ describe('JsonSnapsRegistry', () => { databaseUnavailable: false, }, }); - await messenger.call('SnapsRegistry:update'); + await messenger.call('SnapRegistryController:requestUpdate'); expect(fetchMock).toHaveBeenCalledTimes(2); expect(spy).not.toHaveBeenCalled(); @@ -644,8 +650,8 @@ describe('JsonSnapsRegistry', () => { .mockResponseOnce(JSON.stringify(MOCK_SIGNATURE_FILE)); const { messenger } = getRegistry(); - await messenger.call('SnapsRegistry:update'); - await messenger.call('SnapsRegistry:update'); + await messenger.call('SnapRegistryController:requestUpdate'); + await messenger.call('SnapRegistryController:requestUpdate'); expect(fetchMock).toHaveBeenCalledTimes(2); }); @@ -657,8 +663,8 @@ describe('JsonSnapsRegistry', () => { const { messenger } = getRegistry(); await Promise.all([ - messenger.call('SnapsRegistry:update'), - messenger.call('SnapsRegistry:update'), + messenger.call('SnapRegistryController:requestUpdate'), + messenger.call('SnapRegistryController:requestUpdate'), ]); expect(fetchMock).toHaveBeenCalledTimes(2); @@ -670,8 +676,18 @@ describe('JsonSnapsRegistry', () => { const { registry } = getRegistry(); expect( - deriveStateFromMetadata(registry.state, registry.metadata, 'anonymous'), - ).toMatchInlineSnapshot(`{}`); + deriveStateFromMetadata( + registry.state, + registry.metadata, + 'includeInDebugSnapshot', + ), + ).toMatchInlineSnapshot(` + { + "databaseUnavailable": false, + "lastUpdated": null, + "signature": null, + } + `); }); it('includes expected state in state logs', () => { diff --git a/packages/snaps-controllers/src/snaps/registry/json.ts b/packages/snaps-controllers/src/snaps/registry/SnapRegistryController.ts similarity index 78% rename from packages/snaps-controllers/src/snaps/registry/json.ts rename to packages/snaps-controllers/src/snaps/registry/SnapRegistryController.ts index 3c5310f5a5..4944f3789b 100644 --- a/packages/snaps-controllers/src/snaps/registry/json.ts +++ b/packages/snaps-controllers/src/snaps/registry/SnapRegistryController.ts @@ -20,14 +20,14 @@ import { satisfiesVersionRange, } from '@metamask/utils'; +import type { SnapRegistryControllerMethodActions } from './SnapRegistryController-method-action-types'; import type { - SnapsRegistry, - SnapsRegistryInfo, - SnapsRegistryMetadata, - SnapsRegistryRequest, - SnapsRegistryResult, -} from './registry'; -import { SnapsRegistryStatus } from './registry'; + SnapRegistryInfo, + SnapRegistryMetadata, + SnapRegistryRequest, + SnapRegistryResult, +} from './types'; +import { SnapRegistryStatus } from './types'; const SNAP_REGISTRY_URL = 'https://acl.execution.metamask.io/latest/registry.json'; @@ -48,9 +48,9 @@ export type ClientConfig = { version: SemVerVersion; }; -export type JsonSnapsRegistryArgs = { - messenger: SnapsRegistryMessenger; - state?: SnapsRegistryState; +export type SnapRegistryControllerArgs = { + messenger: SnapRegistryControllerMessenger; + state?: SnapRegistryControllerState; fetchFunction?: typeof fetch; url?: JsonSnapsRegistryUrl; recentFetchThreshold?: number; @@ -59,59 +59,44 @@ export type JsonSnapsRegistryArgs = { clientConfig: ClientConfig; }; -export type GetResult = { - type: `${typeof controllerName}:get`; - handler: SnapsRegistry['get']; -}; - -export type ResolveVersion = { - type: `${typeof controllerName}:resolveVersion`; - handler: SnapsRegistry['resolveVersion']; -}; - -export type GetMetadata = { - type: `${typeof controllerName}:getMetadata`; - handler: SnapsRegistry['getMetadata']; -}; - -export type Update = { - type: `${typeof controllerName}:update`; - handler: SnapsRegistry['update']; -}; - -export type SnapsRegistryGetStateAction = ControllerGetStateAction< +export type SnapRegistryControllerGetStateAction = ControllerGetStateAction< typeof controllerName, - SnapsRegistryState + SnapRegistryControllerState >; -export type SnapsRegistryActions = - | SnapsRegistryGetStateAction - | GetResult - | GetMetadata - | Update - | ResolveVersion; +export type SnapRegistryControllerActions = + | SnapRegistryControllerGetStateAction + | SnapRegistryControllerMethodActions; -export type SnapsRegistryStateChangeEvent = ControllerStateChangeEvent< +export type SnapRegistryControllerStateChangeEvent = ControllerStateChangeEvent< typeof controllerName, - SnapsRegistryState + SnapRegistryControllerState >; -export type SnapsRegistryEvents = SnapsRegistryStateChangeEvent; +export type SnapRegistryControllerEvents = + SnapRegistryControllerStateChangeEvent; -export type SnapsRegistryMessenger = Messenger< - 'SnapsRegistry', - SnapsRegistryActions, - SnapsRegistryEvents +export type SnapRegistryControllerMessenger = Messenger< + typeof controllerName, + SnapRegistryControllerActions, + SnapRegistryControllerEvents >; -export type SnapsRegistryState = { +export type SnapRegistryControllerState = { database: SnapsRegistryDatabase | null; signature: string | null; lastUpdated: number | null; databaseUnavailable: boolean; }; -const controllerName = 'SnapsRegistry'; +const controllerName = 'SnapRegistryController'; + +const MESSENGER_EXPOSED_METHODS = [ + 'get', + 'getMetadata', + 'resolveVersion', + 'requestUpdate', +] as const; const defaultState = { database: null, @@ -120,10 +105,10 @@ const defaultState = { databaseUnavailable: false, }; -export class JsonSnapsRegistry extends BaseController< +export class SnapRegistryController extends BaseController< typeof controllerName, - SnapsRegistryState, - SnapsRegistryMessenger + SnapRegistryControllerState, + SnapRegistryControllerMessenger > { readonly #url: JsonSnapsRegistryUrl; @@ -151,7 +136,7 @@ export class JsonSnapsRegistry extends BaseController< fetchFunction = globalThis.fetch.bind(undefined), recentFetchThreshold = inMilliseconds(5, Duration.Minute), refetchOnAllowlistMiss = true, - }: JsonSnapsRegistryArgs) { + }: SnapRegistryControllerArgs) { super({ messenger, metadata: { @@ -194,22 +179,9 @@ export class JsonSnapsRegistry extends BaseController< this.#refetchOnAllowlistMiss = refetchOnAllowlistMiss; this.#currentUpdate = null; - this.messenger.registerActionHandler('SnapsRegistry:get', async (...args) => - this.#get(...args), - ); - - this.messenger.registerActionHandler( - 'SnapsRegistry:getMetadata', - (...args) => this.#getMetadata(...args), - ); - - this.messenger.registerActionHandler( - 'SnapsRegistry:resolveVersion', - async (...args) => this.#resolveVersion(...args), - ); - - this.messenger.registerActionHandler('SnapsRegistry:update', async () => - this.#triggerUpdate(), + this.messenger.registerMethodActionHandlers( + this, + MESSENGER_EXPOSED_METHODS, ); } @@ -225,7 +197,7 @@ export class JsonSnapsRegistry extends BaseController< * * If an existing update is in progress this function will await that update. */ - async #triggerUpdate() { + async requestUpdate() { // If an update is ongoing, wait for that. if (this.#currentUpdate) { await this.#currentUpdate; @@ -285,7 +257,7 @@ export class JsonSnapsRegistry extends BaseController< async #getDatabase(): Promise { if (this.state.database === null) { - await this.#triggerUpdate(); + await this.requestUpdate(); } return this.state.database; @@ -293,9 +265,9 @@ export class JsonSnapsRegistry extends BaseController< async #getSingle( snapId: string, - snapInfo: SnapsRegistryInfo, + snapInfo: SnapRegistryInfo, refetch = false, - ): Promise { + ): Promise { const database = await this.#getDatabase(); const blockedEntry = database?.blockedSnaps.find((blocked) => { @@ -311,7 +283,7 @@ export class JsonSnapsRegistry extends BaseController< if (blockedEntry) { return { - status: SnapsRegistryStatus.Blocked, + status: SnapRegistryStatus.Blocked, reason: blockedEntry.reason, }; } @@ -323,25 +295,25 @@ export class JsonSnapsRegistry extends BaseController< !clientRange || satisfiesVersionRange(this.#clientConfig.version, clientRange); if (version && version.checksum === snapInfo.checksum && isCompatible) { - return { status: SnapsRegistryStatus.Verified }; + return { status: SnapRegistryStatus.Verified }; } // For now, if we have an allowlist miss, we can refetch once and try again. if (this.#refetchOnAllowlistMiss && !refetch) { - await this.#triggerUpdate(); + await this.requestUpdate(); return this.#getSingle(snapId, snapInfo, true); } return { status: this.state.databaseUnavailable - ? SnapsRegistryStatus.Unavailable - : SnapsRegistryStatus.Unverified, + ? SnapRegistryStatus.Unavailable + : SnapRegistryStatus.Unverified, }; } - async #get( - snaps: SnapsRegistryRequest, - ): Promise> { + async get( + snaps: SnapRegistryRequest, + ): Promise> { return Object.entries(snaps).reduce< - Promise> + Promise> >(async (previousPromise, [snapId, snapInfo]) => { const result = await this.#getSingle(snapId, snapInfo); const acc = await previousPromise; @@ -358,7 +330,7 @@ export class JsonSnapsRegistry extends BaseController< * @param refetch - An optional flag used to determine if we are refetching the registry. * @returns An allowlisted version within the specified version range if available otherwise returns the input version range. */ - async #resolveVersion( + async resolveVersion( snapId: string, versionRange: SemVerRange, refetch = false, @@ -367,8 +339,8 @@ export class JsonSnapsRegistry extends BaseController< const versions = database?.verifiedSnaps[snapId]?.versions ?? null; if (!versions && this.#refetchOnAllowlistMiss && !refetch) { - await this.#triggerUpdate(); - return this.#resolveVersion(snapId, versionRange, true); + await this.requestUpdate(); + return this.resolveVersion(snapId, versionRange, true); } // If we cannot narrow down the version range we return the unaltered version range. @@ -394,8 +366,8 @@ export class JsonSnapsRegistry extends BaseController< const targetVersion = getTargetVersion(compatibleVersions, versionRange); if (!targetVersion && this.#refetchOnAllowlistMiss && !refetch) { - await this.#triggerUpdate(); - return this.#resolveVersion(snapId, versionRange, true); + await this.requestUpdate(); + return this.resolveVersion(snapId, versionRange, true); } // If we cannot narrow down the version range we return the unaltered version range. @@ -415,7 +387,7 @@ export class JsonSnapsRegistry extends BaseController< * @returns The metadata for the given snap ID, or `null` if the snap is not * verified. */ - #getMetadata(snapId: string): SnapsRegistryMetadata | null { + getMetadata(snapId: string): SnapRegistryMetadata | null { return this.state?.database?.verifiedSnaps[snapId]?.metadata ?? null; } diff --git a/packages/snaps-controllers/src/snaps/registry/index.ts b/packages/snaps-controllers/src/snaps/registry/index.ts index e1865b60d5..a4c0875fa5 100644 --- a/packages/snaps-controllers/src/snaps/registry/index.ts +++ b/packages/snaps-controllers/src/snaps/registry/index.ts @@ -1,2 +1,23 @@ -export * from './registry'; -export * from './json'; +export type { + SnapRegistryControllerActions, + SnapRegistryControllerEvents, + SnapRegistryControllerArgs, + SnapRegistryControllerGetStateAction, + SnapRegistryControllerMessenger, + SnapRegistryControllerState, + SnapRegistryControllerStateChangeEvent, +} from './SnapRegistryController'; +export { SnapRegistryController } from './SnapRegistryController'; +export type { + SnapRegistryControllerGetAction, + SnapRegistryControllerGetMetadataAction, + SnapRegistryControllerRequestUpdateAction, + SnapRegistryControllerResolveVersionAction, +} from './SnapRegistryController-method-action-types'; +export type { + SnapRegistryInfo, + SnapRegistryMetadata, + SnapRegistryRequest, + SnapRegistryResult, +} from './types'; +export { SnapRegistryStatus } from './types'; diff --git a/packages/snaps-controllers/src/snaps/registry/registry.ts b/packages/snaps-controllers/src/snaps/registry/registry.ts deleted file mode 100644 index 07c5009677..0000000000 --- a/packages/snaps-controllers/src/snaps/registry/registry.ts +++ /dev/null @@ -1,53 +0,0 @@ -import type { - BlockReason, - SnapsRegistryDatabase, -} from '@metamask/snaps-registry'; -import type { SnapId } from '@metamask/snaps-sdk'; -import type { SemVerRange, SemVerVersion } from '@metamask/utils'; - -export type SnapsRegistryInfo = { version: SemVerVersion; checksum: string }; -export type SnapsRegistryRequest = Record; -export type SnapsRegistryMetadata = - SnapsRegistryDatabase['verifiedSnaps'][SnapId]['metadata']; - -export enum SnapsRegistryStatus { - Unverified = 0, - Blocked = 1, - Verified = 2, - Unavailable = 3, -} - -export type SnapsRegistryResult = { - status: SnapsRegistryStatus; - reason?: BlockReason; -}; - -export type SnapsRegistry = { - get( - snaps: SnapsRegistryRequest, - ): Promise>; - - update(): Promise; - - /** - * Find an allowlisted version within a specified version range. - * - * @param snapId - The ID of the snap we are trying to resolve a version for. - * @param versionRange - The version range. - * @returns An allowlisted version within the specified version range. - * @throws If an allowlisted version does not exist within the version range. - */ - resolveVersion( - snapId: SnapId, - versionRange: SemVerRange, - ): Promise; - - /** - * Get metadata for the given snap ID. - * - * @param snapId - The ID of the snap to get metadata for. - * @returns The metadata for the given snap ID, or `null` if the snap is not - * verified. - */ - getMetadata(snapId: SnapId): SnapsRegistryMetadata | null; -}; diff --git a/packages/snaps-controllers/src/snaps/registry/types.ts b/packages/snaps-controllers/src/snaps/registry/types.ts new file mode 100644 index 0000000000..bbdcb82163 --- /dev/null +++ b/packages/snaps-controllers/src/snaps/registry/types.ts @@ -0,0 +1,23 @@ +import type { + BlockReason, + SnapsRegistryDatabase, +} from '@metamask/snaps-registry'; +import type { SnapId } from '@metamask/snaps-sdk'; +import type { SemVerVersion } from '@metamask/utils'; + +export type SnapRegistryInfo = { version: SemVerVersion; checksum: string }; +export type SnapRegistryRequest = Record; +export type SnapRegistryMetadata = + SnapsRegistryDatabase['verifiedSnaps'][SnapId]['metadata']; + +export enum SnapRegistryStatus { + Unverified = 0, + Blocked = 1, + Verified = 2, + Unavailable = 3, +} + +export type SnapRegistryResult = { + status: SnapRegistryStatus; + reason?: BlockReason; +}; diff --git a/packages/snaps-controllers/src/test-utils/controller.tsx b/packages/snaps-controllers/src/test-utils/controller.tsx index 7211352855..0fd143a10f 100644 --- a/packages/snaps-controllers/src/test-utils/controller.tsx +++ b/packages/snaps-controllers/src/test-utils/controller.tsx @@ -53,7 +53,7 @@ import type { Json } from '@metamask/utils'; import { MOCK_CRONJOB_PERMISSION } from './cronjob'; import { getNodeEES, getNodeEESMessenger } from './execution-environment'; -import { MockSnapsRegistry } from './registry'; +import { MockSnapRegistryController } from './registry'; import type { CronjobControllerMessenger } from '../cronjob/CronjobController'; import type { SnapInsightsControllerMessenger } from '../insights'; import type { @@ -62,11 +62,7 @@ import type { } from '../interface/SnapInterfaceController'; import type { MultichainRoutingServiceMessenger } from '../multichain/MultichainRoutingService'; import type { ExecutionService, ExecutionServiceMessenger } from '../services'; -import type { - SnapsRegistryActions, - SnapsRegistryEvents, - SnapsRegistryMessenger, -} from '../snaps'; +import type { SnapRegistryControllerMessenger } from '../snaps'; import { SnapController } from '../snaps'; import type { PersistedSnapControllerState, @@ -74,11 +70,7 @@ import type { SnapControllerStateChangeEvent, } from '../snaps/SnapController'; import type { KeyDerivationOptions } from '../types'; -import type { - WebSocketServiceActions, - WebSocketServiceAllowedActions, - WebSocketServiceEvents, -} from '../websocket'; +import type { WebSocketServiceMessenger } from '../websocket'; const asyncNoOp = async () => Promise.resolve(); @@ -329,10 +321,14 @@ export const MOCK_INSIGHTS_PERMISSIONS_NO_ORIGINS: Record< export type RootMessenger = Messenger< MockAnyNamespace, MessengerActions< - SnapControllerMessenger | SnapsRegistryMessenger | ExecutionServiceMessenger + | SnapControllerMessenger + | SnapRegistryControllerMessenger + | ExecutionServiceMessenger >, MessengerEvents< - SnapControllerMessenger | SnapsRegistryMessenger | ExecutionServiceMessenger + | SnapControllerMessenger + | SnapRegistryControllerMessenger + | ExecutionServiceMessenger > >; @@ -434,7 +430,7 @@ export const getRootMessenger = () => { messenger.registerActionHandler('ExecutionService:terminateSnap', asyncNoOp); // eslint-disable-next-line no-new - new MockSnapsRegistry(messenger); + new MockSnapRegistryController(messenger); messenger.registerActionHandler( 'SnapInterfaceController:createInterface', @@ -496,10 +492,10 @@ export const getSnapControllerMessenger = ( 'PermissionController:getSubjectNames', 'SubjectMetadataController:getSubjectMetadata', 'SubjectMetadataController:addSubjectMetadata', - 'SnapsRegistry:get', - 'SnapsRegistry:getMetadata', - 'SnapsRegistry:update', - 'SnapsRegistry:resolveVersion', + 'SnapRegistryController:get', + 'SnapRegistryController:getMetadata', + 'SnapRegistryController:requestUpdate', + 'SnapRegistryController:resolveVersion', 'SnapInterfaceController:createInterface', 'SnapInterfaceController:setInterfaceDisplayed', 'SnapInterfaceController:getInterface', @@ -513,7 +509,7 @@ export const getSnapControllerMessenger = ( 'ExecutionService:outboundRequest', 'ExecutionService:outboundResponse', 'KeyringController:lock', - 'SnapsRegistry:stateChange', + 'SnapRegistryController:stateChange', ], messenger: snapControllerMessenger, }); @@ -789,29 +785,32 @@ export const getRestrictedCronjobControllerMessenger = ( return cronjobControllerMessenger; }; -// Mock controller messenger for registry -export const getRootSnapsRegistryControllerMessenger = () => { - const messenger = new MockControllerMessenger< - SnapsRegistryActions, - SnapsRegistryEvents - >(); +type SnapRegistryControllerRootMessenger = Messenger< + MockAnyNamespace, + MessengerActions, + MessengerEvents +>; + +export const getRootSnapRegistryControllerMessenger = () => { + const messenger: SnapRegistryControllerRootMessenger = + new MockControllerMessenger(); jest.spyOn(messenger, 'call'); return messenger; }; -export const getRestrictedSnapsRegistryControllerMessenger = ( - messenger: ReturnType< - typeof getRootSnapsRegistryControllerMessenger - > = getRootSnapsRegistryControllerMessenger(), +export const getRestrictedSnapRegistryControllerMessenger = ( + rootMessenger: ReturnType< + typeof getRootSnapRegistryControllerMessenger + > = getRootSnapRegistryControllerMessenger(), ) => { - return new Messenger< - 'SnapsRegistry', - SnapsRegistryActions, - SnapsRegistryEvents, - any - >({ namespace: 'SnapsRegistry', parent: messenger }); + const messenger: SnapRegistryControllerMessenger = new Messenger({ + namespace: 'SnapRegistryController', + parent: rootMessenger, + }); + + return messenger; }; /** @@ -1024,12 +1023,15 @@ export const getRestrictedMultichainRoutingServiceMessenger = ( return controllerMessenger; }; -// Mock controller messenger for WebSocketService +type WebSocketServiceRootMessenger = Messenger< + MockAnyNamespace, + MessengerActions, + MessengerEvents +>; + export const getRootWebSocketServiceMessenger = () => { - const messenger = new MockControllerMessenger< - WebSocketServiceActions | WebSocketServiceAllowedActions, - WebSocketServiceEvents - >(); + const messenger: WebSocketServiceRootMessenger = + new MockControllerMessenger(); jest.spyOn(messenger, 'call'); @@ -1041,12 +1043,10 @@ export const getRestrictedWebSocketServiceMessenger = ( typeof getRootWebSocketServiceMessenger > = getRootWebSocketServiceMessenger(), ) => { - const controllerMessenger = new Messenger< - 'WebSocketService', - WebSocketServiceActions | WebSocketServiceAllowedActions, - WebSocketServiceEvents, - any - >({ namespace: 'WebSocketService', parent: messenger }); + const controllerMessenger: WebSocketServiceMessenger = new Messenger({ + namespace: 'WebSocketService', + parent: messenger, + }); messenger.delegate({ actions: ['SnapController:handleRequest'], diff --git a/packages/snaps-controllers/src/test-utils/registry.ts b/packages/snaps-controllers/src/test-utils/registry.ts index ef44f797ff..34bacae94a 100644 --- a/packages/snaps-controllers/src/test-utils/registry.ts +++ b/packages/snaps-controllers/src/test-utils/registry.ts @@ -1,31 +1,30 @@ import type { RootMessenger } from './controller'; -import type { SnapsRegistry } from '../snaps'; -import { SnapsRegistryStatus } from '../snaps'; +import { SnapRegistryStatus } from '../snaps'; -export class MockSnapsRegistry implements SnapsRegistry { +export class MockSnapRegistryController { readonly #messenger; constructor(messenger: RootMessenger) { this.#messenger = messenger; this.#messenger.registerActionHandler( - 'SnapsRegistry:get', + 'SnapRegistryController:get', this.get.bind(this), ); this.#messenger.registerActionHandler( - 'SnapsRegistry:getMetadata', + 'SnapRegistryController:getMetadata', this.getMetadata.bind(this), ); this.#messenger.registerActionHandler( - 'SnapsRegistry:resolveVersion', + 'SnapRegistryController:resolveVersion', this.resolveVersion.bind(this), ); this.#messenger.registerActionHandler( - 'SnapsRegistry:update', - this.update.bind(this), + 'SnapRegistryController:requestUpdate', + this.requestUpdate.bind(this), ); } @@ -34,7 +33,7 @@ export class MockSnapsRegistry implements SnapsRegistry { Object.keys(snaps).reduce( (acc, snapId) => ({ ...acc, - [snapId]: { status: SnapsRegistryStatus.Unverified }, + [snapId]: { status: SnapRegistryStatus.Unverified }, }), {}, ), @@ -47,9 +46,9 @@ export class MockSnapsRegistry implements SnapsRegistry { getMetadata = jest.fn().mockReturnValue(null); - update = jest.fn().mockImplementation(() => { + requestUpdate = jest.fn().mockImplementation(() => { this.#messenger.publish( - 'SnapsRegistry:stateChange', + 'SnapRegistryController:stateChange', { database: { verifiedSnaps: {}, blockedSnaps: [] }, lastUpdated: Date.now(),