Skip to content

Commit 9883172

Browse files
committed
feat: add more debugging options from local test
also address code revie comments
1 parent 6d87237 commit 9883172

File tree

8 files changed

+210
-10
lines changed

8 files changed

+210
-10
lines changed

examples/demo-app/scripts/deploy-msa-anvil.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ cast send "$PAYMASTER" --value 10ether --private-key "$ANVIL_ACCOUNT_0_KEY" --rp
6060
echo "💳 Depositing 10 ETH into EntryPoint for paymaster..."
6161
cast send "$PAYMASTER" "deposit()" --value 10ether --private-key "$ANVIL_ACCOUNT_0_KEY" --rpc-url "$RPC_URL" 2>&1 || echo "Deposit initiated"
6262

63+
# Add stake to the paymaster (required for ERC-4337)
64+
echo "🔒 Adding stake to paymaster (1 day unlock delay)..."
65+
cast send "$PAYMASTER" "addStake(uint32)" 86400 --value 1ether --private-key "$ANVIL_ACCOUNT_0_KEY" --rpc-url "$RPC_URL" 2>&1 || echo "Stake added"
66+
6367
# Verify all addresses were extracted
6468
if [ -z "$EOA_VALIDATOR" ] || [ -z "$SESSION_VALIDATOR" ] || [ -z "$WEBAUTHN_VALIDATOR" ] || \
6569
[ -z "$GUARDIAN_EXECUTOR" ] || [ -z "$ACCOUNT_IMPL" ] || [ -z "$BEACON" ] || [ -z "$FACTORY" ] || [ -z "$PAYMASTER" ]; then
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/**
2+
* Composable for checking if the guardian module is installed on an account
3+
*/
4+
5+
import type { Address } from "viem";
6+
import { isGuardianModuleInstalled } from "zksync-sso-4337";
7+
8+
export const useGuardianModuleCheck = () => {
9+
const { getPublicClient, defaultChain, contractsByChain } = useClientStore();
10+
const contracts = contractsByChain[defaultChain!.id];
11+
12+
const checkInProgress = ref(false);
13+
const checkError = ref<Error | null>(null);
14+
const isInstalled = ref<boolean | null>(null);
15+
16+
/**
17+
* Check if the guardian executor module is installed on the given account
18+
*
19+
* This is useful for verifying that accounts deployed on testnet have the
20+
* guardian recovery module properly configured.
21+
*
22+
* @param accountAddress - The smart account address to check
23+
* @returns Promise that resolves to true if module is installed, false otherwise
24+
*/
25+
async function checkGuardianModuleInstalled(accountAddress: Address): Promise<boolean> {
26+
checkInProgress.value = true;
27+
checkError.value = null;
28+
isInstalled.value = null;
29+
30+
try {
31+
if (!contracts.guardianExecutor) {
32+
throw new Error("GuardianExecutor contract address not configured for this chain");
33+
}
34+
35+
const client = getPublicClient({ chainId: defaultChain.id });
36+
37+
const result = await isGuardianModuleInstalled({
38+
client,
39+
accountAddress,
40+
guardianExecutorAddress: contracts.guardianExecutor,
41+
});
42+
43+
isInstalled.value = result.isInstalled;
44+
return result.isInstalled;
45+
} catch (err) {
46+
const error = err instanceof Error ? err : new Error(String(err));
47+
checkError.value = error;
48+
throw error;
49+
} finally {
50+
checkInProgress.value = false;
51+
}
52+
}
53+
54+
return {
55+
checkGuardianModuleInstalled,
56+
checkInProgress: readonly(checkInProgress),
57+
checkError: readonly(checkError),
58+
isInstalled: readonly(isInstalled),
59+
};
60+
};

packages/auth-server/composables/useRecoveryGuardian.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,18 @@ export const useRecoveryGuardian = () => {
273273
let recoveryData: Hex;
274274

275275
if (recoveryType === RecoveryType.WebAuthn) {
276+
// Validate inputs before encoding
277+
if (!credentialId || credentialId.trim().length === 0) {
278+
throw new Error("credentialId cannot be empty");
279+
}
280+
276281
const publicKeyBytes = getPublicKeyBytesFromPasskeySignature(credentialPublicKey);
282+
283+
// Validate that public key coordinates are valid 32-byte values
284+
if (publicKeyBytes[0].length !== 32 || publicKeyBytes[1].length !== 32) {
285+
throw new Error("Invalid public key coordinates: must be 32 bytes each");
286+
}
287+
277288
const publicKeyHex = [
278289
pad(`0x${publicKeyBytes[0].toString("hex")}`),
279290
pad(`0x${publicKeyBytes[1].toString("hex")}`),

packages/auth-server/pages/recovery/guardian/(actions)/confirm-guardian.vue

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ import { AddressSchema } from "@/utils/schemas";
108108
const accountData = useAppKitAccount();
109109
const route = useRoute();
110110
const { confirmGuardian, confirmGuardianInProgress, getGuardians, getGuardiansData } = useRecoveryGuardian();
111+
const { verifyAccountSetup } = useDebugGuardian();
111112
const { getConfigurableAccount, getConfigurableAccountInProgress } = useConfigurableAccount();
112113
const { getWalletClient, defaultChain } = useClientStore();
113114
const { isSsoAccount: checkIsSsoAccount, isLoading: isSsoAccountLoading, error: isSsoAccountError } = useIsSsoAccount();
@@ -176,6 +177,11 @@ const status = computed(() => {
176177
const confirmGuardianAction = async () => {
177178
try {
178179
confirmGuardianError.value = null;
180+
181+
// Debug: Verify account setup before attempting confirmation
182+
console.log("=== Starting Guardian Confirmation Debug ===");
183+
await verifyAccountSetup(accountAddress.value);
184+
179185
let client;
180186
181187
if (isSsoAccount.value) {

packages/auth-server/stores/local-node.json

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@
22
"rpcUrl": "http://localhost:8545",
33
"chainId": 1337,
44
"deployer": "0xa0Ee7A142d267C1f36714E4a8F75612F20a79720",
5-
"eoaValidator": "0x7079ade5d4C71aE7868E6AC8553148FfC3D8d660",
6-
"sessionValidator": "0xE619369f26285FEd402bd0c3526aEb1faf2BE2C0",
7-
"webauthnValidator": "0x85ed36BD17265a4eb2f6d87FAd3E6277066986f0",
8-
"guardianExecutor": "0xb3b3496CC339d80b2182a3985FeF5EBE60bc896C",
9-
"accountImplementation": "0xddfa09697bCe57712B5365ff573c3e6b3C8FFDDb",
10-
"beacon": "0x197072f17a5e1A35C1909999e6f3cFEDe5A42BB8",
11-
"factory": "0xF9e4C9Cfec57860d82e6B41Cd50DEF2b3826D5CF",
12-
"testPaymaster": "0xf8C803b1378C96557381f43F86f77D72D79BeE95",
13-
"mockPaymaster": "0xf8C803b1378C96557381f43F86f77D72D79BeE95",
5+
"eoaValidator": "0xEc5AB17Cc35221cdF54EaEb0868eA82d4D75D9Bf",
6+
"sessionValidator": "0xd512108c249cC5ec5370491AD916Be31bb88Dad2",
7+
"webauthnValidator": "0xadF4aCF92F0A1398d50402Db551feb92b1125DAb",
8+
"guardianExecutor": "0x0efdDbB35e9BBc8c762E5B4a0f627210b6c9A721",
9+
"accountImplementation": "0xd7400e73bA36388c71C850aC96288E390bd22Ebe",
10+
"beacon": "0xbe38809914b552f295cD3e8dF2e77b3DA69cBC8b",
11+
"factory": "0xb11632A56608Bad85562FaE6f1AF269d1fE9e8e6",
12+
"testPaymaster": "0x287E8b17ce85B451a906EA8A179212e5a24680A3",
13+
"mockPaymaster": "0x287E8b17ce85B451a906EA8A179212e5a24680A3",
1414
"entryPoint": "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108",
1515
"bundlerUrl": "http://localhost:4337"
1616
}

packages/sdk-4337/src/client/actions/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,12 @@ export type {
2828
} from "./passkey.js";
2929
export { addPasskey, fetchAccount, findAddressesByPasskey } from "./passkey.js";
3030

31+
// Module management exports
32+
export type {
33+
IsModuleInstalledParams,
34+
IsModuleInstalledResult,
35+
} from "./modules.js";
36+
export { isGuardianModuleInstalled, isModuleInstalled, ModuleType } from "./modules.js";
37+
3138
// Utilities
3239
export { generateAccountId } from "./utils.js";
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
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+
}

packages/sdk-4337/src/client/actions/passkey.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ export async function fetchAccount(
315315
});
316316

317317
if (!publicKeyCoords || !publicKeyCoords[0] || !publicKeyCoords[1]) {
318-
throw new Error(`Passkey credentials not found in on-chain module for passkey ${credentialId}`);
318+
throw new Error(`Credential not found in on-chain module for credential ${credentialId}`);
319319
}
320320

321321
// Convert the public key coordinates back to COSE format

0 commit comments

Comments
 (0)