@metamask/snaps-simulation
Version:
A simulation framework for MetaMask Snaps, enabling headless testing of Snaps in a controlled environment
284 lines • 10.2 kB
JavaScript
import { HandlerType } from "@metamask/snaps-utils";
import { create } from "@metamask/superstruct";
import { createModuleLogger } from "@metamask/utils";
import { rootLogger } from "./logger.mjs";
import { handleRequest } from "./request.mjs";
import { addJsonRpcMock, removeJsonRpcMock } from "./store/index.mjs";
import { assertIsResponseWithInterface, JsonRpcMockOptionsStruct, NameLookupOptionsStruct, SignatureOptionsStruct, TransactionOptionsStruct } from "./structs.mjs";
const log = createModuleLogger(rootLogger, 'helpers');
/**
* Get the helper functions for the Snap.
*
* @param snap - The installed Snap.
* @param snap.snapId - The ID of the Snap.
* @param snap.store - The Redux store.
* @param snap.executionService - The execution service.
* @param snap.runSaga - The `runSaga` function.
* @param snap.controllerMessenger - The controller messenger.
* @param snap.options - The simulation options.
* @returns The Snap helpers.
*/
export function getHelpers({ snapId, store, executionService, runSaga, controllerMessenger, options, }) {
const onTransaction = async (request) => {
log('Sending transaction %o.', request);
const { origin: transactionOrigin, chainId, ...transaction } = create(request, TransactionOptionsStruct);
const response = await handleRequest({
snapId,
store,
executionService,
runSaga,
controllerMessenger,
simulationOptions: options,
handler: HandlerType.OnTransaction,
request: {
method: '',
params: {
chainId,
transaction,
transactionOrigin,
},
},
});
assertIsResponseWithInterface(response);
return response;
};
// This can't be async because it returns a `SnapRequest`.
// eslint-disable-next-line @typescript-eslint/promise-function-async
const onCronjob = (request) => {
log('Running cronjob %o.', options);
return handleRequest({
snapId,
store,
executionService,
controllerMessenger,
simulationOptions: options,
runSaga,
handler: HandlerType.OnCronjob,
request,
});
};
// This can't be async because it returns a `SnapRequest`.
// eslint-disable-next-line @typescript-eslint/promise-function-async
const onKeyringRequest = (request) => {
log('Sending keyring request %o.', request);
return handleRequest({
snapId,
store,
executionService,
runSaga,
controllerMessenger,
simulationOptions: options,
handler: HandlerType.OnKeyringRequest,
request,
});
};
return {
// This can't be async because it returns a `SnapRequest`.
// eslint-disable-next-line @typescript-eslint/promise-function-async
request: (request) => {
log('Sending request %o.', request);
return handleRequest({
snapId,
store,
executionService,
controllerMessenger,
simulationOptions: options,
runSaga,
handler: HandlerType.OnRpcRequest,
request,
});
},
onTransaction,
sendTransaction: onTransaction,
onKeyringRequest,
// This can't be async because it returns a `SnapRequest`.
// eslint-disable-next-line @typescript-eslint/promise-function-async
onInstall: (request) => {
log('Running onInstall handler.');
return handleRequest({
snapId,
store,
executionService,
controllerMessenger,
simulationOptions: options,
runSaga,
handler: HandlerType.OnInstall,
request: {
method: '',
...request,
},
});
},
// This can't be async because it returns a `SnapRequest`.
// eslint-disable-next-line @typescript-eslint/promise-function-async
onUpdate: (request) => {
log('Running onUpdate handler.');
return handleRequest({
snapId,
store,
executionService,
controllerMessenger,
simulationOptions: options,
runSaga,
handler: HandlerType.OnUpdate,
request: {
method: '',
...request,
},
});
},
// This can't be async because it returns a `SnapRequest`.
// eslint-disable-next-line @typescript-eslint/promise-function-async
onStart: (request) => {
log('Running onStart handler.');
return handleRequest({
snapId,
store,
executionService,
controllerMessenger,
simulationOptions: options,
runSaga,
handler: HandlerType.OnStart,
request: {
method: '',
...request,
},
});
},
onNameLookup: async (nameLookupOptions) => {
log('Requesting name lookup %o.', nameLookupOptions);
const params = create(nameLookupOptions, NameLookupOptionsStruct);
const response = await handleRequest({
snapId,
store,
executionService,
controllerMessenger,
simulationOptions: options,
runSaga,
handler: HandlerType.OnNameLookup,
request: {
method: '',
params,
},
});
return response;
},
onSignature: async (request) => {
log('Requesting signature %o.', request);
const { origin: signatureOrigin, ...signature } = create(request, SignatureOptionsStruct);
const response = await handleRequest({
snapId,
store,
executionService,
controllerMessenger,
simulationOptions: options,
runSaga,
handler: HandlerType.OnSignature,
request: {
method: '',
params: {
signature,
signatureOrigin,
},
},
});
assertIsResponseWithInterface(response);
return response;
},
onCronjob,
runCronjob: onCronjob,
onBackgroundEvent: onCronjob,
onHomePage: async () => {
log('Rendering home page.');
const response = await handleRequest({
snapId,
store,
executionService,
controllerMessenger,
simulationOptions: options,
runSaga,
handler: HandlerType.OnHomePage,
request: {
method: '',
},
});
assertIsResponseWithInterface(response);
return response;
},
onSettingsPage: async () => {
log('Rendering settings page.');
const response = await handleRequest({
snapId,
store,
executionService,
controllerMessenger,
simulationOptions: options,
runSaga,
handler: HandlerType.OnSettingsPage,
request: {
method: '',
},
});
assertIsResponseWithInterface(response);
return response;
},
onProtocolRequest: async (scope, rawRequest) => {
log('Sending protocol request.');
const request = {
jsonrpc: '2.0',
id: rawRequest.id ?? 1,
method: rawRequest.method,
...(rawRequest.params ? { params: rawRequest.params } : {}),
};
const response = await handleRequest({
snapId,
store,
executionService,
controllerMessenger,
simulationOptions: options,
runSaga,
handler: HandlerType.OnProtocolRequest,
request: {
origin: rawRequest.origin,
method: '',
params: {
scope,
request,
},
},
});
return response;
},
// This can't be async because it returns a `SnapRequest`.
// eslint-disable-next-line @typescript-eslint/promise-function-async
onClientRequest: (request) => {
log('Sending client request.');
return handleRequest({
snapId,
store,
executionService,
controllerMessenger,
simulationOptions: options,
runSaga,
handler: HandlerType.OnClientRequest,
request,
});
},
mockJsonRpc(mock) {
log('Mocking JSON-RPC request %o.', mock);
const { method, result } = create(mock, JsonRpcMockOptionsStruct);
store.dispatch(addJsonRpcMock({ method, result }));
return {
unmock() {
log('Unmocking JSON-RPC request %o.', mock);
store.dispatch(removeJsonRpcMock(method));
},
};
},
close: async () => {
log('Closing execution service.');
await executionService.terminateAllSnaps();
},
};
}
//# sourceMappingURL=helpers.mjs.map