Skip to content

Commit 7536fb5

Browse files
authored
feat: add discoverability methods (#127)
* add discoverability methods * update client definitions * extract types into a seperate file * update exports * linter fixes
1 parent 2692a45 commit 7536fb5

8 files changed

+534
-92
lines changed
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import type {
2+
GetGrantedExecutionPermissionsResult,
3+
MetaMaskExtensionClient,
4+
} from './erc7715Types';
5+
6+
/**
7+
* Retrieves all previously granted execution permissions from the wallet according to EIP-7715 specification.
8+
*
9+
* @param client - The client to use for the request.
10+
* @returns A promise that resolves to an array of granted permission responses.
11+
* @description
12+
* This function queries the wallet for all granted permissions that are not yet revoked.
13+
* Each permission response includes the chain ID, address, signer, permission details,
14+
* context, and dependency information.
15+
* @example
16+
* ```typescript
17+
* const grantedPermissions = await erc7715GetGrantedExecutionPermissionsAction(client);
18+
* // Returns an array of PermissionResponse objects
19+
* ```
20+
*/
21+
export async function erc7715GetGrantedExecutionPermissionsAction(
22+
client: MetaMaskExtensionClient,
23+
): Promise<GetGrantedExecutionPermissionsResult> {
24+
const result = await client.request(
25+
{
26+
method: 'wallet_getGrantedExecutionPermissions',
27+
params: [],
28+
},
29+
{ retryCount: 0 },
30+
);
31+
32+
if (!result) {
33+
throw new Error('Failed to get granted execution permissions');
34+
}
35+
36+
return result;
37+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import type {
2+
GetSupportedExecutionPermissionsResult,
3+
MetaMaskExtensionClient,
4+
} from './erc7715Types';
5+
6+
/**
7+
* Retrieves the supported execution permission types from the wallet according to EIP-7715 specification.
8+
*
9+
* @param client - The client to use for the request.
10+
* @returns A promise that resolves to a record of supported permission types with their chain IDs and rule types.
11+
* @description
12+
* This function queries the wallet for the permission types it supports.
13+
* The result is keyed by permission type and includes the supported chain IDs and rule types.
14+
* @example
15+
* ```typescript
16+
* const supported = await erc7715GetSupportedExecutionPermissionsAction(client);
17+
* // Returns:
18+
* // {
19+
* // "native-token-allowance": {
20+
* // "chainIds": ["0x1", "0x89"],
21+
* // "ruleTypes": ["expiry"]
22+
* // },
23+
* // "erc20-token-allowance": {
24+
* // "chainIds": ["0x1"],
25+
* // "ruleTypes": []
26+
* // }
27+
* // }
28+
* ```
29+
*/
30+
export async function erc7715GetSupportedExecutionPermissionsAction(
31+
client: MetaMaskExtensionClient,
32+
): Promise<GetSupportedExecutionPermissionsResult> {
33+
const result = await client.request(
34+
{
35+
method: 'wallet_getSupportedExecutionPermissions',
36+
params: [],
37+
},
38+
{ retryCount: 0 },
39+
);
40+
41+
if (!result) {
42+
throw new Error('Failed to get supported execution permissions');
43+
}
44+
45+
return result;
46+
}

packages/smart-accounts-kit/src/actions/erc7715RequestExecutionPermissionsAction.ts

Lines changed: 11 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -10,47 +10,19 @@ import type {
1010
Rule,
1111
Hex,
1212
} from '@metamask/7715-permission-types';
13-
import { isHex, toHex } from 'viem';
14-
import type {
15-
Client,
16-
Account,
17-
RpcSchema,
18-
Transport,
19-
Chain,
20-
Address,
21-
} from 'viem';
13+
import { toHex } from 'viem';
14+
import type { Address } from 'viem';
2215

23-
/**
24-
* RPC schema for MetaMask related methods.
25-
*
26-
* Extends the base RPC schema with methods specific to interacting with EIP-7715:
27-
* - `wallet_invokeSnap`: Invokes a method on a specific Snap.
28-
*/
29-
export type MetaMaskExtensionSchema = RpcSchema &
30-
[
31-
{
32-
// eslint-disable-next-line @typescript-eslint/naming-convention
33-
Method: 'wallet_requestExecutionPermissions';
34-
// eslint-disable-next-line @typescript-eslint/naming-convention
35-
Params: PermissionRequest<PermissionTypes>[];
36-
// eslint-disable-next-line @typescript-eslint/naming-convention
37-
ReturnType: PermissionResponse<PermissionTypes>[];
38-
},
39-
];
16+
import { isDefined, toHexOrThrow } from '../utils';
17+
import type { MetaMaskExtensionClient } from './erc7715Types';
4018

41-
/**
42-
* A Viem client extended with MetaMask Snap-specific RPC methods.
43-
*
44-
* This client type allows for interaction with MetaMask Snaps through
45-
* the standard Viem client interface, with added type safety for
46-
* Snap-specific methods.
47-
*/
48-
export type MetaMaskExtensionClient = Client<
49-
Transport,
50-
Chain | undefined,
51-
Account | undefined,
52-
MetaMaskExtensionSchema
53-
>;
19+
export type {
20+
GetGrantedExecutionPermissionsResult,
21+
GetSupportedExecutionPermissionsResult,
22+
MetaMaskExtensionClient,
23+
MetaMaskExtensionSchema,
24+
SupportedPermissionInfo,
25+
} from './erc7715Types';
5426

5527
type PermissionParameter = {
5628
type: string;
@@ -241,59 +213,6 @@ function formatPermissionsRequest(
241213
};
242214
}
243215

244-
/**
245-
* Checks if a value is defined (not null or undefined).
246-
*
247-
* @param value - The value to check.
248-
* @returns A boolean indicating whether the value is defined.
249-
*/
250-
function isDefined<TValue>(value: TValue | null | undefined): value is TValue {
251-
return value !== undefined && value !== null;
252-
}
253-
254-
/**
255-
* Asserts that a value is defined (not null or undefined).
256-
*
257-
* @param value - The value to check.
258-
* @param parameterName - Optional: The name of the parameter that is being checked.
259-
* @throws {Error} If the value is null or undefined.
260-
*/
261-
function assertIsDefined<TValue>(
262-
value: TValue | null | undefined,
263-
parameterName?: string,
264-
): asserts value is TValue {
265-
if (!isDefined(value)) {
266-
throw new Error(
267-
`Invalid parameters: ${parameterName ?? 'value'} is required`,
268-
);
269-
}
270-
}
271-
272-
/**
273-
* Converts a value to a hex string or throws an error if the value is invalid.
274-
*
275-
* @param value - The value to convert to hex.
276-
* @param parameterName - Optional: The name of the parameter that is being converted to hex.
277-
* @returns The value as a hex string.
278-
*/
279-
function toHexOrThrow(
280-
value: Parameters<typeof toHex>[0] | undefined,
281-
parameterName?: string,
282-
) {
283-
assertIsDefined(value, parameterName);
284-
285-
if (typeof value === 'string') {
286-
if (!isHex(value)) {
287-
throw new Error(
288-
`Invalid parameters: ${parameterName ?? 'value'} is not a valid hex value`,
289-
);
290-
}
291-
return value;
292-
}
293-
294-
return toHex(value);
295-
}
296-
297216
type PermissionFormatter = (params: {
298217
permission: PermissionParameter;
299218
isAdjustmentAllowed: boolean;
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import type {
2+
PermissionTypes,
3+
PermissionRequest,
4+
PermissionResponse,
5+
} from '@metamask/7715-permission-types';
6+
import type { Client, Account, RpcSchema, Transport, Chain } from 'viem';
7+
8+
/**
9+
* Represents the supported execution permissions for a specific permission type.
10+
*/
11+
export type SupportedPermissionInfo = {
12+
chainIds: `0x${string}`[];
13+
ruleTypes: string[];
14+
};
15+
16+
/**
17+
* Result type for the getSupportedExecutionPermissions action.
18+
* A record keyed by permission type containing supported chain IDs and rule types.
19+
*/
20+
export type GetSupportedExecutionPermissionsResult = Record<
21+
string,
22+
SupportedPermissionInfo
23+
>;
24+
25+
/**
26+
* Result type for the getGrantedExecutionPermissions action.
27+
* An array of permission responses representing all granted permissions that are not yet revoked.
28+
*/
29+
export type GetGrantedExecutionPermissionsResult =
30+
PermissionResponse<PermissionTypes>[];
31+
32+
/**
33+
* RPC schema for ERC-7715 execution permission methods.
34+
*
35+
* Extends the base RPC schema with methods specific to interacting with EIP-7715:
36+
* - `wallet_requestExecutionPermissions`: Requests execution permissions from the wallet.
37+
* - `wallet_getSupportedExecutionPermissions`: Gets supported permission types.
38+
* - `wallet_getGrantedExecutionPermissions`: Gets all granted permissions.
39+
*/
40+
/* eslint-disable @typescript-eslint/naming-convention */
41+
export type MetaMaskExtensionSchema = RpcSchema &
42+
[
43+
{
44+
Method: 'wallet_requestExecutionPermissions';
45+
Params: PermissionRequest<PermissionTypes>[];
46+
ReturnType: PermissionResponse<PermissionTypes>[];
47+
},
48+
{
49+
Method: 'wallet_getSupportedExecutionPermissions';
50+
Params: [];
51+
ReturnType: GetSupportedExecutionPermissionsResult;
52+
},
53+
{
54+
Method: 'wallet_getGrantedExecutionPermissions';
55+
Params: [];
56+
ReturnType: GetGrantedExecutionPermissionsResult;
57+
},
58+
];
59+
/* eslint-enable @typescript-eslint/naming-convention */
60+
61+
/**
62+
* A Viem client extended with ERC-7715 execution permission RPC methods.
63+
*
64+
* This client type allows for interaction with wallets that support ERC-7715
65+
* through the standard Viem client interface, with added type safety for
66+
* execution permission methods.
67+
*/
68+
export type MetaMaskExtensionClient = Client<
69+
Transport,
70+
Chain | undefined,
71+
Account | undefined,
72+
MetaMaskExtensionSchema
73+
>;

packages/smart-accounts-kit/src/actions/index.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import {
99
sendTransactionWithDelegationAction,
1010
sendUserOperationWithDelegationAction,
1111
} from './erc7710RedeemDelegationAction';
12+
import { erc7715GetGrantedExecutionPermissionsAction } from './erc7715GetGrantedExecutionPermissionsAction';
13+
import { erc7715GetSupportedExecutionPermissionsAction } from './erc7715GetSupportedExecutionPermissionsAction';
1214
import { erc7715RequestExecutionPermissionsAction } from './erc7715RequestExecutionPermissionsAction';
1315
import type {
1416
MetaMaskExtensionClient,
@@ -56,6 +58,16 @@ export {
5658
type RequestExecutionPermissionsReturnType,
5759
} from './erc7715RequestExecutionPermissionsAction';
5860

61+
export { erc7715GetSupportedExecutionPermissionsAction as getSupportedExecutionPermissions } from './erc7715GetSupportedExecutionPermissionsAction';
62+
63+
export { erc7715GetGrantedExecutionPermissionsAction as getGrantedExecutionPermissions } from './erc7715GetGrantedExecutionPermissionsAction';
64+
65+
export {
66+
type GetSupportedExecutionPermissionsResult,
67+
type GetGrantedExecutionPermissionsResult,
68+
type SupportedPermissionInfo,
69+
} from './erc7715Types';
70+
5971
export type { DelegatedCall } from './erc7710RedeemDelegationAction';
6072

6173
export const erc7715ProviderActions = () => (client: Client) => ({
@@ -67,6 +79,16 @@ export const erc7715ProviderActions = () => (client: Client) => ({
6779
parameters,
6880
);
6981
},
82+
getSupportedExecutionPermissions: async () => {
83+
return erc7715GetSupportedExecutionPermissionsAction(
84+
client as MetaMaskExtensionClient,
85+
);
86+
},
87+
getGrantedExecutionPermissions: async () => {
88+
return erc7715GetGrantedExecutionPermissionsAction(
89+
client as MetaMaskExtensionClient,
90+
);
91+
},
7092
});
7193

7294
export const erc7710WalletActions = () => (client: WalletClient) => ({

packages/smart-accounts-kit/src/utils.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,58 @@ export const hasProperties = <
7474
(prop) => prop in object && object[prop] !== undefined,
7575
);
7676
};
77+
78+
/**
79+
* Checks if a value is defined (not null or undefined).
80+
*
81+
* @param value - The value to check.
82+
* @returns A boolean indicating whether the value is defined.
83+
*/
84+
export function isDefined<TValue>(
85+
value: TValue | null | undefined,
86+
): value is TValue {
87+
return value !== undefined && value !== null;
88+
}
89+
90+
/**
91+
* Asserts that a value is defined (not null or undefined).
92+
*
93+
* @param value - The value to check.
94+
* @param parameterName - Optional: The name of the parameter that is being checked.
95+
* @throws {Error} If the value is null or undefined.
96+
*/
97+
export function assertIsDefined<TValue>(
98+
value: TValue | null | undefined,
99+
parameterName?: string,
100+
): asserts value is TValue {
101+
if (!isDefined(value)) {
102+
throw new Error(
103+
`Invalid parameters: ${parameterName ?? 'value'} is required`,
104+
);
105+
}
106+
}
107+
108+
/**
109+
* Converts a value to a hex string or throws an error if the value is invalid.
110+
*
111+
* @param value - The value to convert to hex.
112+
* @param parameterName - Optional: The name of the parameter that is being converted to hex.
113+
* @returns The value as a hex string.
114+
*/
115+
export function toHexOrThrow(
116+
value: Parameters<typeof toHex>[0] | undefined,
117+
parameterName?: string,
118+
): Hex {
119+
assertIsDefined(value, parameterName);
120+
121+
if (typeof value === 'string') {
122+
if (!isHex(value)) {
123+
throw new Error(
124+
`Invalid parameters: ${parameterName ?? 'value'} is not a valid hex value`,
125+
);
126+
}
127+
return value;
128+
}
129+
130+
return toHex(value);
131+
}

0 commit comments

Comments
 (0)