Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: Extract wallet_invokeSnap implementation into own middleware #2398

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/snaps-rpc-methods/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export {
handlers as permittedMethods,
createSnapsMethodMiddleware,
createSnapsMethodRemapMiddleware,
} from './permitted';
export type { PermittedRpcMethodHooks } from './permitted';
export { SnapCaveatType } from '@metamask/snaps-utils';
Expand Down
8 changes: 5 additions & 3 deletions packages/snaps-rpc-methods/src/permitted/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,16 @@ export const methodHandlers = {
wallet_getAllSnaps: getAllSnapsHandler,
wallet_getSnaps: getSnapsHandler,
wallet_requestSnaps: requestSnapsHandler,
wallet_invokeSnap: invokeSnapSugarHandler,
wallet_invokeKeyring: invokeKeyringHandler,
snap_getClientStatus: getClientStatusHandler,
snap_getFile: getFileHandler,
snap_createInterface: createInterfaceHandler,
snap_updateInterface: updateInterfaceHandler,
snap_getInterfaceState: getInterfaceStateHandler,
};
/* eslint-enable @typescript-eslint/naming-convention */

export const handlers = Object.values(methodHandlers);
// Methods that remap themselves to another method.
export const methodRemapHandlers = {
wallet_invokeSnap: invokeSnapSugarHandler,
};
/* eslint-enable @typescript-eslint/naming-convention */
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,9 @@ describe('wallet_invokeSnap', () => {
id: 'some-id',
jsonrpc: '2.0',
method: 'wallet_invokeSnap',
// @ts-expect-error - Invalid params.
params: {
// @ts-expect-error - Invalid params.
snapId: undefined,

// @ts-expect-error - Invalid params.
request: [],
},
};
Expand Down
57 changes: 56 additions & 1 deletion packages/snaps-rpc-methods/src/permitted/middleware.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@ import {
MOCK_SNAP_ID,
getTruncatedSnap,
} from '@metamask/snaps-utils/test-utils';
import { jsonrpc2 } from '@metamask/utils';

import { createSnapsMethodMiddleware } from './middleware';
import {
createSnapsMethodMiddleware,
createSnapsMethodRemapMiddleware,
} from './middleware';

describe('createSnapsMethodMiddleware', () => {
it('supports wallet_getSnaps', async () => {
Expand Down Expand Up @@ -126,3 +130,54 @@ describe('createSnapsMethodMiddleware', () => {
});
});
});

describe('createSnapsMethodRemapMiddleware', () => {
const mockMethodName = 'foo';
const getMockHandlerMap = () => ({
[mockMethodName]: {
implementation: jest.fn(),
},
});
const getMockRequest = () => ({
jsonrpc: jsonrpc2,
id: 1,
method: mockMethodName,
});
const getMockResponse = () => ({
jsonrpc: jsonrpc2,
id: 'foo',
result: null,
});

// Exports coverage
it('creates a middleware function', () => {
const middleware = createSnapsMethodRemapMiddleware();
expect(typeof middleware).toBe('function');
});

it('calls remap handlers', () => {
const handlers = getMockHandlerMap();
// @ts-expect-error Intentional destructive testing
const middleware = createSnapsMethodRemapMiddleware(handlers);
const nextMock = jest.fn();
const endMock = jest.fn();

middleware(getMockRequest(), getMockResponse(), nextMock, endMock);

expect(handlers[mockMethodName].implementation).toHaveBeenCalledTimes(1);
expect(nextMock).not.toHaveBeenCalled();
expect(endMock).not.toHaveBeenCalled();
});

it('forwards requests for methods without a remap handler', () => {
// @ts-expect-error Intentional destructive testing
const middleware = createSnapsMethodRemapMiddleware({});
const nextMock = jest.fn();
const endMock = jest.fn();

middleware(getMockRequest(), getMockResponse(), nextMock, endMock);

expect(nextMock).toHaveBeenCalledTimes(1);
expect(endMock).not.toHaveBeenCalled();
});
});
28 changes: 26 additions & 2 deletions packages/snaps-rpc-methods/src/permitted/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ import { logError } from '@metamask/snaps-utils';
import type { Json, JsonRpcParams } from '@metamask/utils';

import { selectHooks } from '../utils';
import { methodHandlers } from './handlers';
import { methodHandlers, methodRemapHandlers } from './handlers';

/**
* Creates a middleware that handles permitted snap RPC methods.
*
* @param isSnap - A flag that should indicate whether the requesting origin is a snap or not.
* @param hooks - An object containing the hooks made available to the permitted RPC methods.
* @returns The middleware.
* @returns The middleware function.
*/
export function createSnapsMethodMiddleware(
isSnap: boolean,
Expand Down Expand Up @@ -50,3 +50,27 @@ export function createSnapsMethodMiddleware(
return next();
};
}

/**
* Creates a middleware that handles Snap-related methods that are remapped to a different
* method. In other words, the actual implementation exists elsewhere.
*
* @param handlerMap - The map of method names to their handlers.
* @returns The middleware function.
*/
export function createSnapsMethodRemapMiddleware(
handlerMap = methodRemapHandlers,
): JsonRpcMiddleware<JsonRpcParams, Json> {
return function methodRemapMiddleware(request, response, next, end) {
const handler = handlerMap[request.method as keyof typeof handlerMap];
if (handler) {
return (handler.implementation as JsonRpcMiddleware<JsonRpcParams, Json>)(
request,
response,
next,
end,
);
}
return next();
};
}
Loading