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

Add basic support for account RPC methods in snaps simulator #1710

Merged
merged 7 commits into from
Aug 30, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
56 changes: 51 additions & 5 deletions packages/snaps-simulator/src/features/simulation/middleware.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import {
BIP44CoinTypeNode,
getBIP44AddressKeyDeriver,
} from '@metamask/key-tree';
import { logError } from '@metamask/snaps-utils';
import type {
JsonRpcEngineEndCallback,
Expand All @@ -10,8 +14,48 @@ import type {
export const methodHandlers = {
// eslint-disable-next-line @typescript-eslint/naming-convention
metamask_getProviderState: getProviderStateHandler,
// eslint-disable-next-line @typescript-eslint/naming-convention
eth_requestAccounts: getAccountsHandler,
// eslint-disable-next-line @typescript-eslint/naming-convention
eth_accounts: getAccountsHandler,
};
FrederikBolding marked this conversation as resolved.
Show resolved Hide resolved

export type MiscMiddlewareHooks = {
getMnemonic: () => Promise<Uint8Array>;
};

/**
* A mock handler for account related methods that always returns the first address for the selected SRP.
*
* @param _request - Incoming JSON-RPC request, ignored for this specific handler.
* @param response - The outgoing JSON-RPC response, modified to return the result.
* @param _next - The json-rpc-engine middleware next handler.
* @param end - The json-rpc-engine middleware end handler.
* @param hooks - Any hooks required by this handler.
*/
async function getAccountsHandler(
_request: JsonRpcRequest<unknown>,
response: PendingJsonRpcResponse<unknown>,
_next: JsonRpcEngineNextCallback,
end: JsonRpcEngineEndCallback,
hooks: MiscMiddlewareHooks,
) {
const { getMnemonic } = hooks;

const node = await BIP44CoinTypeNode.fromDerivationPath([
await getMnemonic(),
`bip32:44'`,
`bip32:60'`,
]);

const deriveAddress = await getBIP44AddressKeyDeriver(node);

const { address } = await deriveAddress(0);
FrederikBolding marked this conversation as resolved.
Show resolved Hide resolved

response.result = [address];
return end();
}

/**
* A mock handler for metamask_getProviderState that always returns a specific hardcoded result.
*
Expand Down Expand Up @@ -39,12 +83,14 @@ async function getProviderStateHandler(
/**
* Creates a middleware for handling misc RPC methods normally handled internally by the MM client.
*
* NOTE: This middleware provides all `hooks` to all handlers and should therefore NOT be used outside of the simulator.
*
* @param hooks - Any hooks used by the middleware handlers.
* @returns Nothing.
*/
export function createMiscMethodMiddleware(): JsonRpcMiddleware<
unknown,
unknown
> {
export function createMiscMethodMiddleware(
hooks: MiscMiddlewareHooks,
): JsonRpcMiddleware<unknown, unknown> {
// This should probably use createAsyncMiddleware
// eslint-disable-next-line @typescript-eslint/no-misused-promises
return async function methodMiddleware(request, response, next, end) {
Expand All @@ -53,7 +99,7 @@ export function createMiscMethodMiddleware(): JsonRpcMiddleware<
if (handler) {
try {
// Implementations may or may not be async, so we must await them.
return await handler(request, response, next, end);
return await handler(request, response, next, end, hooks);
} catch (error: any) {
logError(error);
return end(error);
Expand Down
8 changes: 6 additions & 2 deletions packages/snaps-simulator/src/features/simulation/sagas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,16 +80,20 @@ export function* initSaga({ payload }: PayloadAction<string>) {

const srp: string = yield select(getSrp);

const sharedHooks = {
getMnemonic: async () => mnemonicPhraseToBytes(srp),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be covered?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

None of the hooks are currently covered because we don't have tests that run them directly. IMO out of scope for this PR and not exactly sure how we test to cover them

};

const permissionSpecifications = {
...buildSnapEndowmentSpecifications(Object.keys(ExcludedSnapEndowments)),
...buildSnapRestrictedMethodSpecifications([], {
...sharedHooks,
// TODO: Add all the hooks required
encrypt,
decrypt,
// TODO: Allow changing this?
getLocale: async () => Promise.resolve('en'),
getUnlockPromise: async () => Promise.resolve(true),
getMnemonic: async () => mnemonicPhraseToBytes(srp),
showDialog: async (...args: Parameters<typeof showDialog>) =>
await runSaga(showDialog, ...args).toPromise(),
showNativeNotification: async (
Expand Down Expand Up @@ -137,7 +141,7 @@ export function* initSaga({ payload }: PayloadAction<string>) {

const engine = new JsonRpcEngine();

engine.push(createMiscMethodMiddleware());
engine.push(createMiscMethodMiddleware(sharedHooks));

engine.push(
permissionController.createPermissionMiddleware({
Expand Down
Loading