Skip to content

Commit e9b12d0

Browse files
feat: Add snap_getInterfaceContext
1 parent 126990b commit e9b12d0

File tree

5 files changed

+225
-4
lines changed

5 files changed

+225
-4
lines changed

packages/snaps-rpc-methods/jest.config.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ module.exports = deepmerge(baseConfig, {
1010
],
1111
coverageThreshold: {
1212
global: {
13-
branches: 92.85,
14-
functions: 97.23,
15-
lines: 97.8,
16-
statements: 97.31,
13+
branches: 92.88,
14+
functions: 97.26,
15+
lines: 97.84,
16+
statements: 97.36,
1717
},
1818
},
1919
});
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import { JsonRpcEngine } from '@metamask/json-rpc-engine';
2+
import { type GetInterfaceContextResult } from '@metamask/snaps-sdk';
3+
import type { JsonRpcRequest, PendingJsonRpcResponse } from '@metamask/utils';
4+
5+
import type { GetInterfaceContextParameters } from './getInterfaceContext';
6+
import { getInterfaceContextHandler } from './getInterfaceContext';
7+
8+
describe('snap_getInterfaceContext', () => {
9+
describe('getInterfaceContextHandler', () => {
10+
it('has the expected shape', () => {
11+
expect(getInterfaceContextHandler).toMatchObject({
12+
methodNames: ['snap_getInterfaceContext'],
13+
implementation: expect.any(Function),
14+
hookNames: {
15+
getInterfaceContext: true,
16+
},
17+
});
18+
});
19+
});
20+
21+
describe('implementation', () => {
22+
it('returns the result from the `getInterfaceState` hook', async () => {
23+
const { implementation } = getInterfaceContextHandler;
24+
25+
const getInterfaceContext = jest.fn().mockReturnValue({ foo: 'bar' });
26+
27+
const hooks = {
28+
getInterfaceContext,
29+
};
30+
31+
const engine = new JsonRpcEngine();
32+
33+
engine.push((request, response, next, end) => {
34+
const result = implementation(
35+
request as JsonRpcRequest<GetInterfaceContextParameters>,
36+
response as PendingJsonRpcResponse<GetInterfaceContextResult>,
37+
next,
38+
end,
39+
hooks,
40+
);
41+
42+
result?.catch(end);
43+
});
44+
45+
const response = await engine.handle({
46+
jsonrpc: '2.0',
47+
id: 1,
48+
method: 'snap_getInterfaceContext',
49+
params: {
50+
id: 'foo',
51+
},
52+
});
53+
54+
expect(response).toStrictEqual({
55+
jsonrpc: '2.0',
56+
id: 1,
57+
result: { foo: 'bar' },
58+
});
59+
});
60+
61+
it('throws on invalid params', async () => {
62+
const { implementation } = getInterfaceContextHandler;
63+
64+
const getInterfaceContext = jest.fn().mockReturnValue({ foo: 'bar' });
65+
66+
const hooks = {
67+
getInterfaceContext,
68+
};
69+
70+
const engine = new JsonRpcEngine();
71+
72+
engine.push((request, response, next, end) => {
73+
const result = implementation(
74+
request as JsonRpcRequest<GetInterfaceContextParameters>,
75+
response as PendingJsonRpcResponse<GetInterfaceContextResult>,
76+
next,
77+
end,
78+
hooks,
79+
);
80+
81+
result?.catch(end);
82+
});
83+
84+
const response = await engine.handle({
85+
jsonrpc: '2.0',
86+
id: 1,
87+
method: 'snap_getInterfaceContext',
88+
params: {
89+
id: 42,
90+
},
91+
});
92+
93+
expect(response).toStrictEqual({
94+
error: {
95+
code: -32602,
96+
message:
97+
'Invalid params: At path: id -- Expected a string, but received: 42.',
98+
stack: expect.any(String),
99+
},
100+
id: 1,
101+
jsonrpc: '2.0',
102+
});
103+
});
104+
});
105+
});
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import type { JsonRpcEngineEndCallback } from '@metamask/json-rpc-engine';
2+
import type { PermittedHandlerExport } from '@metamask/permission-controller';
3+
import { rpcErrors } from '@metamask/rpc-errors';
4+
import type {
5+
GetInterfaceContextParams,
6+
GetInterfaceContextResult,
7+
InterfaceState,
8+
JsonRpcRequest,
9+
} from '@metamask/snaps-sdk';
10+
import { type InferMatching } from '@metamask/snaps-utils';
11+
import { StructError, create, object, string } from '@metamask/superstruct';
12+
import type { PendingJsonRpcResponse } from '@metamask/utils';
13+
14+
import type { MethodHooksObject } from '../utils';
15+
16+
const hookNames: MethodHooksObject<GetInterfaceContextMethodHooks> = {
17+
getInterfaceContext: true,
18+
};
19+
20+
export type GetInterfaceContextMethodHooks = {
21+
/**
22+
* @param id - The interface ID.
23+
* @returns The interface state.
24+
*/
25+
getInterfaceContext: (id: string) => InterfaceState;
26+
};
27+
28+
export const getInterfaceContextHandler: PermittedHandlerExport<
29+
GetInterfaceContextMethodHooks,
30+
GetInterfaceContextParameters,
31+
GetInterfaceContextResult
32+
> = {
33+
methodNames: ['snap_getInterfaceContext'],
34+
implementation: getInterfaceContextImplementation,
35+
hookNames,
36+
};
37+
38+
const GetInterfaceContextParametersStruct = object({
39+
id: string(),
40+
});
41+
42+
export type GetInterfaceContextParameters = InferMatching<
43+
typeof GetInterfaceContextParametersStruct,
44+
GetInterfaceContextParams
45+
>;
46+
47+
/**
48+
* The `snap_getInterfaceContext` method implementation.
49+
*
50+
* @param req - The JSON-RPC request object.
51+
* @param res - The JSON-RPC response object.
52+
* @param _next - The `json-rpc-engine` "next" callback. Not used by this
53+
* function.
54+
* @param end - The `json-rpc-engine` "end" callback.
55+
* @param hooks - The RPC method hooks.
56+
* @param hooks.getInterfaceContext - The function to get the interface state.
57+
* @returns Noting.
58+
*/
59+
function getInterfaceContextImplementation(
60+
req: JsonRpcRequest<GetInterfaceContextParameters>,
61+
res: PendingJsonRpcResponse<GetInterfaceContextResult>,
62+
_next: unknown,
63+
end: JsonRpcEngineEndCallback,
64+
{ getInterfaceContext }: GetInterfaceContextMethodHooks,
65+
): void {
66+
const { params } = req;
67+
68+
try {
69+
const validatedParams = getValidatedParams(params);
70+
71+
const { id } = validatedParams;
72+
73+
res.result = getInterfaceContext(id);
74+
} catch (error) {
75+
return end(error);
76+
}
77+
78+
return end();
79+
}
80+
81+
/**
82+
* Validate the getInterfaceContext method `params` and returns them cast to the correct
83+
* type. Throws if validation fails.
84+
*
85+
* @param params - The unvalidated params object from the method request.
86+
* @returns The validated getInterfaceContext method parameter object.
87+
*/
88+
function getValidatedParams(params: unknown): GetInterfaceContextParameters {
89+
try {
90+
return create(params, GetInterfaceContextParametersStruct);
91+
} catch (error) {
92+
if (error instanceof StructError) {
93+
throw rpcErrors.invalidParams({
94+
message: `Invalid params: ${error.message}.`,
95+
});
96+
}
97+
/* istanbul ignore next */
98+
throw rpcErrors.internal();
99+
}
100+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import type { InterfaceContext } from '../interface';
2+
3+
/**
4+
* The request parameters for the `snap_getInterfaceContext` method.
5+
*
6+
* @property id - The interface id.
7+
*/
8+
export type GetInterfaceContextParams = {
9+
id: string;
10+
};
11+
12+
/**
13+
* The result returned by the `snap_getInterfaceContext` method, which is the context for a given interface.
14+
*/
15+
export type GetInterfaceContextResult = InterfaceContext | null;

packages/snaps-sdk/src/types/methods/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export * from './get-bip44-entropy';
66
export * from './get-client-status';
77
export * from './get-entropy';
88
export * from './get-file';
9+
export * from './get-interface-context';
910
export * from './get-interface-state';
1011
export * from './get-locale';
1112
export * from './get-preferences';

0 commit comments

Comments
 (0)