Skip to content

Commit

Permalink
feat!: provide origin to lifecycle hooks (#2441)
Browse files Browse the repository at this point in the history
Provide the `origin` parameter to lifecycle hooks, the origin will be
the origin that originally triggered the install or update process.

This PR also removes `request` from the handler since this is unused and
[undocumented](https://docs.metamask.io/snaps/features/lifecycle-hooks/#2-run-an-action-on-installation).
  • Loading branch information
FrederikBolding authored May 30, 2024
1 parent 37a7fc4 commit 8872751
Show file tree
Hide file tree
Showing 6 changed files with 47 additions and 35 deletions.
4 changes: 2 additions & 2 deletions packages/snaps-controllers/src/snaps/SnapController.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8865,7 +8865,7 @@ describe('SnapController', () => {
MOCK_SNAP_ID,
{
handler: HandlerType.OnInstall,
origin: '',
origin: MOCK_ORIGIN,
request: {
jsonrpc: '2.0',
id: expect.any(String),
Expand Down Expand Up @@ -9008,7 +9008,7 @@ describe('SnapController', () => {
MOCK_SNAP_ID,
{
handler: HandlerType.OnUpdate,
origin: '',
origin: MOCK_ORIGIN,
request: {
jsonrpc: '2.0',
id: expect.any(String),
Expand Down
51 changes: 33 additions & 18 deletions packages/snaps-controllers/src/snaps/SnapController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -871,25 +871,35 @@ export class SnapController extends BaseController<
);
/* eslint-enable @typescript-eslint/unbound-method */

this.messagingSystem.subscribe('SnapController:snapInstalled', ({ id }) => {
this.#callLifecycleHook(id, HandlerType.OnInstall).catch((error) => {
logError(
`Error when calling \`onInstall\` lifecycle hook for snap "${id}": ${getErrorMessage(
error,
)}`,
this.messagingSystem.subscribe(
'SnapController:snapInstalled',
({ id }, origin) => {
this.#callLifecycleHook(origin, id, HandlerType.OnInstall).catch(
(error) => {
logError(
`Error when calling \`onInstall\` lifecycle hook for snap "${id}": ${getErrorMessage(
error,
)}`,
);
},
);
});
});
},
);

this.messagingSystem.subscribe('SnapController:snapUpdated', ({ id }) => {
this.#callLifecycleHook(id, HandlerType.OnUpdate).catch((error) => {
logError(
`Error when calling \`onUpdate\` lifecycle hook for snap "${id}": ${getErrorMessage(
error,
)}`,
this.messagingSystem.subscribe(
'SnapController:snapUpdated',
({ id }, _oldVersion, origin) => {
this.#callLifecycleHook(origin, id, HandlerType.OnUpdate).catch(
(error) => {
logError(
`Error when calling \`onUpdate\` lifecycle hook for snap "${id}": ${getErrorMessage(
error,
)}`,
);
},
);
});
});
},
);

this.#initializeStateMachine();
this.#registerMessageHandlers();
Expand Down Expand Up @@ -3592,12 +3602,17 @@ export class SnapController extends BaseController<
* `endowment:lifecycle-hooks` permission. If the snap does not have the
* permission, nothing happens.
*
* @param origin - The origin.
* @param snapId - The snap ID.
* @param handler - The lifecycle hook to call. This should be one of the
* supported lifecycle hooks.
* @private
*/
async #callLifecycleHook(snapId: SnapId, handler: HandlerType) {
async #callLifecycleHook(
origin: string,
snapId: SnapId,
handler: HandlerType,
) {
const permissionName = handlerEndowments[handler];

assert(permissionName, 'Lifecycle hook must have an endowment.');
Expand All @@ -3615,7 +3630,7 @@ export class SnapController extends BaseController<
await this.handleRequest({
snapId,
handler,
origin: '',
origin,
request: {
jsonrpc: '2.0',
method: handler,
Expand Down
4 changes: 2 additions & 2 deletions packages/snaps-execution-environments/coverage.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"branches": 80,
"functions": 90.06,
"lines": 90.76,
"statements": 90.13
"lines": 90.77,
"statements": 90.15
}
Original file line number Diff line number Diff line change
Expand Up @@ -1615,7 +1615,7 @@ describe('BaseSnapExecutor', () => {
// eslint-disable-next-line no-loop-func
it(`supports \`${handler}\` export`, async () => {
const CODE = `
module.exports.${handler} = ({ request }) => request.params[0];
module.exports.${handler} = ({ origin }) => origin;
`;

const executor = new TestSnapExecutor();
Expand All @@ -1635,14 +1635,14 @@ describe('BaseSnapExecutor', () => {
MOCK_SNAP_ID,
handler,
MOCK_ORIGIN,
{ jsonrpc: '2.0', method: 'foo', params: ['bar'] },
{ jsonrpc: '2.0', method: handler },
],
});

expect(await executor.readCommand()).toStrictEqual({
id: 2,
jsonrpc: '2.0',
result: 'bar',
result: MOCK_ORIGIN,
});
});

Expand Down
4 changes: 3 additions & 1 deletion packages/snaps-execution-environments/src/common/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,11 @@ export function getHandlerArguments(
return { origin, request };

case HandlerType.OnCronjob:
return { request };

case HandlerType.OnInstall:
case HandlerType.OnUpdate:
return { request };
return { origin };

case HandlerType.OnHomePage:
return {};
Expand Down
13 changes: 4 additions & 9 deletions packages/snaps-sdk/src/types/handlers/lifecycle.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import type { JsonRpcRequest } from '@metamask/utils';

/**
* A lifecycle event handler. This is called whenever a lifecycle event occurs,
* such as the Snap being installed or updated.
Expand All @@ -8,11 +6,10 @@ import type { JsonRpcRequest } from '@metamask/utils';
* permission.
*
* @param args - The request arguments.
* @param args.request - The JSON-RPC request sent to the Snap. This does not
* contain any parameters.
* @param args.origin - The origin that triggered the lifecycle event hook.
*/
export type LifecycleEventHandler = (args: {
request: JsonRpcRequest;
origin: string;
}) => Promise<unknown>;

/**
Expand All @@ -24,8 +21,7 @@ export type LifecycleEventHandler = (args: {
* This type is an alias for {@link LifecycleEventHandler}.
*
* @param args - The request arguments.
* @param args.request - The JSON-RPC request sent to the Snap. This does not
* contain any parameters.
* @param args.origin - The origin that triggered the lifecycle event hook.
*/
export type OnInstallHandler = LifecycleEventHandler;

Expand All @@ -38,7 +34,6 @@ export type OnInstallHandler = LifecycleEventHandler;
* This type is an alias for {@link LifecycleEventHandler}.
*
* @param args - The request arguments.
* @param args.request - The JSON-RPC request sent to the Snap. This does not
* contain any parameters.
* @param args.origin - The origin that triggered the lifecycle event hook.
*/
export type OnUpdateHandler = LifecycleEventHandler;

0 comments on commit 8872751

Please sign in to comment.