Skip to content

Commit 9a18557

Browse files
authored
Merge pull request #46 from MetaMask/chore/override-is-deployed-function2
Override isDeployed For 7702 Implementation
2 parents 4797b61 + b979419 commit 9a18557

5 files changed

Lines changed: 661 additions & 0 deletions

File tree

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,5 @@ export {
2424
type PeriodTransferResult,
2525
type StreamingResult,
2626
} from './getCaveatAvailableAmount';
27+
28+
export { isValid7702Implementation } from './isValid7702Implementation';
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import type { Client, Address, Hex } from 'viem';
2+
import { isAddressEqual } from 'viem';
3+
import { getCode } from 'viem/actions';
4+
5+
import type { DeleGatorEnvironment } from '../types';
6+
7+
// EIP-7702 delegation prefix (0xef0100)
8+
const DELEGATION_PREFIX = '0xef0100' as const;
9+
10+
/**
11+
* Parameters for checking if an account is delegated to the EIP-7702 implementation.
12+
*/
13+
export type IsValid7702ImplementationParameters = {
14+
/** The client to use for the query. */
15+
client: Client;
16+
/** The address to check for proper delegation. */
17+
accountAddress: Address;
18+
/** The DeleGator environment containing contract addresses. */
19+
environment: DeleGatorEnvironment;
20+
};
21+
22+
/**
23+
* Extracts the delegated contract address from EIP-7702 delegation code.
24+
*
25+
* @param code - The code returned from getCode for a delegated account.
26+
* @returns The delegated contract address or null if not a valid delegation.
27+
*/
28+
function extractDelegatedAddress(code: Hex | undefined): Address | null {
29+
if (code?.length !== 48) {
30+
// 0x (2 chars) + ef0100 (6 chars) + address (40 chars) = 48 chars
31+
return null;
32+
}
33+
34+
if (!code.toLowerCase().startsWith(DELEGATION_PREFIX.toLowerCase())) {
35+
return null;
36+
}
37+
38+
// Extract the 20-byte address after the delegation prefix
39+
const addressHex = code.slice(8); // Remove '0xef0100' prefix (8 chars)
40+
return `0x${addressHex}`;
41+
}
42+
43+
/**
44+
* Checks if an account is properly delegated to the EIP-7702 implementation.
45+
*
46+
* This function validates EIP-7702 delegations by checking if the EOA has a 7702
47+
* contract assigned to it and comparing the delegated address against the 7702
48+
* implementation found in the environment.
49+
*
50+
* @param params - The parameters for checking the delegation.
51+
* @param params.client - The client to use for the query.
52+
* @param params.accountAddress - The address to check for proper delegation.
53+
* @param params.environment - The DeleGator environment containing contract addresses.
54+
* @returns A promise that resolves to true if the account is properly delegated to the 7702 implementation, false otherwise.
55+
* @example
56+
* ```typescript
57+
* const isValid = await isValid7702Implementation({
58+
* client: publicClient,
59+
* accountAddress: '0x...',
60+
* environment: delegatorEnvironment,
61+
* });
62+
*
63+
* if (isValid) {
64+
* console.log('Account is properly delegated to EIP-7702 implementation');
65+
* } else {
66+
* console.log('Account is not properly delegated');
67+
* }
68+
* ```
69+
*/
70+
export async function isValid7702Implementation({
71+
client,
72+
accountAddress,
73+
environment,
74+
}: IsValid7702ImplementationParameters): Promise<boolean> {
75+
try {
76+
// Get the code at the account address
77+
const code = await getCode(client, {
78+
address: accountAddress,
79+
});
80+
81+
// Extract the delegated contract address from the EIP-7702 delegation code
82+
const delegatedAddress = extractDelegatedAddress(code);
83+
84+
// If no valid delegation found, return false
85+
if (!delegatedAddress) {
86+
return false;
87+
}
88+
89+
// Compare the delegated address with the 7702 implementation in the environment
90+
const expectedImplementation =
91+
environment.implementations.EIP7702StatelessDeleGatorImpl;
92+
if (!expectedImplementation) {
93+
return false;
94+
}
95+
96+
return isAddressEqual(delegatedAddress, expectedImplementation);
97+
} catch (error) {
98+
// If the call fails (e.g., no code at address, network error),
99+
// then it's not properly delegated to our implementation
100+
return false;
101+
}
102+
}

packages/delegation-toolkit/src/toMetaMaskSmartAccount.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
toSmartAccount,
1111
} from 'viem/account-abstraction';
1212

13+
import { isValid7702Implementation } from './actions/isValid7702Implementation';
1314
import { Implementation } from './constants';
1415
import { getCounterfactualAccountData } from './counterfactualAccountData';
1516
import {
@@ -197,5 +198,19 @@ export async function toMetaMaskSmartAccount<
197198
...signatory,
198199
});
199200

201+
// Override isDeployed only for EIP-7702 implementation to check proper delegation code
202+
if (implementation === Implementation.Stateless7702) {
203+
return {
204+
...smartAccount,
205+
isDeployed: async () =>
206+
isValid7702Implementation({
207+
client,
208+
accountAddress: address,
209+
environment,
210+
}),
211+
};
212+
}
213+
214+
// For other implementations, use the default isDeployed behavior
200215
return smartAccount;
201216
}

0 commit comments

Comments
 (0)