Skip to content

Commit ba6005b

Browse files
Use withKeyring
1 parent 8edc6fc commit ba6005b

File tree

4 files changed

+96
-74
lines changed

4 files changed

+96
-74
lines changed

packages/snaps-controllers/src/multichain/MultichainRouter.test.ts

Lines changed: 37 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
SOLANA_CAIP2,
1212
MOCK_SOLANA_ACCOUNTS,
1313
MOCK_BTC_ACCOUNTS,
14+
getMockWithSnapKeyring,
1415
} from '../test-utils';
1516
import { MultichainRouter } from './MultichainRouter';
1617

@@ -19,24 +20,23 @@ describe('MultichainRouter', () => {
1920
it('can route signing requests to account Snaps without address resolution', async () => {
2021
const rootMessenger = getRootMultichainRouterMessenger();
2122
const messenger = getRestrictedMultichainRouterMessenger(rootMessenger);
23+
const withSnapKeyring = getMockWithSnapKeyring({
24+
submitRequest: jest.fn().mockResolvedValue({
25+
txid: '53de51e2fa75c3cfa51132865f7d430138b1cd92a8f5267ec836ec565b422969',
26+
}),
27+
});
2228

2329
/* eslint-disable-next-line no-new */
2430
new MultichainRouter({
2531
messenger,
32+
withSnapKeyring,
2633
});
2734

2835
rootMessenger.registerActionHandler(
2936
'AccountsController:listMultichainAccounts',
3037
() => MOCK_BTC_ACCOUNTS,
3138
);
3239

33-
rootMessenger.registerActionHandler(
34-
'KeyringController:submitNonEvmRequest',
35-
async () => ({
36-
txid: '53de51e2fa75c3cfa51132865f7d430138b1cd92a8f5267ec836ec565b422969',
37-
}),
38-
);
39-
4040
rootMessenger.registerActionHandler(
4141
'SnapController:handleRequest',
4242
async ({ handler }) => {
@@ -66,24 +66,23 @@ describe('MultichainRouter', () => {
6666
it('can route signing requests to account Snaps using address resolution', async () => {
6767
const rootMessenger = getRootMultichainRouterMessenger();
6868
const messenger = getRestrictedMultichainRouterMessenger(rootMessenger);
69+
const withSnapKeyring = getMockWithSnapKeyring({
70+
submitRequest: jest.fn().mockResolvedValue({
71+
signature: '0x',
72+
}),
73+
});
6974

7075
/* eslint-disable-next-line no-new */
7176
new MultichainRouter({
7277
messenger,
78+
withSnapKeyring,
7379
});
7480

7581
rootMessenger.registerActionHandler(
7682
'AccountsController:listMultichainAccounts',
7783
() => MOCK_SOLANA_ACCOUNTS,
7884
);
7985

80-
rootMessenger.registerActionHandler(
81-
'KeyringController:submitNonEvmRequest',
82-
async () => ({
83-
signature: '0x',
84-
}),
85-
);
86-
8786
rootMessenger.registerActionHandler(
8887
'PermissionController:getPermissions',
8988
() => MOCK_SOLANA_SNAP_PERMISSIONS,
@@ -113,13 +112,15 @@ describe('MultichainRouter', () => {
113112
expect(result).toStrictEqual({ signature: '0x' });
114113
});
115114

116-
it('can route protocol requests to procotol Snaps', async () => {
115+
it('can route protocol requests to protocol Snaps', async () => {
117116
const rootMessenger = getRootMultichainRouterMessenger();
118117
const messenger = getRestrictedMultichainRouterMessenger(rootMessenger);
118+
const withSnapKeyring = getMockWithSnapKeyring();
119119

120120
/* eslint-disable-next-line no-new */
121121
new MultichainRouter({
122122
messenger,
123+
withSnapKeyring,
123124
});
124125

125126
rootMessenger.registerActionHandler(
@@ -161,10 +162,12 @@ describe('MultichainRouter', () => {
161162
it('throws if no suitable Snaps are found', async () => {
162163
const rootMessenger = getRootMultichainRouterMessenger();
163164
const messenger = getRestrictedMultichainRouterMessenger(rootMessenger);
165+
const withSnapKeyring = getMockWithSnapKeyring();
164166

165167
/* eslint-disable-next-line no-new */
166168
new MultichainRouter({
167169
messenger,
170+
withSnapKeyring,
168171
});
169172

170173
rootMessenger.registerActionHandler(
@@ -190,10 +193,15 @@ describe('MultichainRouter', () => {
190193
it('throws if address resolution fails', async () => {
191194
const rootMessenger = getRootMultichainRouterMessenger();
192195
const messenger = getRestrictedMultichainRouterMessenger(rootMessenger);
196+
const withSnapKeyring = getMockWithSnapKeyring({
197+
// Simulate the Snap returning a bogus address
198+
resolveAccountAddress: jest.fn().mockResolvedValue({ address: 'foo' }),
199+
});
193200

194201
/* eslint-disable-next-line no-new */
195202
new MultichainRouter({
196203
messenger,
204+
withSnapKeyring,
197205
});
198206

199207
rootMessenger.registerActionHandler(
@@ -206,17 +214,6 @@ describe('MultichainRouter', () => {
206214
() => MOCK_SOLANA_SNAP_PERMISSIONS,
207215
);
208216

209-
rootMessenger.registerActionHandler(
210-
'SnapController:handleRequest',
211-
async ({ handler }) => {
212-
if (handler === HandlerType.OnKeyringRequest) {
213-
// Simulate the Snap returning a bogus address
214-
return { address: 'foo' };
215-
}
216-
throw new Error('Unmocked request');
217-
},
218-
);
219-
220217
await expect(
221218
messenger.call('MultichainRouter:handleRequest', {
222219
connectedAddresses: SOLANA_CONNECTED_ACCOUNTS,
@@ -234,10 +231,18 @@ describe('MultichainRouter', () => {
234231
it('throws if address resolution returns an address that isnt available', async () => {
235232
const rootMessenger = getRootMultichainRouterMessenger();
236233
const messenger = getRestrictedMultichainRouterMessenger(rootMessenger);
234+
const withSnapKeyring = getMockWithSnapKeyring({
235+
// Simulate the Snap returning an unconnected address
236+
resolveAccountAddress: jest.fn().mockResolvedValue({
237+
address:
238+
'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:7S3P4HxJpyyigGzodYwHtCxZyUQe9JiBMHyRWXArAaKa',
239+
}),
240+
});
237241

238242
/* eslint-disable-next-line no-new */
239243
new MultichainRouter({
240244
messenger,
245+
withSnapKeyring,
241246
});
242247

243248
rootMessenger.registerActionHandler(
@@ -250,20 +255,6 @@ describe('MultichainRouter', () => {
250255
() => MOCK_SOLANA_SNAP_PERMISSIONS,
251256
);
252257

253-
rootMessenger.registerActionHandler(
254-
'SnapController:handleRequest',
255-
async ({ handler }) => {
256-
if (handler === HandlerType.OnKeyringRequest) {
257-
// Simulate the Snap returning an unconnected address
258-
return {
259-
address:
260-
'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:7S3P4HxJpyyigGzodYwHtCxZyUQe9JiBMHyRWXArAaKa',
261-
};
262-
}
263-
throw new Error('Unmocked request');
264-
},
265-
);
266-
267258
await expect(
268259
messenger.call('MultichainRouter:handleRequest', {
269260
connectedAddresses: SOLANA_CONNECTED_ACCOUNTS,
@@ -283,10 +274,12 @@ describe('MultichainRouter', () => {
283274
it('returns a set of both protocol and account Snap methods', async () => {
284275
const rootMessenger = getRootMultichainRouterMessenger();
285276
const messenger = getRestrictedMultichainRouterMessenger(rootMessenger);
277+
const withSnapKeyring = getMockWithSnapKeyring();
286278

287279
/* eslint-disable-next-line no-new */
288280
new MultichainRouter({
289281
messenger,
282+
withSnapKeyring,
290283
});
291284

292285
rootMessenger.registerActionHandler('SnapController:getAll', () => {
@@ -313,10 +306,12 @@ describe('MultichainRouter', () => {
313306
it('handles lack of protocol Snaps', async () => {
314307
const rootMessenger = getRootMultichainRouterMessenger();
315308
const messenger = getRestrictedMultichainRouterMessenger(rootMessenger);
309+
const withSnapKeyring = getMockWithSnapKeyring();
316310

317311
/* eslint-disable-next-line no-new */
318312
new MultichainRouter({
319313
messenger,
314+
withSnapKeyring,
320315
});
321316

322317
rootMessenger.registerActionHandler('SnapController:getAll', () => {
@@ -343,10 +338,12 @@ describe('MultichainRouter', () => {
343338
it('handles lack of account Snaps', async () => {
344339
const rootMessenger = getRootMultichainRouterMessenger();
345340
const messenger = getRestrictedMultichainRouterMessenger(rootMessenger);
341+
const withSnapKeyring = getMockWithSnapKeyring();
346342

347343
/* eslint-disable-next-line no-new */
348344
new MultichainRouter({
349345
messenger,
346+
withSnapKeyring,
350347
});
351348

352349
rootMessenger.registerActionHandler('SnapController:getAll', () => {

packages/snaps-controllers/src/multichain/MultichainRouter.ts

Lines changed: 37 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
SnapEndowments,
77
} from '@metamask/snaps-rpc-methods';
88
import type { Json, JsonRpcRequest, SnapId } from '@metamask/snaps-sdk';
9+
import type { Caip2ChainId } from '@metamask/snaps-utils';
910
import { HandlerType } from '@metamask/snaps-utils';
1011
import type { CaipAccountId, CaipChainId } from '@metamask/utils';
1112
import { hasProperty, parseCaipAccountId } from '@metamask/utils';
@@ -36,21 +37,25 @@ type InternalAccount = {
3637
};
3738
};
3839

40+
type SnapKeyring = {
41+
submitRequest: (request: Record<string, Json>) => Promise<Json>;
42+
resolveAccountAddress: (options: {
43+
snapId: SnapId;
44+
scope: Caip2ChainId;
45+
request: Json;
46+
}) => Promise<{ address: CaipAccountId } | null>;
47+
};
48+
49+
// Expecting a bound function that calls KeyringController.withKeyring selecting the Snap keyring
50+
type WithSnapKeyringFunction = <ReturnType>(
51+
operation: (keyring: SnapKeyring) => Promise<ReturnType>,
52+
) => Promise<ReturnType>;
53+
3954
export type AccountsControllerListMultichainAccountsAction = {
4055
type: `AccountsController:listMultichainAccounts`;
4156
handler: (chainId?: CaipChainId) => InternalAccount[];
4257
};
4358

44-
export type KeyringControllerSubmitNonEvmRequestAction = {
45-
type: `KeyringController:submitNonEvmRequest`;
46-
handler: (args: {
47-
address: string;
48-
method: string;
49-
params?: Json[] | Record<string, Json>;
50-
chainId: CaipChainId;
51-
}) => Promise<Json>;
52-
};
53-
5459
export type MultichainRouterActions =
5560
| MultichainRouterHandleRequestAction
5661
| MultichainRouterGetSupportedMethodsAction;
@@ -59,8 +64,7 @@ export type MultichainRouterAllowedActions =
5964
| GetAllSnaps
6065
| HandleSnapRequest
6166
| GetPermissions
62-
| AccountsControllerListMultichainAccountsAction
63-
| KeyringControllerSubmitNonEvmRequestAction;
67+
| AccountsControllerListMultichainAccountsAction;
6468

6569
export type MultichainRouterEvents = never;
6670

@@ -74,6 +78,7 @@ export type MultichainRouterMessenger = RestrictedControllerMessenger<
7478

7579
export type MultichainRouterArgs = {
7680
messenger: MultichainRouterMessenger;
81+
withSnapKeyring: WithSnapKeyringFunction;
7782
};
7883

7984
type ProtocolSnap = {
@@ -86,8 +91,11 @@ const name = 'MultichainRouter';
8691
export class MultichainRouter {
8792
#messenger: MultichainRouterMessenger;
8893

89-
constructor({ messenger }: MultichainRouterArgs) {
94+
#withSnapKeyring: WithSnapKeyringFunction;
95+
96+
constructor({ messenger, withSnapKeyring }: MultichainRouterArgs) {
9097
this.#messenger = messenger;
98+
this.#withSnapKeyring = withSnapKeyring;
9199

92100
this.#messenger.registerActionHandler(
93101
`${name}:handleRequest`,
@@ -107,20 +115,12 @@ export class MultichainRouter {
107115
) {
108116
try {
109117
// TODO: Decide if we should call this using another abstraction.
110-
const result = (await this.#messenger.call(
111-
'SnapController:handleRequest',
112-
{
118+
const result = (await this.#withSnapKeyring(async (keyring) =>
119+
keyring.resolveAccountAddress({
113120
snapId,
114-
origin: 'metamask',
115-
request: {
116-
method: 'keyring_resolveAccountAddress',
117-
params: {
118-
scope,
119-
request,
120-
},
121-
},
122-
handler: HandlerType.OnKeyringRequest,
123-
},
121+
request,
122+
scope,
123+
}),
124124
)) as { address: CaipAccountId } | null;
125125
const address = result?.address;
126126
return address ? parseCaipAccountId(address).address : null;
@@ -176,7 +176,7 @@ export class MultichainRouter {
176176
}
177177

178178
return {
179-
address: selectedAccount.address,
179+
accountId: selectedAccount.id,
180180
snapId: selectedAccount.metadata.snap.id,
181181
};
182182
}
@@ -239,12 +239,16 @@ export class MultichainRouter {
239239
);
240240
if (accountSnap) {
241241
// TODO: Decide on API for this.
242-
return this.#messenger.call('KeyringController:submitNonEvmRequest', {
243-
address: accountSnap.address,
244-
method,
245-
params,
246-
chainId: scope,
247-
});
242+
return this.#withSnapKeyring(async (keyring) =>
243+
keyring.submitRequest({
244+
id: accountSnap.accountId,
245+
scope,
246+
request: {
247+
method,
248+
params,
249+
},
250+
}),
251+
);
248252
}
249253

250254
// If the RPC request cannot be serviced by an account Snap,

packages/snaps-controllers/src/test-utils/controller.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -895,7 +895,6 @@ export const getRestrictedMultichainRouterMessenger = (
895895
'SnapController:getAll',
896896
'SnapController:handleRequest',
897897
'AccountsController:listMultichainAccounts',
898-
'KeyringController:submitNonEvmRequest',
899898
],
900899
});
901900

packages/snaps-controllers/src/test-utils/multichain.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,3 +87,25 @@ export const MOCK_SOLANA_SNAP_PERMISSIONS: Record<
8787
parentCapability: SnapEndowments.Protocol,
8888
},
8989
};
90+
91+
type MockSnapKeyring = {
92+
submitRequest: (request: unknown) => Promise<unknown>;
93+
resolveAccountAddress: (options: unknown) => Promise<unknown>;
94+
};
95+
96+
type MockOperationCallback = <ReturnType>(
97+
keyring: MockSnapKeyring,
98+
) => Promise<ReturnType>;
99+
100+
export const getMockWithSnapKeyring = (
101+
{ submitRequest = jest.fn(), resolveAccountAddress = jest.fn() } = {
102+
submitRequest: jest.fn(),
103+
resolveAccountAddress: jest.fn(),
104+
},
105+
) => {
106+
return async (callback: MockOperationCallback) =>
107+
callback({
108+
submitRequest,
109+
resolveAccountAddress,
110+
});
111+
};

0 commit comments

Comments
 (0)