Skip to content

Commit 66cc787

Browse files
committed
Support calling wallet_requestExecutionPermissions directly on the walletApi
1 parent a22ecb5 commit 66cc787

4 files changed

Lines changed: 536 additions & 367 deletions

File tree

packages/delegation-toolkit/src/experimental/erc7715RequestExecutionPermissionsAction.ts

Lines changed: 68 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import type {
1111
} from '@metamask/7715-permission-types';
1212
import { isHex, toHex, type Address } from 'viem';
1313

14-
import type { SnapClient } from './snapsAuthorization.js';
14+
import type { MetaMaskExtensionClient as MetaMaskExtensionClient } from './snapsAuthorization.js';
1515

1616
type PermissionParameter = {
1717
type: string;
@@ -119,44 +119,93 @@ export type RequestExecutionPermissionsReturnType = PermissionResponse<
119119
PermissionTypes
120120
>[];
121121

122+
function getRequestExecutionPermissionsRequestParams(params: {
123+
kernelSnapId: string | undefined;
124+
permissionRequest: PermissionRequest<AccountSigner, PermissionTypes>[];
125+
}):
126+
| {
127+
method: 'wallet_invokeSnap';
128+
params: {
129+
snapId: string;
130+
request: {
131+
method: 'wallet_requestExecutionPermissions';
132+
params: PermissionRequest<AccountSigner, PermissionTypes>[];
133+
};
134+
};
135+
}
136+
| {
137+
method: 'wallet_requestExecutionPermissions';
138+
params: PermissionRequest<AccountSigner, PermissionTypes>[];
139+
} {
140+
if (params.kernelSnapId) {
141+
return {
142+
method: 'wallet_invokeSnap',
143+
params: {
144+
snapId: params.kernelSnapId,
145+
request: {
146+
method: 'wallet_requestExecutionPermissions',
147+
params: params.permissionRequest,
148+
},
149+
},
150+
};
151+
}
152+
153+
return {
154+
method: 'wallet_requestExecutionPermissions',
155+
params: params.permissionRequest,
156+
};
157+
}
158+
122159
/**
123160
* Grants permissions according to EIP-7715 specification.
124161
*
125162
* @template Signer - The type of the signer, either an Address or Account.
126163
* @param client - The client to use for the request.
127164
* @param parameters - The permissions requests to grant.
128-
* @param kernelSnapId - The ID of the kernel snap to invoke, defaults to 'npm:@metamask/permissions-kernel-snap'.
165+
* @param kernelSnapId - The ID of the kernel snap to invoke, undefined to request via the wallet RPC method.
129166
* @returns A promise that resolves to the permission responses.
130167
* @description
131168
* This function formats the permissions requests and invokes the wallet snap to grant permissions.
132169
* It will throw an error if the permissions could not be granted.
133170
*/
134171
export async function erc7715RequestExecutionPermissionsAction(
135-
client: SnapClient,
172+
client: MetaMaskExtensionClient,
136173
parameters: RequestExecutionPermissionsParameters,
137-
kernelSnapId = 'npm:@metamask/permissions-kernel-snap',
174+
kernelSnapId: string | undefined,
138175
): Promise<RequestExecutionPermissionsReturnType> {
139-
const formattedParameters = parameters.map(formatPermissionsRequest);
140-
141-
const result = await client.request(
142-
{
143-
method: 'wallet_invokeSnap',
144-
params: {
145-
snapId: kernelSnapId,
146-
request: {
147-
method: 'wallet_requestExecutionPermissions',
148-
params: formattedParameters,
176+
const formattedPermissionRequest = parameters.map(formatPermissionsRequest);
177+
178+
let result: PermissionResponse<AccountSigner, PermissionTypes>[] | null;
179+
180+
if (kernelSnapId) {
181+
result = await client.request(
182+
{
183+
method: 'wallet_invokeSnap',
184+
params: {
185+
snapId: kernelSnapId,
186+
request: {
187+
method: 'wallet_requestExecutionPermissions',
188+
params: formattedPermissionRequest,
189+
},
149190
},
150191
},
151-
},
152-
{ retryCount: 0 },
153-
);
192+
{ retryCount: 0 },
193+
);
194+
} else {
195+
result = await client.request(
196+
{
197+
method: 'wallet_requestExecutionPermissions',
198+
params: formattedPermissionRequest,
199+
},
200+
{ retryCount: 0 },
201+
);
202+
}
154203

155-
if (result === null) {
204+
if (!result) {
156205
throw new Error('Failed to grant permissions');
157206
}
158207

159-
return result as any as RequestExecutionPermissionsReturnType;
208+
return result;
160209
}
161210

162211
/**

packages/delegation-toolkit/src/experimental/index.ts

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
import { erc7715RequestExecutionPermissionsAction } from './erc7715RequestExecutionPermissionsAction';
1313
import type { RequestExecutionPermissionsParameters } from './erc7715RequestExecutionPermissionsAction';
1414
import { ensureSnapsAuthorized } from './snapsAuthorization';
15-
import type { SnapClient } from './snapsAuthorization';
15+
import type { MetaMaskExtensionClient } from './snapsAuthorization';
1616

1717
export {
1818
erc7715RequestExecutionPermissionsAction as requestExecutionPermissions,
@@ -27,20 +27,48 @@ export {
2727
type DelegationStorageConfig,
2828
} from './delegationStorage';
2929

30+
type erc7715ProviderConfig = {
31+
useWalletMethod: boolean;
32+
snapIds?: {
33+
kernelSnapId: string;
34+
providerSnapId: string;
35+
};
36+
};
37+
3038
export const erc7715ProviderActions =
31-
(snapIds?: { kernelSnapId: string; providerSnapId: string }) =>
39+
(erc7715ProviderConfig: erc7715ProviderConfig = { useWalletMethod: false }) =>
3240
(client: Client) => ({
3341
requestExecutionPermissions: async (
3442
parameters: RequestExecutionPermissionsParameters,
3543
) => {
36-
if (!(await ensureSnapsAuthorized(client as SnapClient, snapIds))) {
37-
throw new Error('Snaps not authorized');
44+
const { useWalletMethod, snapIds } = erc7715ProviderConfig;
45+
46+
let kernelSnapId: string | undefined = undefined;
47+
48+
if (!useWalletMethod) {
49+
kernelSnapId =
50+
snapIds?.kernelSnapId || 'npm:@metamask/permissions-kernel-snap';
51+
52+
const snapIdsToAuthorize = {
53+
kernelSnapId,
54+
providerSnapId:
55+
snapIds?.providerSnapId || 'npm:@metamask/gator-permissions-snap',
56+
};
57+
58+
if (
59+
!(await ensureSnapsAuthorized(
60+
client as MetaMaskExtensionClient,
61+
snapIdsToAuthorize,
62+
))
63+
) {
64+
throw new Error('Snaps not authorized');
65+
}
3866
}
3967

4068
return erc7715RequestExecutionPermissionsAction(
41-
client as SnapClient,
69+
client as MetaMaskExtensionClient,
4270
parameters,
43-
snapIds?.kernelSnapId,
71+
kernelSnapId,
4472
);
4573
},
4674
});

packages/delegation-toolkit/src/experimental/snapsAuthorization.ts

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
import {
2+
AccountSigner,
3+
PermissionRequest,
4+
PermissionResponse,
5+
PermissionTypes,
6+
} from '@metamask/7715-permission-types';
17
import type { Account, Chain, Client, RpcSchema, Transport } from 'viem';
28

39
/**
@@ -14,14 +20,14 @@ export type SnapAuthorizations = Record<
1420
>;
1521

1622
/**
17-
* RPC schema for MetaMask Snap-related methods.
23+
* RPC schema for MetaMask related methods.
1824
*
1925
* Extends the base RPC schema with methods specific to interacting with Snaps:
2026
* - `wallet_invokeSnap`: Invokes a method on a specific Snap.
2127
* - `wallet_getSnaps`: Retrieves all installed Snaps and their authorization status.
2228
* - `wallet_requestSnaps`: Requests permission to use specific Snaps.
2329
*/
24-
export type SnapRpcSchema = RpcSchema &
30+
export type MetaMaskExtensionSchema = RpcSchema &
2531
[
2632
{
2733
// eslint-disable-next-line @typescript-eslint/naming-convention
@@ -53,6 +59,14 @@ export type SnapRpcSchema = RpcSchema &
5359
// eslint-disable-next-line @typescript-eslint/naming-convention
5460
ReturnType: SnapAuthorizations;
5561
},
62+
{
63+
// eslint-disable-next-line @typescript-eslint/naming-convention
64+
Method: 'wallet_requestExecutionPermissions';
65+
// eslint-disable-next-line @typescript-eslint/naming-convention
66+
Params: PermissionRequest<AccountSigner, PermissionTypes>[];
67+
// eslint-disable-next-line @typescript-eslint/naming-convention
68+
ReturnType: PermissionResponse<AccountSigner, PermissionTypes>[];
69+
},
5670
];
5771

5872
/**
@@ -62,11 +76,11 @@ export type SnapRpcSchema = RpcSchema &
6276
* the standard Viem client interface, with added type safety for
6377
* Snap-specific methods.
6478
*/
65-
export type SnapClient = Client<
79+
export type MetaMaskExtensionClient = Client<
6680
Transport,
6781
Chain | undefined,
6882
Account | undefined,
69-
SnapRpcSchema
83+
MetaMaskExtensionSchema
7084
>;
7185

7286
/**
@@ -94,7 +108,7 @@ const isSnapAuthorized = (
94108
* @param snapId - The ID of the snap to re-authorize.
95109
* @returns A promise that resolves to a boolean indicating whether the snap was re-authorized.
96110
*/
97-
const reAuthorize = async (client: SnapClient, snapId: string) => {
111+
const reAuthorize = async (client: MetaMaskExtensionClient, snapId: string) => {
98112
const newAuthorizations = await client.request({
99113
method: 'wallet_requestSnaps',
100114
params: {
@@ -118,14 +132,12 @@ const reAuthorize = async (client: SnapClient, snapId: string) => {
118132
* It returns true only if both snaps are successfully authorized.
119133
*/
120134
export async function ensureSnapsAuthorized(
121-
client: SnapClient,
122-
snapIds?: { kernelSnapId: string; providerSnapId: string },
135+
client: MetaMaskExtensionClient,
136+
{
137+
kernelSnapId,
138+
providerSnapId,
139+
}: { kernelSnapId: string; providerSnapId: string },
123140
) {
124-
const kernelSnapId =
125-
snapIds?.kernelSnapId ?? 'npm:@metamask/permissions-kernel-snap';
126-
const providerSnapId =
127-
snapIds?.providerSnapId ?? 'npm:@metamask/gator-permissions-snap';
128-
129141
const existingAuthorizations = await client.request({
130142
method: 'wallet_getSnaps',
131143
params: {} as Record<string, never>,

0 commit comments

Comments
 (0)