diff --git a/packages/snaps-controllers/package.json b/packages/snaps-controllers/package.json index a8915aef13..7e012b40f8 100644 --- a/packages/snaps-controllers/package.json +++ b/packages/snaps-controllers/package.json @@ -94,6 +94,7 @@ "@metamask/snaps-rpc-methods": "workspace:^", "@metamask/snaps-sdk": "workspace:^", "@metamask/snaps-utils": "workspace:^", + "@metamask/storage-service": "^0.0.1", "@metamask/superstruct": "^3.2.1", "@metamask/utils": "^11.8.1", "@xstate/fsm": "^2.0.0", diff --git a/packages/snaps-controllers/src/snaps/SnapController.test.tsx b/packages/snaps-controllers/src/snaps/SnapController.test.tsx index ddabf00d96..c732de9815 100644 --- a/packages/snaps-controllers/src/snaps/SnapController.test.tsx +++ b/packages/snaps-controllers/src/snaps/SnapController.test.tsx @@ -158,7 +158,7 @@ describe('SnapController', () => { }); it('creates a snap controller and execution service', async () => { - const [snapController, service] = getSnapControllerWithEES(); + const [snapController, service] = await getSnapControllerWithEES(); expect(service).toBeDefined(); expect(snapController).toBeDefined(); snapController.destroy(); @@ -166,7 +166,7 @@ describe('SnapController', () => { }); it('adds a snap and uses its JSON-RPC api with a NodeThreadExecutionService', async () => { - const [snapController, service] = getSnapControllerWithEES( + const [snapController, service] = await getSnapControllerWithEES( getSnapControllerWithEESOptions({ state: { snaps: getPersistedSnapsState(), @@ -200,7 +200,7 @@ describe('SnapController', () => { getNodeEESMessenger(rootMessenger), ) as unknown as NodeThreadExecutionService; - const [snapController] = getSnapControllerWithEES( + const [snapController] = await getSnapControllerWithEES( getSnapControllerWithEESOptions({ rootMessenger, state: { @@ -232,7 +232,7 @@ describe('SnapController', () => { it('passes endowments to a snap when executing it', async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ environmentEndowmentPermissions: ['endowment:foo'], messenger, @@ -282,7 +282,7 @@ describe('SnapController', () => { it('errors if attempting to start a snap that was already started', async () => { const messenger = getSnapControllerMessenger(); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -317,7 +317,7 @@ describe('SnapController', () => { }, }); const { rootMessenger } = options; - const [snapController, service] = getSnapControllerWithEES(options); + const [snapController, service] = await getSnapControllerWithEES(options); const snap = snapController.getExpect(MOCK_SNAP_ID); await snapController.startSnap(snap.id); @@ -342,7 +342,7 @@ describe('SnapController', () => { }); it('adds a snap and uses its JSON-RPC API and then get stopped from idling too long', async () => { - const [snapController, service] = getSnapControllerWithEES( + const [snapController, service] = await getSnapControllerWithEES( getSnapControllerWithEESOptions({ idleTimeCheckInterval: 10, maxIdleTime: 50, @@ -377,7 +377,7 @@ describe('SnapController', () => { it('terminates a snap even if connection to worker has failed', async () => { const rootMessenger = getControllerMessenger(); - const [snapController, service] = getSnapControllerWithEES( + const [snapController, service] = await getSnapControllerWithEES( getSnapControllerWithEESOptions({ rootMessenger, idleTimeCheckInterval: 10, @@ -427,7 +427,7 @@ describe('SnapController', () => { }); it(`reads a snap's status after adding it`, async () => { - const [snapController, service] = getSnapControllerWithEES( + const [snapController, service] = await getSnapControllerWithEES( getSnapControllerWithEESOptions({ idleTimeCheckInterval: 1000, maxIdleTime: 2000, @@ -450,7 +450,7 @@ describe('SnapController', () => { }); it('adds a snap, stops it, and starts it again on-demand', async () => { - const [snapController, service] = getSnapControllerWithEES( + const [snapController, service] = await getSnapControllerWithEES( getSnapControllerWithEESOptions({ idleTimeCheckInterval: 1000, maxIdleTime: 2000, @@ -505,7 +505,7 @@ describe('SnapController', () => { }), }); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, detectSnapLocation: loopbackDetect({ manifest }), @@ -558,7 +558,7 @@ describe('SnapController', () => { manifest: manifest.result, }); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -640,7 +640,7 @@ describe('SnapController', () => { manifest: manifest.result, }); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -719,7 +719,7 @@ describe('SnapController', () => { manifest: manifest.result, }); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -770,7 +770,7 @@ describe('SnapController', () => { it('installs a snap via installSnaps', async () => { const messenger = getSnapControllerMessenger(); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, detectSnapLocation: loopbackDetect(), @@ -925,7 +925,7 @@ describe('SnapController', () => { }), }); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, detectSnapLocation: loopbackDetect({ @@ -948,7 +948,7 @@ describe('SnapController', () => { }); it('throws an error if the installation is disabled during installSnaps', async () => { - const controller = getSnapController( + const controller = await getSnapController( getSnapControllerOptions({ featureFlags: { disableSnapInstallation: true, @@ -968,7 +968,7 @@ describe('SnapController', () => { }); it('throws an error if the platform is disabled during installSnaps', async () => { - const controller = getSnapController( + const controller = await getSnapController( getSnapControllerOptions({ getFeatureFlags: () => ({ disableSnaps: true }), }), @@ -986,7 +986,7 @@ describe('SnapController', () => { }); it('throws an error if the platform is disabled during handleRequest', async () => { - const controller = getSnapController( + const controller = await getSnapController( getSnapControllerOptions({ getFeatureFlags: () => ({ disableSnaps: true }), state: getPersistedSnapsState(), @@ -1008,7 +1008,7 @@ describe('SnapController', () => { }); it('throws an error on invalid semver range during installSnaps', async () => { - const controller = getSnapController(); + const controller = await getSnapController(); await expect( controller.installSnaps(MOCK_ORIGIN, { @@ -1022,7 +1022,7 @@ describe('SnapController', () => { }); it("throws an error if semver version range doesn't match downloaded version", async () => { - const controller = getSnapController( + const controller = await getSnapController( getSnapControllerOptions({ detectSnapLocation: loopbackDetect() }), ); @@ -1041,7 +1041,7 @@ describe('SnapController', () => { const registry = new MockSnapsRegistry(); const rootMessenger = getControllerMessenger(registry); const messenger = getSnapControllerMessenger(rootMessenger); - const controller = getSnapController( + const controller = await getSnapController( getSnapControllerOptions({ featureFlags: { requireAllowlist: true }, messenger, @@ -1070,7 +1070,7 @@ describe('SnapController', () => { const registry = new MockSnapsRegistry(); const rootMessenger = getControllerMessenger(registry); const messenger = getSnapControllerMessenger(rootMessenger); - const controller = getSnapController( + const controller = await getSnapController( getSnapControllerOptions({ featureFlags: { requireAllowlist: true }, messenger, @@ -1109,7 +1109,7 @@ describe('SnapController', () => { }), }); - const controller = getSnapController( + const controller = await getSnapController( getSnapControllerOptions({ featureFlags: { requireAllowlist: true }, detectSnapLocation: loopbackDetect({ @@ -1148,7 +1148,7 @@ describe('SnapController', () => { registry.resolveVersion.mockReturnValue('1.1.0'); - const controller = getSnapController( + const controller = await getSnapController( getSnapControllerOptions({ messenger, featureFlags: { requireAllowlist: true }, @@ -1175,7 +1175,7 @@ describe('SnapController', () => { const registry = new MockSnapsRegistry(); const rootMessenger = getControllerMessenger(registry); const messenger = getSnapControllerMessenger(rootMessenger); - const controller = getSnapController( + const controller = await getSnapController( getSnapControllerOptions({ messenger, detectSnapLocation: (_location, options) => @@ -1194,7 +1194,7 @@ describe('SnapController', () => { it('reuses an already installed snap if it satisfies the requested SemVer range', async () => { const messenger = getSnapControllerMessenger(); - const controller = getSnapController( + const controller = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -1218,7 +1218,7 @@ describe('SnapController', () => { it('fails to install snap if user rejects installation', async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const controller = getSnapController( + const controller = await getSnapController( getSnapControllerOptions({ messenger, detectSnapLocation: loopbackDetect(), @@ -1287,7 +1287,7 @@ describe('SnapController', () => { it('removes a snap that errors during installation after being added', async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, detectSnapLocation: loopbackDetect(), @@ -1335,7 +1335,7 @@ describe('SnapController', () => { }); it('adds a snap, disable/enables it, and still gets a response from an RPC method', async () => { - const [snapController, service] = getSnapControllerWithEES( + const [snapController, service] = await getSnapControllerWithEES( getSnapControllerWithEESOptions({ idleTimeCheckInterval: 1000, maxRequestTime: 2000, @@ -1431,7 +1431,7 @@ describe('SnapController', () => { }, }); - const snapController = getSnapController(options); + const snapController = await getSnapController(options); const snap = snapController.getExpect(MOCK_SNAP_ID); rootMessenger.registerActionHandler( @@ -1480,7 +1480,7 @@ describe('SnapController', () => { }, }); - const snapController = getSnapController(options); + const snapController = await getSnapController(options); const snap = snapController.getExpect(MOCK_SNAP_ID); await snapController.startSnap(snap.id); @@ -1505,7 +1505,7 @@ describe('SnapController', () => { }, }); - const snapController = getSnapController(options); + const snapController = await getSnapController(options); const snap = snapController.getExpect(MOCK_SNAP_ID); rootMessenger.registerActionHandler( @@ -1600,7 +1600,7 @@ describe('SnapController', () => { getNodeEESMessenger(options.rootMessenger), setupSnapProvider, ); - const [snapController] = getSnapControllerWithEES(options, service); + const [snapController] = await getSnapControllerWithEES(options, service); const snap = snapController.getExpect(MOCK_SNAP_ID); await snapController.startSnap(snap.id); @@ -1675,7 +1675,7 @@ describe('SnapController', () => { getNodeEESMessenger(rootMessenger), setupSnapProvider, ); - const [snapController] = getSnapControllerWithEES(options, service); + const [snapController] = await getSnapControllerWithEES(options, service); const snap = snapController.getExpect(MOCK_SNAP_ID); rootMessenger.registerActionHandler( @@ -1730,7 +1730,7 @@ describe('SnapController', () => { }); const { rootMessenger } = options; - const [snapController] = getSnapControllerWithEES(options); + const [snapController] = await getSnapControllerWithEES(options); const snap = snapController.getExpect(MOCK_SNAP_ID); rootMessenger.registerActionHandler( @@ -1787,7 +1787,7 @@ describe('SnapController', () => { `, }); - const [snapController] = getSnapControllerWithEES( + const [snapController] = await getSnapControllerWithEES( getSnapControllerWithEESOptions({ detectSnapLocation: loopbackDetect({ manifest, @@ -1840,7 +1840,7 @@ describe('SnapController', () => { `, }); - const [snapController] = getSnapControllerWithEES( + const [snapController] = await getSnapControllerWithEES( getSnapControllerWithEESOptions({ detectSnapLocation: loopbackDetect({ manifest, @@ -1900,7 +1900,7 @@ describe('SnapController', () => { }); const rootMessenger = getControllerMessenger(); - const [snapController, service] = getSnapControllerWithEES( + const [snapController, service] = await getSnapControllerWithEES( getSnapControllerWithEESOptions({ maxRequestTime: 50, rootMessenger, @@ -1990,7 +1990,7 @@ describe('SnapController', () => { ), }, }); - const [snapController, service] = getSnapControllerWithEES(options); + const [snapController, service] = await getSnapControllerWithEES(options); const snap = snapController.getExpect(MOCK_SNAP_ID); @@ -2043,7 +2043,7 @@ describe('SnapController', () => { it(`shouldn't time out a long running snap on start up`, async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, maxRequestTime: 50, @@ -2090,7 +2090,7 @@ describe('SnapController', () => { const { messenger } = options; - const [snapController, service] = getSnapControllerWithEES( + const [snapController, service] = await getSnapControllerWithEES( options, new ExecutionEnvironmentStub( getNodeEESMessenger(options.rootMessenger), @@ -2159,7 +2159,7 @@ describe('SnapController', () => { .fn() .mockReturnValue(TEST_SECRET_RECOVERY_PHRASE_SEED_BYTES); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -2196,7 +2196,7 @@ describe('SnapController', () => { describe('handleRequest', () => { it('throws if the Snap is not installed', async () => { - const snapController = getSnapController(); + const snapController = await getSnapController(); await expect( snapController.handleRequest({ @@ -2225,7 +2225,7 @@ describe('SnapController', () => { async (handler) => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -2260,7 +2260,7 @@ describe('SnapController', () => { it('does not throw if the snap uses a permitted handler', async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -2294,7 +2294,7 @@ describe('SnapController', () => { it('allows MetaMask to send a JSON-RPC request', async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -2341,7 +2341,7 @@ describe('SnapController', () => { it('allows MetaMask to send a keyring request', async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -2388,7 +2388,7 @@ describe('SnapController', () => { it('allows a website origin if it is in the `allowedOrigins` list', async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -2435,7 +2435,7 @@ describe('SnapController', () => { it('allows a website origin if it is in the `allowedOrigins` list for keyring requests', async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -2482,7 +2482,7 @@ describe('SnapController', () => { it('allows a website origin if `dapps` is `true`', async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -2530,7 +2530,7 @@ describe('SnapController', () => { it('allows a Snap origin if it is in the `allowedOrigins` list', async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -2577,7 +2577,7 @@ describe('SnapController', () => { it('allows a Snap origin if `snaps` is `true`', async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -2638,7 +2638,7 @@ describe('SnapController', () => { async (value: RpcOrigins) => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -2691,7 +2691,7 @@ describe('SnapController', () => { const { promise, resolve } = createDeferredPromise(); const ensureOnboardingComplete = jest.fn().mockReturnValue(promise); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -2735,7 +2735,7 @@ describe('SnapController', () => { it('throws if the snap does not have permission to handle JSON-RPC requests from dapps', async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -2775,7 +2775,7 @@ describe('SnapController', () => { it('throws if the snap does not have permission to handle JSON-RPC requests from snaps', async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -2815,7 +2815,7 @@ describe('SnapController', () => { it('throws if the website origin is not in the `allowedOrigins` list for keyring requests', async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -2864,7 +2864,7 @@ describe('SnapController', () => { it('injects context into onUserInput', async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -2935,7 +2935,7 @@ describe('SnapController', () => { it('throws if onTransaction handler returns a phishing link', async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -2997,7 +2997,7 @@ describe('SnapController', () => { it('throws if onTransaction returns an invalid value', async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -3055,7 +3055,7 @@ describe('SnapController', () => { it("doesn't throw if onTransaction return value is valid", async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -3109,7 +3109,7 @@ describe('SnapController', () => { it('throws if onTransaction return value is an invalid id', async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -3163,7 +3163,7 @@ describe('SnapController', () => { it("doesn't throw if onTransaction return value is an id", async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -3222,7 +3222,7 @@ describe('SnapController', () => { it('throws if onSignature handler returns a phishing link', async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -3284,7 +3284,7 @@ describe('SnapController', () => { it('throws if onSignature returns an invalid value', async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -3342,7 +3342,7 @@ describe('SnapController', () => { it('throws if onSignature return value is an invalid id', async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -3396,7 +3396,7 @@ describe('SnapController', () => { it("doesn't throw if onSignature return value is valid", async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -3451,7 +3451,7 @@ describe('SnapController', () => { it(`doesn't throw if onTransaction handler returns null`, async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -3503,7 +3503,7 @@ describe('SnapController', () => { it(`doesn't throw if onSignature handler returns null`, async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -3555,7 +3555,7 @@ describe('SnapController', () => { it('throws if onHomePage handler returns a phishing link', async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -3617,7 +3617,7 @@ describe('SnapController', () => { it('throws if onHomePage return value is an invalid id', async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -3671,7 +3671,7 @@ describe('SnapController', () => { it("doesn't throw if onHomePage return value is valid", async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -3725,7 +3725,7 @@ describe('SnapController', () => { it('throws if onSettingsPage handler returns a phishing link', async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -3787,7 +3787,7 @@ describe('SnapController', () => { it('throws if onSettingsPage return value is an invalid id', async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -3841,7 +3841,7 @@ describe('SnapController', () => { it("doesn't throw if onSettingsPage return value is valid", async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -3895,7 +3895,7 @@ describe('SnapController', () => { it('throws if onNameLookup returns an invalid value', async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -3952,7 +3952,7 @@ describe('SnapController', () => { it("doesn't throw if onNameLookup return value is valid", async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -4014,7 +4014,7 @@ describe('SnapController', () => { it(`doesn't throw if onNameLookup handler returns null`, async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -4067,7 +4067,7 @@ describe('SnapController', () => { it('throws if `onAssetsLookup` handler returns an invalid response', async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -4129,7 +4129,7 @@ describe('SnapController', () => { it('filters out assets that are out of scope for `onAssetsLookup`', async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -4205,7 +4205,7 @@ describe('SnapController', () => { it('returns the value when `onAssetsLookup` returns a valid response for fungible assets', async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -4297,7 +4297,7 @@ describe('SnapController', () => { it('returns the value when `onAssetsLookup` returns a valid response for non-fungible assets', async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -4419,7 +4419,7 @@ describe('SnapController', () => { it('throws if `onAssetsConversion` handler returns an invalid response', async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -4481,7 +4481,7 @@ describe('SnapController', () => { it('filters out assets that are out of scope for `onAssetsConversion`', async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -4555,7 +4555,7 @@ describe('SnapController', () => { it('returns the value when `onAssetsConversion` returns a valid response', async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -4640,7 +4640,7 @@ describe('SnapController', () => { it('throws if `onAssetsMarketData` handler returns an invalid response', async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -4702,7 +4702,7 @@ describe('SnapController', () => { it('filters out assets that are out of scope for `onAssetsMarketData`', async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -4775,7 +4775,7 @@ describe('SnapController', () => { it('returns the value when `onAssetsMarketData` returns a valid response for fungible assets', async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -4860,7 +4860,7 @@ describe('SnapController', () => { it('returns the value when `onAssetsMarketData` returns a valid response for non-fungible assets', async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -4984,7 +4984,7 @@ describe('SnapController', () => { it('throws if `onAssetHistoricalPrice` handler returns an invalid response', async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -5046,7 +5046,7 @@ describe('SnapController', () => { it('returns the value when `onAssetHistoricalPrice` returns a valid response', async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -5123,7 +5123,7 @@ describe('SnapController', () => { it('returns the value when `onClientRequest` returns a valid response', async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -5166,7 +5166,7 @@ describe('SnapController', () => { it('throws if the origin is not "metamask"', async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -5215,7 +5215,7 @@ describe('SnapController', () => { snaps: getPersistedSnapsState(), }, }); - const [snapController, service] = getSnapControllerWithEES(options); + const [snapController, service] = await getSnapControllerWithEES(options); rootMessenger.registerActionHandler( 'PermissionController:hasPermission', @@ -5259,7 +5259,7 @@ describe('SnapController', () => { it('handlers throw if the request has an invalid "jsonrpc" property', async () => { const fakeSnap = getPersistedSnapObject({ status: SnapStatus.Running }); const snapId = fakeSnap.id; - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ state: { snaps: { @@ -5292,7 +5292,7 @@ describe('SnapController', () => { it('handlers throw if the request is not valid JSON', async () => { const fakeSnap = getPersistedSnapObject({ status: SnapStatus.Running }); const snapId = fakeSnap.id; - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ state: { snaps: { @@ -5331,7 +5331,7 @@ describe('SnapController', () => { `, }); - const [snapController, service] = getSnapControllerWithEES( + const [snapController, service] = await getSnapControllerWithEES( getSnapControllerWithEESOptions({ detectSnapLocation: loopbackDetect({ manifest, @@ -5388,7 +5388,7 @@ describe('SnapController', () => { `, }); - const [snapController, service] = getSnapControllerWithEES( + const [snapController, service] = await getSnapControllerWithEES( getSnapControllerWithEESOptions({ idleTimeCheckInterval: 10, maxIdleTime: 50, @@ -5434,7 +5434,7 @@ describe('SnapController', () => { const snapObject = getPersistedSnapObject(); const truncatedSnap = getTruncatedSnap(); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -5470,7 +5470,7 @@ describe('SnapController', () => { shouldAlwaysReload: true, }); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -5619,7 +5619,7 @@ describe('SnapController', () => { .mockImplementationOnce(async () => Promise.resolve(manifest)) .mockImplementationOnce(async () => Promise.resolve(newManifest)); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, detectSnapLocation: loopbackDetect(location), @@ -5851,7 +5851,7 @@ describe('SnapController', () => { shouldAlwaysReload: true, }); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -5927,7 +5927,7 @@ describe('SnapController', () => { shouldAlwaysReload: true, }); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -5973,7 +5973,7 @@ describe('SnapController', () => { }), }); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, detectSnapLocation: loopbackDetect({ manifest }), @@ -6030,7 +6030,7 @@ describe('SnapController', () => { const snapId = `${MOCK_SNAP_ID}_foo`; - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, detectSnapLocation: loopbackDetect({ manifest }), @@ -6108,7 +6108,9 @@ describe('SnapController', () => { preinstalledSnaps, rootMessenger, }); - const [snapController] = getSnapControllerWithEES(snapControllerOptions); + const [snapController] = await getSnapControllerWithEES( + snapControllerOptions, + ); expect(snapControllerOptions.messenger.call).toHaveBeenCalledWith( 'PermissionController:grantPermissions', @@ -6218,7 +6220,9 @@ describe('SnapController', () => { preinstalledSnaps, rootMessenger, }); - const [snapController] = getSnapControllerWithEES(snapControllerOptions); + const [snapController] = await getSnapControllerWithEES( + snapControllerOptions, + ); expect(snapControllerOptions.messenger.call).not.toHaveBeenCalledWith( 'PermissionController:revokePermissions', @@ -6284,7 +6288,9 @@ describe('SnapController', () => { preinstalledSnaps, rootMessenger, }); - const [snapController] = getSnapControllerWithEES(snapControllerOptions); + const [snapController] = await getSnapControllerWithEES( + snapControllerOptions, + ); const approvedPermissions = { [WALLET_SNAP_PERMISSION_KEY]: { @@ -6359,7 +6365,9 @@ describe('SnapController', () => { preinstalledSnaps, rootMessenger, }); - const [snapController] = getSnapControllerWithEES(snapControllerOptions); + const [snapController] = await getSnapControllerWithEES( + snapControllerOptions, + ); expect(snapControllerOptions.messenger.call).toHaveBeenCalledWith( 'PermissionController:grantPermissions', @@ -6456,7 +6464,9 @@ describe('SnapController', () => { ), }, }); - const [snapController] = getSnapControllerWithEES(snapControllerOptions); + const [snapController] = await getSnapControllerWithEES( + snapControllerOptions, + ); expect(snapControllerOptions.messenger.call).toHaveBeenCalledWith( 'PermissionController:revokePermissions', @@ -6527,7 +6537,9 @@ describe('SnapController', () => { snaps: getPersistedSnapsState(), }, }); - const [snapController] = getSnapControllerWithEES(snapControllerOptions); + const [snapController] = await getSnapControllerWithEES( + snapControllerOptions, + ); expect(snapControllerOptions.messenger.call).toHaveBeenCalledTimes(0); @@ -6590,7 +6602,9 @@ describe('SnapController', () => { preinstalledSnaps, rootMessenger, }); - const [snapController] = getSnapControllerWithEES(snapControllerOptions); + const [snapController] = await getSnapControllerWithEES( + snapControllerOptions, + ); expect(snapControllerOptions.messenger.call).toHaveBeenCalledWith( 'PermissionController:grantPermissions', @@ -6688,7 +6702,9 @@ describe('SnapController', () => { rootMessenger, detectSnapLocation, }); - const [snapController] = getSnapControllerWithEES(snapControllerOptions); + const [snapController] = await getSnapControllerWithEES( + snapControllerOptions, + ); await expect( snapController.installSnaps(MOCK_ORIGIN, { @@ -6744,7 +6760,9 @@ describe('SnapController', () => { preinstalledSnaps, rootMessenger, }); - const [snapController] = getSnapControllerWithEES(snapControllerOptions); + const [snapController] = await getSnapControllerWithEES( + snapControllerOptions, + ); expect(snapController.get(MOCK_SNAP_ID)?.hidden).toBe(true); @@ -6796,7 +6814,9 @@ describe('SnapController', () => { preinstalledSnaps, rootMessenger, }); - const [snapController] = getSnapControllerWithEES(snapControllerOptions); + const [snapController] = await getSnapControllerWithEES( + snapControllerOptions, + ); expect(snapController.get(MOCK_SNAP_ID)?.hideSnapBranding).toBe(true); @@ -6836,7 +6856,9 @@ describe('SnapController', () => { preinstalledSnaps, rootMessenger, }); - const [snapController] = getSnapControllerWithEES(snapControllerOptions); + const [snapController] = await getSnapControllerWithEES( + snapControllerOptions, + ); expect(log).toHaveBeenCalledWith( 'The permissions for "npm:@metamask/example-snap" were out of sync and have been automatically restored. If you see this message, please file a bug report.', @@ -6924,7 +6946,9 @@ describe('SnapController', () => { preinstalledSnaps, rootMessenger, }); - const [snapController] = getSnapControllerWithEES(snapControllerOptions); + const [snapController] = await getSnapControllerWithEES( + snapControllerOptions, + ); expect(log).toHaveBeenCalledWith( 'The permissions for "npm:@metamask/example-snap" were out of sync and have been automatically restored. If you see this message, please file a bug report.', @@ -7013,7 +7037,7 @@ describe('SnapController', () => { ]; const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, preinstalledSnaps }), ); @@ -7071,7 +7095,7 @@ describe('SnapController', () => { ]; const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, preinstalledSnaps, @@ -7107,7 +7131,7 @@ describe('SnapController', () => { const manifest = getSnapManifest(); const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, detectSnapLocation: loopbackDetect({ manifest }), @@ -7241,7 +7265,7 @@ describe('SnapController', () => { }); const messenger = getSnapControllerMessenger(); - const controller = getSnapController( + const controller = await getSnapController( getSnapControllerOptions({ messenger, detectSnapLocation: loopbackDetect({ @@ -7273,7 +7297,7 @@ describe('SnapController', () => { }); const messenger = getSnapControllerMessenger(); - const controller = getSnapController( + const controller = await getSnapController( getSnapControllerOptions({ messenger, detectSnapLocation: loopbackDetect({ @@ -7302,7 +7326,7 @@ describe('SnapController', () => { }); const messenger = getSnapControllerMessenger(); - const controller = getSnapController( + const controller = await getSnapController( getSnapControllerOptions({ messenger, detectSnapLocation: loopbackDetect({ @@ -7331,7 +7355,7 @@ describe('SnapController', () => { }); const messenger = getSnapControllerMessenger(); - const controller = getSnapController( + const controller = await getSnapController( getSnapControllerOptions({ messenger, detectSnapLocation: loopbackDetect({ @@ -7363,7 +7387,7 @@ describe('SnapController', () => { }); const messenger = getSnapControllerMessenger(); - const controller = getSnapController( + const controller = await getSnapController( getSnapControllerOptions({ messenger, featureFlags: { @@ -7405,7 +7429,7 @@ describe('SnapController', () => { }); const messenger = getSnapControllerMessenger(); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, detectSnapLocation: loopbackDetect({ @@ -7526,7 +7550,7 @@ describe('SnapController', () => { }); const messenger = getSnapControllerMessenger(); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, detectSnapLocation: loopbackDetect({ @@ -7630,7 +7654,7 @@ describe('SnapController', () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -7710,7 +7734,7 @@ describe('SnapController', () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -7767,7 +7791,7 @@ describe('SnapController', () => { it('returns an error on invalid snap id', async () => { const snapId = 'foo'; const messenger = getSnapControllerMessenger(); - const controller = getSnapController( + const controller = await getSnapController( getSnapControllerOptions({ messenger }), ); await expect( @@ -7809,7 +7833,7 @@ describe('SnapController', () => { }), ); - const controller = getSnapController( + const controller = await getSnapController( getSnapControllerOptions({ messenger, detectSnapLocation: detectLocationMock, @@ -7979,7 +8003,7 @@ describe('SnapController', () => { manifest: manifest.result, }); const messenger = getSnapControllerMessenger(); - const controller = getSnapController( + const controller = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -8029,7 +8053,7 @@ describe('SnapController', () => { Promise.reject(new Error('foo')), ); const detect = loopbackDetect(location); - const controller = getSnapController( + const controller = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -8164,7 +8188,7 @@ describe('SnapController', () => { const { messenger } = options; - const [controller, service] = getSnapControllerWithEES(options); + const [controller, service] = await getSnapControllerWithEES(options); await controller.installSnaps(MOCK_ORIGIN, { [snapId1]: {} }); await controller.installSnaps(MOCK_ORIGIN, { [snapId2]: {} }); @@ -8265,7 +8289,7 @@ describe('SnapController', () => { detectSnapLocation: detect, }); const { messenger } = options; - const [controller, service] = getSnapControllerWithEES(options); + const [controller, service] = await getSnapControllerWithEES(options); const listener = jest.fn(); messenger.subscribe('SnapController:snapRolledback' as any, listener); @@ -8316,7 +8340,7 @@ describe('SnapController', () => { }), }); - const controller = getSnapController( + const controller = await getSnapController( getSnapControllerOptions({ detectSnapLocation: loopbackDetect({ manifest, @@ -8344,7 +8368,7 @@ describe('SnapController', () => { localizationFiles: [getMockLocalizationFile()], }); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, detectSnapLocation: loopbackDetect({ @@ -8392,7 +8416,7 @@ describe('SnapController', () => { localizationFiles: [getMockLocalizationFile({ messages: {} })], }); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, detectSnapLocation: loopbackDetect({ @@ -8415,7 +8439,7 @@ describe('SnapController', () => { it('installs a local Snap as preinstalled Snap when `forcePreinstalledSnaps` is enabled', async () => { const messenger = getSnapControllerMessenger(); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, detectSnapLocation: loopbackDetect(), @@ -8455,7 +8479,7 @@ describe('SnapController', () => { }); const messenger = getSnapControllerMessenger(); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, detectSnapLocation: loopbackDetect(location), @@ -8497,7 +8521,7 @@ describe('SnapController', () => { sourceCode: 'a'.repeat(64_000_001), }); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, detectSnapLocation: loopbackDetect({ @@ -8529,7 +8553,7 @@ describe('SnapController', () => { manifest: manifest.result, }); const messenger = getSnapControllerMessenger(); - const controller = getSnapController( + const controller = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -8571,7 +8595,7 @@ describe('SnapController', () => { const detectSnapLocation = loopbackDetect({ manifest: manifest.result, }); - const controller = getSnapController( + const controller = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -8604,7 +8628,7 @@ describe('SnapController', () => { manifest: manifest.result, }); const messenger = getSnapControllerMessenger(); - const controller = getSnapController( + const controller = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -8666,7 +8690,7 @@ describe('SnapController', () => { }), ); const messenger = getSnapControllerMessenger(); - const controller = getSnapController( + const controller = await getSnapController( getSnapControllerOptions({ messenger, detectSnapLocation, @@ -8850,7 +8874,7 @@ describe('SnapController', () => { }), ); - const controller = getSnapController( + const controller = await getSnapController( getSnapControllerOptions({ messenger, detectSnapLocation, @@ -8966,7 +8990,7 @@ describe('SnapController', () => { }), ); - const controller = getSnapController( + const controller = await getSnapController( getSnapControllerOptions({ messenger, detectSnapLocation, @@ -9055,7 +9079,7 @@ describe('SnapController', () => { }), ); const messenger = getSnapControllerMessenger(); - const controller = getSnapController( + const controller = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -9104,7 +9128,7 @@ describe('SnapController', () => { manifest: manifest.result, }); const messenger = getSnapControllerMessenger(); - const controller = getSnapController( + const controller = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -9235,7 +9259,7 @@ describe('SnapController', () => { const detectSnapLocation = loopbackDetect({ manifest: manifest.result, }); - const controller = getSnapController( + const controller = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -9434,7 +9458,7 @@ describe('SnapController', () => { }), ); - const controller = getSnapController( + const controller = await getSnapController( getSnapControllerOptions({ messenger, detectSnapLocation: detect }), ); @@ -9650,7 +9674,7 @@ describe('SnapController', () => { }), }); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -9797,7 +9821,7 @@ describe('SnapController', () => { ); /* eslint-enable @typescript-eslint/naming-convention */ - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, detectSnapLocation: detect, @@ -9862,7 +9886,7 @@ describe('SnapController', () => { manifest: manifest.result, }); const messenger = getSnapControllerMessenger(); - const controller = getSnapController( + const controller = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -9886,7 +9910,7 @@ describe('SnapController', () => { describe('removeSnap', () => { it('will remove the "wallet_snap" permission from a subject that no longer has any permitted snaps', async () => { const messenger = getSnapControllerMessenger(); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -9934,7 +9958,7 @@ describe('SnapController', () => { it('will update the "wallet_snap" permission from a subject that has one or more permitted snaps', async () => { const messenger = getSnapControllerMessenger(); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -9989,7 +10013,7 @@ describe('SnapController', () => { it("will skip subjects that don't have the snap permission", async () => { const messenger = getSnapControllerMessenger(); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -10047,7 +10071,7 @@ describe('SnapController', () => { it('removes snap state', async () => { const messenger = getSnapControllerMessenger(); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -10071,9 +10095,9 @@ describe('SnapController', () => { }); describe('enableSnap', () => { - it('enables a disabled snap', () => { + it('enables a disabled snap', async () => { const messenger = getSnapControllerMessenger(); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ state: { snaps: getPersistedSnapsState( @@ -10096,8 +10120,8 @@ describe('SnapController', () => { snapController.destroy(); }); - it('throws an error if the specified snap does not exist', () => { - const snapController = getSnapController(); + it('throws an error if the specified snap does not exist', async () => { + const snapController = await getSnapController(); expect(() => snapController.enableSnap(MOCK_SNAP_ID)).toThrow( `Snap "${MOCK_SNAP_ID}" not found.`, ); @@ -10105,8 +10129,8 @@ describe('SnapController', () => { snapController.destroy(); }); - it('throws an error if the specified snap is blocked', () => { - const snapController = getSnapController( + it('throws an error if the specified snap is blocked', async () => { + const snapController = await getSnapController( getSnapControllerOptions({ state: { snaps: getPersistedSnapsState( @@ -10127,7 +10151,7 @@ describe('SnapController', () => { describe('disableSnap', () => { it('disables a snap', async () => { const messenger = getSnapControllerMessenger(); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ state: { snaps: getPersistedSnapsState(), @@ -10149,7 +10173,7 @@ describe('SnapController', () => { }); it('stops a running snap when disabling it', async () => { - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ state: { snaps: getPersistedSnapsState(), @@ -10170,7 +10194,7 @@ describe('SnapController', () => { }); it('throws an error if the specified snap does not exist', async () => { - const snapController = getSnapController(); + const snapController = await getSnapController(); await expect(snapController.disableSnap(MOCK_SNAP_ID)).rejects.toThrow( `Snap "${MOCK_SNAP_ID}" not found.`, ); @@ -10185,7 +10209,7 @@ describe('SnapController', () => { const rootMessenger = getControllerMessenger(registry); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -10216,7 +10240,7 @@ describe('SnapController', () => { origin: 'bar.io', }); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -10281,7 +10305,7 @@ describe('SnapController', () => { origin: MOCK_ORIGIN, }); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -10324,7 +10348,7 @@ describe('SnapController', () => { origin: 'bar.io', }); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -10379,7 +10403,7 @@ describe('SnapController', () => { origin: MOCK_ORIGIN, }); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -10423,7 +10447,7 @@ describe('SnapController', () => { origin: MOCK_ORIGIN, }); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -10497,7 +10521,7 @@ describe('SnapController', () => { ), }); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -10560,7 +10584,7 @@ describe('SnapController', () => { registry.resolveVersion.mockResolvedValue(updateVersion); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -10605,7 +10629,7 @@ describe('SnapController', () => { }, }, }); - const [snapController] = getSnapControllerWithEES(options); + const [snapController] = await getSnapControllerWithEES(options); const { messenger } = options; @@ -10707,7 +10731,7 @@ describe('SnapController', () => { ); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -10818,7 +10842,7 @@ describe('SnapController', () => { }, ); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -10893,7 +10917,7 @@ describe('SnapController', () => { }, ); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -10914,10 +10938,10 @@ describe('SnapController', () => { }); describe('SnapController:get', () => { - it('gets a snap', () => { + it('gets a snap', async () => { const messenger = getSnapControllerMessenger(); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -10940,7 +10964,7 @@ describe('SnapController', () => { it('handles a snap RPC request', async () => { const messenger = getSnapControllerMessenger(); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -10973,7 +10997,7 @@ describe('SnapController', () => { getNodeEESMessenger(rootMessenger), ) as unknown as NodeThreadExecutionService; - const [snapController] = getSnapControllerWithEES( + const [snapController] = await getSnapControllerWithEES( getSnapControllerWithEESOptions({ rootMessenger, trackEvent: mockTrackEvent, @@ -11039,7 +11063,7 @@ describe('SnapController', () => { getNodeEESMessenger(rootMessenger), ) as unknown as NodeThreadExecutionService; - const [snapController] = getSnapControllerWithEES( + const [snapController] = await getSnapControllerWithEES( getSnapControllerWithEESOptions({ environmentEndowmentPermissions: ['endowment:cronjob'], rootMessenger, @@ -11081,7 +11105,7 @@ describe('SnapController', () => { getNodeEESMessenger(rootMessenger), ) as unknown as NodeThreadExecutionService; - const [snapController] = getSnapControllerWithEES( + const [snapController] = await getSnapControllerWithEES( getSnapControllerWithEESOptions({ rootMessenger, trackEvent: mockTrackEvent, @@ -11123,7 +11147,7 @@ describe('SnapController', () => { getNodeEESMessenger(rootMessenger), ) as unknown as NodeThreadExecutionService; - const [snapController] = getSnapControllerWithEES( + const [snapController] = await getSnapControllerWithEES( getSnapControllerWithEESOptions({ rootMessenger, trackEvent: mockTrackEvent, @@ -11159,7 +11183,7 @@ describe('SnapController', () => { it('handles a transaction insight request', async () => { const messenger = getSnapControllerMessenger(); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -11188,7 +11212,7 @@ describe('SnapController', () => { it('handles a signature insight request', async () => { const messenger = getSnapControllerMessenger(); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -11217,7 +11241,7 @@ describe('SnapController', () => { it('handles a name lookup request', async () => { const messenger = getSnapControllerMessenger(); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -11266,7 +11290,7 @@ describe('SnapController', () => { DEFAULT_ENCRYPTION_KEY_DERIVATION_OPTIONS, ); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -11309,7 +11333,7 @@ describe('SnapController', () => { }, ); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -11363,7 +11387,7 @@ describe('SnapController', () => { const state = { foo: 'bar' }; - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -11440,7 +11464,7 @@ describe('SnapController', () => { ), ); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -11464,7 +11488,7 @@ describe('SnapController', () => { it('throws an error if the state is corrupt', async () => { const messenger = getSnapControllerMessenger(); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -11494,7 +11518,7 @@ describe('SnapController', () => { const state = { foo: 'bar' }; - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -11524,7 +11548,7 @@ describe('SnapController', () => { it(`returns null if the Snap has no state yet`, async () => { const messenger = getSnapControllerMessenger(); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -11552,10 +11576,10 @@ describe('SnapController', () => { }); describe('SnapController:has', () => { - it('checks if a snap exists in state', () => { + it('checks if a snap exists in state', async () => { const messenger = getSnapControllerMessenger(); const id = 'npm:fooSnap' as SnapId; - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -11595,7 +11619,7 @@ describe('SnapController', () => { it(`updates the snap's state`, async () => { const messenger = getSnapControllerMessenger(); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -11636,7 +11660,7 @@ describe('SnapController', () => { it(`updates the snap's unencrypted state`, async () => { const messenger = getSnapControllerMessenger(); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -11677,7 +11701,7 @@ describe('SnapController', () => { hmac(sha512, key, data), ); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -11713,7 +11737,7 @@ describe('SnapController', () => { const encryptor = getSnapControllerEncryptor(); const encryptWithKey = jest.spyOn(encryptor, 'encryptWithKey'); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -11781,7 +11805,7 @@ describe('SnapController', () => { const messenger = getSnapControllerMessenger(); const errorValue = new Error('Failed to persist state.'); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -11826,7 +11850,7 @@ describe('SnapController', () => { it('clears the state of a snap', async () => { const messenger = getSnapControllerMessenger(); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -11854,7 +11878,7 @@ describe('SnapController', () => { it('clears the unencrypted state of a snap', async () => { const messenger = getSnapControllerMessenger(); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -11883,7 +11907,7 @@ describe('SnapController', () => { const messenger = getSnapControllerMessenger(); const errorValue = new Error('Failed to persist state.'); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -11923,7 +11947,7 @@ describe('SnapController', () => { describe('SnapController:updateRegistry', () => { it('calls SnapController.updateRegistry()', async () => { const messenger = getSnapControllerMessenger(); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, }), @@ -11941,7 +11965,7 @@ describe('SnapController', () => { }); describe('SnapController:enable', () => { - it('calls SnapController.enableSnap()', () => { + it('calls SnapController.enableSnap()', async () => { const messenger = getSnapControllerMessenger(); const mockSnap = getMockSnapData({ id: 'npm:example' as SnapId, @@ -11949,7 +11973,7 @@ describe('SnapController', () => { enabled: false, }); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -11974,7 +11998,7 @@ describe('SnapController', () => { enabled: true, }); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -11999,7 +12023,7 @@ describe('SnapController', () => { enabled: true, }); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -12024,7 +12048,7 @@ describe('SnapController', () => { origin: MOCK_ORIGIN, }); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -12049,14 +12073,14 @@ describe('SnapController', () => { }); describe('SnapController:getAllSnaps', () => { - it('calls SnapController.getAllSnaps()', () => { + it('calls SnapController.getAllSnaps()', async () => { const messenger = getSnapControllerMessenger(); const mockSnap = getMockSnapData({ id: MOCK_SNAP_ID, origin: MOCK_ORIGIN, }); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -12073,7 +12097,7 @@ describe('SnapController', () => { }); describe('SnapController:getRunnableSnaps', () => { - it('calls SnapController.getRunnableSnaps()', () => { + it('calls SnapController.getRunnableSnaps()', async () => { const messenger = getSnapControllerMessenger(); const mockSnap = getMockSnapData({ id: MOCK_SNAP_ID, @@ -12085,7 +12109,7 @@ describe('SnapController', () => { enabled: false, }); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -12107,7 +12131,7 @@ describe('SnapController', () => { describe('SnapController:install', () => { it('calls SnapController.installSnaps()', async () => { const messenger = getSnapControllerMessenger(); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, }), @@ -12127,7 +12151,7 @@ describe('SnapController', () => { }); describe('SnapController:disconnectOrigin', () => { - it('calls SnapController.removeSnapFromSubject()', () => { + it('calls SnapController.removeSnapFromSubject()', async () => { const messenger = getSnapControllerMessenger(); const permittedSnaps = [ MOCK_SNAP_ID, @@ -12141,7 +12165,7 @@ describe('SnapController', () => { getPersistedSnapObject({ id: snapId as SnapId }), ); const snaps = getPersistedSnapsState(...snapObjects); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -12186,9 +12210,9 @@ describe('SnapController', () => { }); describe('SnapController:revokeDynamicPermissions', () => { - it('calls PermissionController:revokePermissions', () => { + it('calls PermissionController:revokePermissions', async () => { const messenger = getSnapControllerMessenger(); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, }), @@ -12208,9 +12232,9 @@ describe('SnapController', () => { snapController.destroy(); }); - it('throws if input permission is not a dynamic permission', () => { + it('throws if input permission is not a dynamic permission', async () => { const messenger = getSnapControllerMessenger(); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, }), @@ -12242,7 +12266,7 @@ describe('SnapController', () => { const messenger = getSnapControllerMessenger(); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, detectSnapLocation: loopbackDetect({ @@ -12295,7 +12319,7 @@ describe('SnapController', () => { const messenger = getSnapControllerMessenger(); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, detectSnapLocation: loopbackDetect({ @@ -12327,7 +12351,7 @@ describe('SnapController', () => { it('returns null if file does not exist', async () => { const messenger = getSnapControllerMessenger(); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, detectSnapLocation: loopbackDetect(), @@ -12363,7 +12387,7 @@ describe('SnapController', () => { const messenger = getSnapControllerMessenger(); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, detectSnapLocation: loopbackDetect({ @@ -12408,7 +12432,7 @@ describe('SnapController', () => { it('calls the `onInstall` lifecycle hook', async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -12468,7 +12492,7 @@ describe('SnapController', () => { it('does not call the `onInstall` lifecycle hook if the snap does not have the `endowment:lifecycle-hooks` permission', async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -12513,7 +12537,7 @@ describe('SnapController', () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -12550,7 +12574,7 @@ describe('SnapController', () => { it('calls the `onUpdate` lifecycle hook', async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -12611,7 +12635,7 @@ describe('SnapController', () => { it('does not call the `onUpdate` lifecycle hook if the snap does not have the `endowment:lifecycle-hooks` permission', async () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -12656,7 +12680,7 @@ describe('SnapController', () => { const rootMessenger = getControllerMessenger(); const messenger = getSnapControllerMessenger(rootMessenger); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -12701,7 +12725,7 @@ describe('SnapController', () => { origin: MOCK_ORIGIN, }); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -12732,7 +12756,7 @@ describe('SnapController', () => { platformVersion: '6.0.0' as SemVerVersion, }); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -12759,7 +12783,7 @@ describe('SnapController', () => { platformVersion: '6.0.0' as SemVerVersion, }); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -12786,7 +12810,7 @@ describe('SnapController', () => { platformVersion: '6.0.0' as SemVerVersion, }); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -12812,7 +12836,7 @@ describe('SnapController', () => { const manifest = getSnapManifest(); delete manifest.platformVersion; - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -12864,7 +12888,7 @@ describe('SnapController', () => { }, }); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -12950,7 +12974,7 @@ describe('SnapController', () => { }, }); - const snapController = getSnapController( + const snapController = await getSnapController( getSnapControllerOptions({ messenger, state: { @@ -13008,8 +13032,8 @@ describe('SnapController', () => { }); describe('metadata', () => { - it('includes expected state in debug snapshots', () => { - const controller = getSnapController(); + it('includes expected state in debug snapshots', async () => { + const controller = await getSnapController(); expect( deriveStateFromMetadata( @@ -13025,8 +13049,8 @@ describe('SnapController', () => { }); describe('includeInStateLogs', () => { - it('includes expected state in state logs', () => { - const controller = getSnapController(); + it('includes expected state in state logs', async () => { + const controller = await getSnapController(); expect( deriveStateFromMetadata( @@ -13042,13 +13066,13 @@ describe('SnapController', () => { `); }); - it('strips out large state properties', () => { + it('strips out large state properties', async () => { const id = 'npm:foo' as SnapId; const auxiliaryFile = new VirtualFile({ path: 'src/foo.json', value: stringToBytes('{ "foo" : "bar" }'), }); - const controller = getSnapController( + const controller = await getSnapController( getSnapControllerOptions({ state: { snaps: getPersistedSnapsState( @@ -13110,8 +13134,8 @@ describe('SnapController', () => { }); describe('persist', () => { - it('persists expected state', () => { - const controller = getSnapController(); + it('persists expected state', async () => { + const controller = await getSnapController(); expect( deriveStateFromMetadata( @@ -13130,7 +13154,7 @@ describe('SnapController', () => { it('can rehydrate state', async () => { const id = 'npm:foo' as SnapId; - const firstSnapController = getSnapController( + const firstSnapController = await getSnapController( getSnapControllerOptions({ state: { snaps: getPersistedSnapsState( @@ -13153,7 +13177,7 @@ describe('SnapController', () => { ); // create a new controller - const secondSnapController = getSnapController( + const secondSnapController = await getSnapController( getSnapControllerOptions({ state: persistedState as PersistedSnapControllerState, }), @@ -13169,7 +13193,7 @@ describe('SnapController', () => { }); it('does not persist snaps in the installing state', async () => { - const firstSnapController = getSnapController( + const firstSnapController = await getSnapController( getSnapControllerOptions({ state: { snaps: getPersistedSnapsState( @@ -13193,7 +13217,7 @@ describe('SnapController', () => { ); // create a new controller - const secondSnapController = getSnapController( + const secondSnapController = await getSnapController( getSnapControllerOptions({ state: persistedState as PersistedSnapControllerState, }), @@ -13205,8 +13229,8 @@ describe('SnapController', () => { }); }); - it('exposes expected state to UI', () => { - const controller = getSnapController(); + it('exposes expected state to UI', async () => { + const controller = await getSnapController(); expect( deriveStateFromMetadata( diff --git a/packages/snaps-controllers/src/snaps/SnapController.ts b/packages/snaps-controllers/src/snaps/SnapController.ts index eb16034a41..ed0c1f0deb 100644 --- a/packages/snaps-controllers/src/snaps/SnapController.ts +++ b/packages/snaps-controllers/src/snaps/SnapController.ts @@ -77,6 +77,8 @@ import type { StatusContext, StatusEvents, StatusStates, + StorageServiceSnapData, + StoredSnap, TruncatedSnap, TruncatedSnapFields, } from '@metamask/snaps-utils'; @@ -111,6 +113,12 @@ import { OnAssetsConversionResponseStruct, OnAssetsMarketDataResponseStruct, } from '@metamask/snaps-utils'; +import type { + StorageServiceGetItemAction, + StorageServiceSetItemAction, + StorageServiceRemoveItemAction, + StorageServiceClearAction, +} from '@metamask/storage-service'; import type { Json, NonEmptyArray, @@ -232,7 +240,7 @@ export type SnapRuntimeData = { /** * A promise that resolves when the Snap has finished installing */ - installPromise: null | Promise; + installPromise: null | Promise; /** * A promise that resolves when the Snap has finished booting @@ -333,6 +341,7 @@ type RollbackSnapshot = { granted?: RequestedPermissions; requestData?: Record; }; + previousSourceCode: string; previousInitialConnections?: Record | null; newInitialConnections?: Record; newVersion: string; @@ -670,7 +679,11 @@ export type AllowedActions = | Update | ResolveVersion | CreateInterface - | GetInterface; + | GetInterface + | StorageServiceSetItemAction + | StorageServiceGetItemAction + | StorageServiceRemoveItemAction + | StorageServiceClearAction; export type AllowedEvents = | ExecutionServiceEvents @@ -943,6 +956,8 @@ export class SnapController extends BaseController< readonly #ensureOnboardingComplete: () => Promise; + readonly #controllerSetup = createDeferredPromise(); + constructor({ closeAllConnections, messenger, @@ -991,7 +1006,6 @@ export class SnapController extends BaseController< return Object.values(snaps).reduce>>( (acc, snap) => { const snapCopy: Partial = { ...snap }; - delete snapCopy.sourceCode; delete snapCopy.auxiliaryFiles; acc[snap.id] = snapCopy; return acc; @@ -1115,10 +1129,6 @@ export class SnapController extends BaseController< this.#setupRuntime(snap.id), ); - if (this.#preinstalledSnaps) { - this.#handlePreinstalledSnaps(this.#preinstalledSnaps); - } - this.#trackSnapExport = throttleTracking( (snapId: SnapId, handler: string, success: boolean, origin: string) => { const snapMetadata = this.messenger.call( @@ -1215,8 +1225,9 @@ export class SnapController extends BaseController< * actions. */ #registerMessageHandlers(): void { - this.messenger.registerActionHandler(`${controllerName}:init`, (...args) => - this.init(...args), + this.messenger.registerActionHandler( + `${controllerName}:init`, + async (...args) => this.init(...args), ); this.messenger.registerActionHandler( @@ -1331,17 +1342,41 @@ export class SnapController extends BaseController< /** * Initialise the SnapController. * - * Currently this method calls the `onStart` lifecycle hook for all + * Currently this method sets up the controller and calls the `onStart` lifecycle hook for all * runnable Snaps. */ init() { - // Lazily populate the `isReady` state. - this.#ensureCanUsePlatform().catch(logWarning); + this.#setup().catch((error) => { + logError('Error during SnapController initialization.', error); + }); this.#callLifecycleHooks(METAMASK_ORIGIN, HandlerType.OnStart); } - #handlePreinstalledSnaps(preinstalledSnaps: PreinstalledSnap[]) { + /** + * Sets up the SnapController by handling preinstalled snaps. + * This method will resolve the controller setup promise when complete + * or reject the promise if there is an error. + * + * @throws If there is an error during setup. + */ + async #setup() { + try { + if (this.#preinstalledSnaps) { + await this.#handlePreinstalledSnaps(this.#preinstalledSnaps); + } + + await this.#platformIsReady(); + + this.#controllerSetup.resolve(); + } catch (error) { + this.#controllerSetup.reject(error); + + throw error; + } + } + + async #handlePreinstalledSnaps(preinstalledSnaps: PreinstalledSnap[]) { for (const { snapId, manifest, @@ -1413,7 +1448,7 @@ export class SnapController extends BaseController< }; // Add snap to the SnapController state - this.#set({ + await this.#set({ id: snapId, origin: METAMASK_ORIGIN, files: filesObject, @@ -1729,6 +1764,13 @@ export class SnapController extends BaseController< * Waits for onboarding and then asserts whether the Snaps platform is allowed to run. */ async #ensureCanUsePlatform() { + await this.#platformIsReady(); + + // Ensure the controller has finished setting up. + await this.#controllerSetup.promise; + } + + async #platformIsReady() { // Ensure the user has onboarded before allowing access to Snaps. await this.#ensureOnboardingComplete(); @@ -1831,9 +1873,11 @@ export class SnapController extends BaseController< throw new Error(`Snap "${snapId}" is disabled.`); } + const sourceCode = await this.#getSourceCode(snapId); + await this.#startSnap({ snapId, - sourceCode: snap.sourceCode, + sourceCode, }); } @@ -2417,9 +2461,11 @@ export class SnapController extends BaseController< this.#snapsRuntimeData.clear(); this.#rollbackSnapshots.clear(); + await this.#clearStorageService(); + // We want to remove all snaps & permissions, except for preinstalled snaps if (this.#preinstalledSnaps) { - this.#handlePreinstalledSnaps(this.#preinstalledSnaps); + await this.#handlePreinstalledSnaps(this.#preinstalledSnaps); } } @@ -2470,6 +2516,8 @@ export class SnapController extends BaseController< delete state.unencryptedSnapStates[snapId]; }); + await this.#removeSourceCode(snapId); + // If the snap has been fully installed before, also emit snapUninstalled. if (snap.status !== SnapStatus.Installing) { this.messenger.publish(`SnapController:snapUninstalled`, truncated); @@ -3118,7 +3166,7 @@ export class SnapController extends BaseController< this.#transition(snapId, SnapStatusEvents.Update); - this.#set({ + await this.#set({ origin, id: snapId, files: newSnap, @@ -3223,7 +3271,7 @@ export class SnapController extends BaseController< * version. * @returns The resulting snap object. */ - async #add(args: AddSnapArgs): Promise { + async #add(args: AddSnapArgs): Promise { const { id: snapId, location, versionRange } = args; this.#setupRuntime(snapId); @@ -3373,7 +3421,7 @@ export class SnapController extends BaseController< * @param args - The add snap args. * @returns The resulting snap object. */ - #set(args: SetSnapArgs): PersistedSnap { + async #set(args: SetSnapArgs): Promise { const { id: snapId, origin, @@ -3446,7 +3494,6 @@ export class SnapController extends BaseController< initialPermissions: manifest.result.initialPermissions, manifest: manifest.result, status: this.#statusMachine.config.initial as StatusStates['value'], - sourceCode, version, versionHistory, auxiliaryFiles, @@ -3464,12 +3511,17 @@ export class SnapController extends BaseController< // checking for isUpdate here as this function is also used in // the install flow, we do not care to create snapshots for installs if (isUpdate) { + const previousSourceCode = await this.#getSourceCode(snapId); const rollbackSnapshot = this.#getRollbackSnapshot(snapId); + if (rollbackSnapshot !== undefined) { rollbackSnapshot.statePatches = inversePatches; + rollbackSnapshot.previousSourceCode = previousSourceCode; } } + await this.#setSourceCode(snapId, sourceCode); + // In case the Snap uses a localized manifest, we need to get the // proposed name from the localized manifest. const { proposedName } = getLocalizedSnapManifest( @@ -4237,6 +4289,7 @@ export class SnapController extends BaseController< this.#rollbackSnapshots.set(snapId, { statePatches: [], + previousSourceCode: '', permissions: {}, newVersion: '', }); @@ -4278,12 +4331,15 @@ export class SnapController extends BaseController< permissions, previousInitialConnections, newInitialConnections, + previousSourceCode, } = rollbackSnapshot; if (statePatches?.length) { this.applyPatches(statePatches); } + await this.#setSourceCode(snapId, previousSourceCode); + // Reset snap status, as we may have been in another state when we stored state patches // But now we are 100% in a stopped state if (this.get(snapId)?.status !== SnapStatus.Stopped) { @@ -4664,4 +4720,62 @@ export class SnapController extends BaseController< runtime.state = undefined; } } + + /** + * Retrieve the source code for a snap from storage. + * + * @param snapId - The snap ID. + * @returns The source code for the snap. + */ + async #getSourceCode(snapId: SnapId): Promise { + const storage = await this.#getStorage(snapId); + + assert(storage?.sourceCode, `Source code for snap "${snapId}" not found.`); + + return storage?.sourceCode; + } + + async #getStorage(snapId: SnapId): Promise { + const { result, error } = await this.messenger.call( + 'StorageService:getItem', + this.name, + snapId, + ); + + assert(!error, `Error retrieving storage for snap "${snapId}": ${error}`); + + return result as StorageServiceSnapData | null; + } + + /** + * Store the source code for a snap in storage. + * For now we call the StorageService with just the source code + * since that's all we store. + * + * @param snapId - The snap ID. + * @param sourceCode - The source code for the snap. + */ + async #setSourceCode(snapId: SnapId, sourceCode: string): Promise { + await this.messenger.call('StorageService:setItem', this.name, snapId, { + sourceCode, + }); + } + + /** + * Remove the source code for a snap from storage. + * Since we only store source code, this effectively + * removes all data for the snap for now. + * + * @param snapId - The snap ID. + */ + async #removeSourceCode(snapId: SnapId): Promise { + await this.messenger.call('StorageService:removeItem', this.name, snapId); + } + + /** + * Clear all snap data from the storage service. + */ + async #clearStorageService(): Promise { + await this.messenger.call('StorageService:clear', this.name); + } } diff --git a/packages/snaps-controllers/src/test-utils/controller.tsx b/packages/snaps-controllers/src/test-utils/controller.tsx index 51eda543aa..69192534a7 100644 --- a/packages/snaps-controllers/src/test-utils/controller.tsx +++ b/packages/snaps-controllers/src/test-utils/controller.tsx @@ -25,6 +25,7 @@ import { } from '@metamask/snaps-rpc-methods'; import type { SnapId } from '@metamask/snaps-sdk'; import { Text } from '@metamask/snaps-sdk/jsx'; +import type { PersistedSnap } from '@metamask/snaps-utils'; import { SnapCaveatType } from '@metamask/snaps-utils'; import { getPersistedSnapObject, @@ -35,6 +36,7 @@ import { MockControllerMessenger, TEST_SECRET_RECOVERY_PHRASE_SEED_BYTES, } from '@metamask/snaps-utils/test-utils'; +import { InMemoryStorageAdapter } from '@metamask/storage-service'; import type { Json } from '@metamask/utils'; import { MOCK_CRONJOB_PERMISSION } from './cronjob'; @@ -69,7 +71,7 @@ import type { SnapsRegistryActions, SnapsRegistryEvents, } from '../snaps'; -import { SnapController } from '../snaps'; +import { controllerName, SnapController } from '../snaps'; import type { KeyDerivationOptions } from '../types'; import type { WebSocketServiceActions, @@ -153,6 +155,8 @@ export class MockApprovalController { export const approvalControllerMock = new MockApprovalController(); +export const storageAdapter = new InMemoryStorageAdapter(); + export const snapDialogPermissionKey = 'snap_dialog'; export const MOCK_INTERFACE_ID = 'QovlAsV2Z3xLP5hsrVMsz'; @@ -452,6 +456,26 @@ export const getControllerMessenger = (registry = new MockSnapsRegistry()) => { }, ); + messenger.registerActionHandler( + 'StorageService:setItem', + storageAdapter.setItem.bind(storageAdapter), + ); + + messenger.registerActionHandler( + 'StorageService:getItem', + storageAdapter.getItem.bind(storageAdapter), + ); + + messenger.registerActionHandler( + 'StorageService:removeItem', + storageAdapter.removeItem.bind(storageAdapter), + ); + + messenger.registerActionHandler( + 'StorageService:clear', + storageAdapter.clear.bind(storageAdapter), + ); + jest.spyOn(messenger, 'call'); return messenger; @@ -497,6 +521,10 @@ export const getSnapControllerMessenger = ( 'SnapsRegistry:resolveVersion', 'SnapInterfaceController:createInterface', 'SnapInterfaceController:getInterface', + 'StorageService:setItem', + 'StorageService:getItem', + 'StorageService:removeItem', + 'StorageService:clear', ], events: [ 'ExecutionService:unhandledError', @@ -614,19 +642,94 @@ export const getSnapControllerWithEESOptions = ({ }; }; -export const getSnapController = (options = getSnapControllerOptions()) => { - return new SnapController(options); +export const hydrateStorageService = async ( + sourceCodes: Record, +) => { + await Promise.all( + Object.entries(sourceCodes).map(async ([snapId, sourceCode]) => { + await storageAdapter.setItem(controllerName, snapId, { sourceCode }); + }), + ); }; -export const getSnapControllerWithEES = ( +export const extractSourceCodeFromState = ( + state: PersistedSnapControllerState | undefined, +) => { + if (!state) { + return { state: undefined, sourceCodes: undefined }; + } + + const { snaps: snapControllerState, ...stateRest } = state; + + const { snaps, sourceCodes } = Object.entries(snapControllerState).reduce<{ + snaps: Record; + sourceCodes: Record; + }>( + (acc, [snapId, snap]) => { + const { sourceCode, ...rest } = snap; + + acc.snaps[snapId as SnapId] = rest; + acc.sourceCodes[snapId as SnapId] = sourceCode; + + return acc; + }, + { + snaps: {}, + sourceCodes: {}, + }, + ); + + const newState = { + snaps, + ...stateRest, + }; + + return { state: newState, sourceCodes }; +}; + +export const getSnapController = async ( + options = getSnapControllerOptions(), + init = true, +) => { + const { state, ...restOptions } = options; + const { state: snapControllerState, sourceCodes } = + extractSourceCodeFromState(state); + + const controller = new SnapController({ + state: snapControllerState, + ...restOptions, + }); + + if (sourceCodes) { + await hydrateStorageService(sourceCodes); + } + + if (init) { + controller.init(); + } + + return controller; +}; + +export const getSnapControllerWithEES = async ( options = getSnapControllerWithEESOptions(), service?: ReturnType, + init = true, ) => { const _service = // @ts-expect-error: TODO: Investigate type mismatch. service ?? getNodeEES(getNodeEESMessenger(options.rootMessenger)); + if (options.state?.snaps) { + await hydrateStorageService(options.state.snaps); + } + const controller = new SnapController(options); + + if (init) { + controller.init(); + } + return [controller, _service] as const; }; diff --git a/packages/snaps-simulation/src/methods/hooks/get-snap.ts b/packages/snaps-simulation/src/methods/hooks/get-snap.ts index af622295ca..98db6bf31a 100644 --- a/packages/snaps-simulation/src/methods/hooks/get-snap.ts +++ b/packages/snaps-simulation/src/methods/hooks/get-snap.ts @@ -23,7 +23,6 @@ export function getGetSnapImplementation(preinstalled: boolean = true) { status: SnapStatus.Running, versionHistory: [], initialPermissions: {}, - sourceCode: '', manifest: { version: '0.1.0' as SemVerVersion, proposedName: 'Test Snap', diff --git a/packages/snaps-utils/src/snaps.ts b/packages/snaps-utils/src/snaps.ts index 2a47668107..57ed8e4f3a 100644 --- a/packages/snaps-utils/src/snaps.ts +++ b/packages/snaps-utils/src/snaps.ts @@ -91,8 +91,17 @@ export type SnapAuxiliaryFile = { */ export type SnapAuxilaryFile = SnapAuxiliaryFile; +/** + * A Snap's data as stored in the StorageService. + */ +export type StorageServiceSnapData = { + sourceCode: string; +}; + export type PersistedSnap = Snap; +export type StoredSnap = PersistedSnap & StorageServiceSnapData; + /** * A Snap as it exists in {@link SnapController} state. */ @@ -107,11 +116,6 @@ export type Snap = TruncatedSnap & { */ initialPermissions: SnapPermissions; - /** - * The source code of the Snap. - */ - sourceCode: string; - /** * The Snap's manifest file. */ diff --git a/packages/snaps-utils/src/test-utils/snap.ts b/packages/snaps-utils/src/test-utils/snap.ts index 2012112dc0..9976af03dc 100644 --- a/packages/snaps-utils/src/test-utils/snap.ts +++ b/packages/snaps-utils/src/test-utils/snap.ts @@ -59,7 +59,6 @@ export const getSnapObject = ({ enabled = true, id = MOCK_SNAP_ID, initialPermissions = getSnapManifest().initialPermissions, - sourceCode = DEFAULT_SNAP_BUNDLE, manifest = getSnapManifest(), status = SnapStatus.Stopped, version = getSnapManifest().version, @@ -74,7 +73,6 @@ export const getSnapObject = ({ return { blocked, initialPermissions, - sourceCode, id, version: version as SemVerVersion, manifest, diff --git a/yarn.lock b/yarn.lock index ff91fd6cbf..449cf2cf6f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4269,6 +4269,7 @@ __metadata: "@metamask/snaps-rpc-methods": "workspace:^" "@metamask/snaps-sdk": "workspace:^" "@metamask/snaps-utils": "workspace:^" + "@metamask/storage-service": "npm:^0.0.1" "@metamask/superstruct": "npm:^3.2.1" "@metamask/utils": "npm:^11.8.1" "@noble/hashes": "npm:^1.7.1" @@ -4682,6 +4683,16 @@ __metadata: languageName: unknown linkType: soft +"@metamask/storage-service@npm:^0.0.1": + version: 0.0.1 + resolution: "@metamask/storage-service@npm:0.0.1" + dependencies: + "@metamask/messenger": "npm:^0.3.0" + "@metamask/utils": "npm:^11.8.1" + checksum: 10/ba2443e4bab7a4ef64bf3e0a10403ef94d50225e5ae5931a6fd32a21bfa8e276916ab6dc377d2db30c15c50acaeaee74a3c0b418a563311da9f98b9172fba300 + languageName: node + linkType: hard + "@metamask/superstruct@npm:^3.1.0, @metamask/superstruct@npm:^3.2.1": version: 3.2.1 resolution: "@metamask/superstruct@npm:3.2.1"