|
| 1 | +/** |
| 2 | + * Module management actions for ERC-7579 smart accounts |
| 3 | + */ |
| 4 | + |
| 5 | +import type { Address, Hex, PublicClient } from "viem"; |
| 6 | + |
| 7 | +/** |
| 8 | + * ERC-7579 module type IDs |
| 9 | + */ |
| 10 | +export const ModuleType = { |
| 11 | + VALIDATOR: 1n, |
| 12 | + EXECUTOR: 2n, |
| 13 | + FALLBACK: 3n, |
| 14 | + HOOK: 4n, |
| 15 | +} as const; |
| 16 | + |
| 17 | +/** |
| 18 | + * Minimal ABI for isModuleInstalled function on ERC-7579 accounts |
| 19 | + */ |
| 20 | +const IERC7579AccountAbi = [ |
| 21 | + { |
| 22 | + type: "function", |
| 23 | + name: "isModuleInstalled", |
| 24 | + inputs: [ |
| 25 | + { name: "moduleTypeId", type: "uint256", internalType: "uint256" }, |
| 26 | + { name: "module", type: "address", internalType: "address" }, |
| 27 | + { name: "additionalContext", type: "bytes", internalType: "bytes" }, |
| 28 | + ], |
| 29 | + outputs: [{ name: "", type: "bool", internalType: "bool" }], |
| 30 | + stateMutability: "view", |
| 31 | + }, |
| 32 | +] as const; |
| 33 | + |
| 34 | +export type IsModuleInstalledParams = { |
| 35 | + /** Public client for reading contract state */ |
| 36 | + client: PublicClient; |
| 37 | + /** The smart account address to check */ |
| 38 | + accountAddress: Address; |
| 39 | + /** The module address to check for installation */ |
| 40 | + moduleAddress: Address; |
| 41 | + /** The module type ID (use ModuleType constants) */ |
| 42 | + moduleTypeId: bigint; |
| 43 | + /** Optional additional context for module lookup (usually empty) */ |
| 44 | + additionalContext?: Hex; |
| 45 | +}; |
| 46 | + |
| 47 | +export type IsModuleInstalledResult = { |
| 48 | + /** Whether the module is installed on the account */ |
| 49 | + isInstalled: boolean; |
| 50 | +}; |
| 51 | + |
| 52 | +/** |
| 53 | + * Check if a module is installed on an ERC-7579 smart account |
| 54 | + * |
| 55 | + * @example |
| 56 | + * ```typescript |
| 57 | + * const result = await isModuleInstalled({ |
| 58 | + * client: publicClient, |
| 59 | + * accountAddress: "0x...", |
| 60 | + * moduleAddress: "0x...", |
| 61 | + * moduleTypeId: ModuleType.EXECUTOR, |
| 62 | + * }); |
| 63 | + * console.log("Guardian module installed:", result.isInstalled); |
| 64 | + * ``` |
| 65 | + */ |
| 66 | +export async function isModuleInstalled( |
| 67 | + params: IsModuleInstalledParams, |
| 68 | +): Promise<IsModuleInstalledResult> { |
| 69 | + const { |
| 70 | + client, |
| 71 | + accountAddress, |
| 72 | + moduleAddress, |
| 73 | + moduleTypeId, |
| 74 | + additionalContext = "0x", |
| 75 | + } = params; |
| 76 | + |
| 77 | + const isInstalled = await client.readContract({ |
| 78 | + address: accountAddress, |
| 79 | + abi: IERC7579AccountAbi, |
| 80 | + functionName: "isModuleInstalled", |
| 81 | + args: [moduleTypeId, moduleAddress, additionalContext], |
| 82 | + }); |
| 83 | + |
| 84 | + return { isInstalled }; |
| 85 | +} |
| 86 | + |
| 87 | +/** |
| 88 | + * Check if the guardian executor module is installed on an account |
| 89 | + * |
| 90 | + * @example |
| 91 | + * ```typescript |
| 92 | + * const result = await isGuardianModuleInstalled({ |
| 93 | + * client: publicClient, |
| 94 | + * accountAddress: "0x...", |
| 95 | + * guardianExecutorAddress: "0x...", |
| 96 | + * }); |
| 97 | + * if (!result.isInstalled) { |
| 98 | + * console.warn("Guardian module not installed on account"); |
| 99 | + * } |
| 100 | + * ``` |
| 101 | + */ |
| 102 | +export async function isGuardianModuleInstalled(params: { |
| 103 | + client: PublicClient; |
| 104 | + accountAddress: Address; |
| 105 | + guardianExecutorAddress: Address; |
| 106 | +}): Promise<IsModuleInstalledResult> { |
| 107 | + return isModuleInstalled({ |
| 108 | + ...params, |
| 109 | + moduleAddress: params.guardianExecutorAddress, |
| 110 | + moduleTypeId: ModuleType.EXECUTOR, |
| 111 | + }); |
| 112 | +} |
0 commit comments