Skip to content

Commit 0de78fd

Browse files
committed
feat: passing paymaster transactions
still need to setup tests and then integrate via auth server, but root cause was entirely unrelated
1 parent 71694b6 commit 0de78fd

File tree

6 files changed

+109
-49
lines changed

6 files changed

+109
-49
lines changed

PASSKEY_PAYMASTER_DEBUG.md

Lines changed: 30 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22

33
## Problem Statement
44

5-
The **"Send with Paymaster (Passkey)"** button fails with AA23 error
6-
(signature validation failure) during gas estimation, while:
5+
The **"Send with Paymaster (Passkey)"** button fails with AA23 error (signature
6+
validation failure) during gas estimation, while:
77

8-
-**EOA + Paymaster** works correctly (confirmed: balance changed
9-
0.1 → 0.09 ETH)
8+
-**EOA + Paymaster** works correctly (confirmed: balance changed 0.1 → 0.09
9+
ETH)
1010
-**Passkey without Paymaster** works correctly
1111
-**Rust integration test** with passkey + paymaster passes all assertions
1212
-**Vue button** with passkey + paymaster consistently fails with AA23
@@ -29,8 +29,8 @@ The **"Send with Paymaster (Passkey)"** button fails with AA23 error
2929
- This sets `data: Bytes::default()` (**empty bytes**, not a flow type)
3030
- Calls `passkey_send_transaction()` which internally uses `send_user_op()`
3131
- Validates: sender only loses transfer amount (no gas fees paid by sender)
32-
- **Test assertion:** `sender_delta == amount` (only 1000 wei transferred,
33-
no gas)
32+
- **Test assertion:** `sender_delta == amount` (only 1000 wei transferred, no
33+
gas)
3434

3535
## Failing Implementation: Vue Button
3636

@@ -69,8 +69,7 @@ AA23 reverted
6969

7070
### Error Code Meaning
7171

72-
- **AA23** = Account's `validateAccountSignature()` reverted during
73-
validation
72+
- **AA23** = Account's `validateAccountSignature()` reverted during validation
7473
- This is **NOT** a paymaster error (which would be AA33)
7574
- Occurs during gas estimation (`eth_call`) with state overrides
7675
- Not during actual transaction submission
@@ -81,8 +80,8 @@ The AA23 error during gas estimation suggests one of these:
8180

8281
1. **Account state issue**: The passkey hasn't been registered properly or
8382
WebAuthn validator isn't installed as a module
84-
2. **Stub signature mismatch**: The stub signature used during gas
85-
estimation doesn't match what the validator expects
83+
2. **Stub signature mismatch**: The stub signature used during gas estimation
84+
doesn't match what the validator expects
8685
3. **Nonce state issue**: If account already used the passkey for other
8786
transactions, nonce validation might fail
8887
4. **State override problem**: The state override in the bundler's gas
@@ -178,8 +177,7 @@ in PaymasterParams constructor
178177
- [ ] Deploy Account
179178
- [ ] Fund Account (0.1 ETH)
180179
- [ ] Load WebAuthn Validator Address
181-
- [ ] Register Passkey (critical - installs WebAuthn validator
182-
module)
180+
- [ ] Register Passkey (critical - installs WebAuthn validator module)
183181
- [ ] Fund Paymaster (1 ETH)
184182
- [ ] Verify Paymaster Balance shows > 0
185183

@@ -188,8 +186,7 @@ in PaymasterParams constructor
188186
- ✅ Steps 1-5 complete successfully
189187
- ✅ Console logs show addresses are loaded
190188
- ❌ "Send with Paymaster (Passkey)" fails with AA23
191-
- Error occurs during bundler's gas estimation (eth_call
192-
simulateValidation)
189+
- Error occurs during bundler's gas estimation (eth_call simulateValidation)
193190

194191
## Areas to Investigate
195192

@@ -246,14 +243,12 @@ The WebAuthnValidator might have specific requirements:
246243
- Expected signature format (from `stub_signature_passkey_core`)
247244
- Validation might fail if account state isn't properly simulated
248245
- Validator code location:
249-
`packages/erc4337-contracts/contracts/validators/
250-
WebAuthnValidator.sol`
246+
`packages/erc4337-contracts/contracts/validators/ WebAuthnValidator.sol`
251247

252248
## WASM FFI Flow Details
253249

254250
**File:**
255-
`packages/sdk-platforms/rust/zksync-sso-erc4337/crates/
256-
zksync-sso-erc4337-ffi-web/src/lib.rs`
251+
`packages/sdk-platforms/rust/zksync-sso-erc4337/crates/ zksync-sso-erc4337-ffi-web/src/lib.rs`
257252

258253
### Paymaster Normalization (lines 340-379)
259254

@@ -272,9 +267,8 @@ fn normalize_paymaster_params(pm: Option<PaymasterParams>) -> Option<PaymasterPa
272267
}
273268
```
274269

275-
**Note:** Hex decoding failures silently fall back to empty bytes.
276-
This is why "0x05" vs null shouldn't matter, but might indicate other
277-
issues.
270+
**Note:** Hex decoding failures silently fall back to empty bytes. This is why
271+
"0x05" vs null shouldn't matter, but might indicate other issues.
278272

279273
### UserOperation Preparation (lines 1186-1500)
280274

@@ -327,8 +321,7 @@ const balance = await publicClient.getBalance({
327321

328322
### Anvil Logs
329323

330-
Watch the Anvil terminal (Terminal "a") during "Send with Paymaster
331-
(Passkey)":
324+
Watch the Anvil terminal (Terminal "a") during "Send with Paymaster (Passkey)":
332325

333326
- Should show `eth_call` with state overrides
334327
- Look for the account address in the calls
@@ -349,8 +342,7 @@ Check the bundler RPC calls in browser DevTools:
349342

350343
### Hypothesis 1: Module Not Installed
351344

352-
After clicking "Register Passkey", the WebAuthn validator should be
353-
installed.
345+
After clicking "Register Passkey", the WebAuthn validator should be installed.
354346

355347
**Test:**
356348

@@ -370,8 +362,7 @@ console.log("WebAuthn validator installed:", isInstalled)
370362

371363
The passkey credentials might not be stored properly.
372364

373-
**Test:** Check the account's storage for passkey after "Register
374-
Passkey":
365+
**Test:** Check the account's storage for passkey after "Register Passkey":
375366

376367
```typescript
377368
// Get account's passkey registry state
@@ -423,11 +414,11 @@ const paymaster = new PaymasterParams(paymasterAddress, null, null, null);
423414
### Core Logic
424415

425416
- [lib.rs](packages/sdk-platforms/rust/zksync-sso-erc4337/crates/
426-
zksync-sso-erc4337-ffi-web/src/lib.rs) -
427-
`prepare_passkey_user_operation()`, `normalize_paymaster_params()`
417+
zksync-sso-erc4337-ffi-web/src/lib.rs) - `prepare_passkey_user_operation()`,
418+
`normalize_paymaster_params()`
428419
- [send.rs](packages/sdk-platforms/rust/zksync-sso-erc4337/crates/
429-
zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/
430-
send.rs) - `send_user_op()`
420+
zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/ send.rs) -
421+
`send_user_op()`
431422
- [passkey.rs](packages/sdk-platforms/rust/zksync-sso-erc4337/crates/
432423
zksync-sso-erc4337-core/src/erc4337/account/modular_smart_account/
433424
send/passkey.rs) - `passkey_send_transaction()`
@@ -443,28 +434,32 @@ const paymaster = new PaymasterParams(paymasterAddress, null, null, null);
443434

444435
- [web-sdk-test.vue](examples/demo-app/pages/web-sdk-test.vue) -
445436
`sendWithPasskeyPaymaster()` (line 1449)
446-
- [TransactionSender.vue](examples/demo-app/components/
447-
TransactionSender.vue) - Paymaster checkbox and submission
437+
- [TransactionSender.vue](examples/demo-app/components/ TransactionSender.vue) -
438+
Paymaster checkbox and submission
448439

449440
## Next Steps for Debugging
450441

451442
1. **Enable verbose logging:**
452-
- Add `console.log()` statements in `prepare_passkey_user_operation()`
453-
(Rust WASM)
443+
444+
- Add `console.log()` statements in `prepare_passkey_user_operation()` (Rust
445+
WASM)
454446
- Log the prepared UserOperation before signing
455447
- Log the final paymaster field values
456448

457449
2. **Test with reduced scope:**
450+
458451
- Try sending without paymaster to confirm passkey works
459452
- Try EOA with paymaster to confirm paymaster works
460453
- Combine both to identify interaction issue
461454

462455
3. **Compare UserOperations:**
456+
463457
- Print full UserOp from working EOA + paymaster
464458
- Print full UserOp from failing Passkey + paymaster
465459
- Diff the two to find structural differences
466460

467461
4. **Check state machine:**
462+
468463
- Ensure "Register Passkey" actually installed the module
469464
- Verify account state includes registered passkey
470465
- Confirm nonce is correct before sending

examples/demo-app/pages/web-sdk-test.vue

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1420,17 +1420,39 @@ async function fundPaymaster() {
14201420
const deployerKey = eoaSignerPrivateKey;
14211421
const deployerAccount = privateKeyToAccount(`0x${deployerKey}`);
14221422
1423-
// Create a simple transfer transaction
1424-
await createWalletClient({
1423+
const walletClient = createWalletClient({
14251424
account: deployerAccount,
14261425
chain: getChainConfig(contracts),
14271426
transport: http(rpcUrl),
1428-
}).sendTransaction({
1427+
});
1428+
1429+
// Create a simple transfer transaction to fund the paymaster
1430+
await walletClient.sendTransaction({
14291431
to: paymasterAddress as Address,
14301432
value: parseEther(paymasterFundAmount.value),
14311433
account: deployerAccount,
14321434
});
14331435
1436+
// eslint-disable-next-line no-console
1437+
console.log("💰 Funded paymaster with", paymasterFundAmount.value, "ETH");
1438+
1439+
// Wait for confirmation
1440+
await new Promise((resolve) => setTimeout(resolve, 2000));
1441+
1442+
// Now deposit the funds into the EntryPoint by calling deposit()
1443+
// eslint-disable-next-line no-console
1444+
console.log("💳 Depositing funds into EntryPoint...");
1445+
1446+
await walletClient.sendTransaction({
1447+
to: paymasterAddress as Address,
1448+
data: "0xd0e30db0" as `0x${string}`, // deposit() function selector
1449+
value: parseEther(paymasterFundAmount.value),
1450+
account: deployerAccount,
1451+
});
1452+
1453+
// eslint-disable-next-line no-console
1454+
console.log("✅ Deposit complete");
1455+
14341456
// Wait for confirmation
14351457
await new Promise((resolve) => setTimeout(resolve, 2000));
14361458
await fetchPaymasterBalance();
@@ -1488,8 +1510,26 @@ async function sendWithPasskeyPaymaster() {
14881510
14891511
// Build config and paymaster params
14901512
const config = new SendTransactionConfig(rpcUrl, bundlerUrl, entryPoint);
1491-
// Use empty data for paymaster (matching Rust test's default_paymaster)
1492-
const paymaster = new PaymasterParams(paymasterAddress, null, null, null);
1513+
// Passkey signatures are large and need more gas than ECDSA
1514+
// verificationGasLimit: 500k (passkey validation is expensive)
1515+
// postOpGasLimit: 1M (default)
1516+
const paymaster = new PaymasterParams(
1517+
paymasterAddress,
1518+
null,
1519+
"500000", // verification gas limit
1520+
null, // postOp will default to 1M
1521+
);
1522+
1523+
// eslint-disable-next-line no-console
1524+
console.log("🔧 Preparing passkey transaction with paymaster:", paymasterAddress);
1525+
// eslint-disable-next-line no-console
1526+
console.log(" EntryPoint:", entryPoint);
1527+
// eslint-disable-next-line no-console
1528+
console.log(" Account:", accountAddress);
1529+
// eslint-disable-next-line no-console
1530+
console.log(" To:", toAddress);
1531+
// eslint-disable-next-line no-console
1532+
console.log(" Amount:", amountWei, "wei");
14931533
14941534
// Prepare user operation (includes paymaster into the prepared payload)
14951535
const prepared = await prepare_passkey_user_operation(

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ echo "💰 Funding paymaster with 10 ETH..."
5555
ANVIL_ACCOUNT_0_KEY="0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
5656
cast send "$PAYMASTER" --value 10ether --private-key "$ANVIL_ACCOUNT_0_KEY" --rpc-url "$RPC_URL" 2>&1 || echo "Fund transfer initiated"
5757

58+
# Deposit the paymaster's ETH into the EntryPoint
59+
echo "💳 Depositing 10 ETH into EntryPoint for paymaster..."
60+
cast send "$PAYMASTER" "deposit()" --value 10ether --private-key "$ANVIL_ACCOUNT_0_KEY" --rpc-url "$RPC_URL" 2>&1 || echo "Deposit initiated"
61+
5862
# Verify all addresses were extracted
5963
if [ -z "$EOA_VALIDATOR" ] || [ -z "$SESSION_VALIDATOR" ] || [ -z "$WEBAUTHN_VALIDATOR" ] || \
6064
[ -z "$GUARDIAN_EXECUTOR" ] || [ -z "$ACCOUNT_IMPL" ] || [ -z "$BEACON" ] || [ -z "$FACTORY" ] || [ -z "$PAYMASTER" ]; then

examples/demo-app/smart-contracts/DeployPaymaster.s.sol

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,17 @@ import { console } from "forge-std/console.sol";
66
import { TestPaymaster } from "./TestPaymaster.sol";
77

88
contract DeployPaymaster is Script {
9+
// EntryPoint v0.8 canonical address
10+
address constant ENTRY_POINT = 0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108;
11+
912
function run() public {
1013
vm.startBroadcast();
1114

12-
TestPaymaster paymaster = new TestPaymaster();
15+
TestPaymaster paymaster = new TestPaymaster(ENTRY_POINT);
1316

1417
vm.stopBroadcast();
1518

1619
console.log("TestPaymaster:", address(paymaster));
20+
console.log("EntryPoint:", ENTRY_POINT);
1721
}
1822
}

examples/demo-app/smart-contracts/TestPaymaster.sol

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ pragma solidity ^0.8.28;
33

44
import { IPaymaster } from "account-abstraction/interfaces/IPaymaster.sol";
55
import { PackedUserOperation } from "account-abstraction/interfaces/PackedUserOperation.sol";
6+
import { IEntryPoint } from "account-abstraction/interfaces/IEntryPoint.sol";
67

78
/// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
89
/// !!! !!!
@@ -14,6 +15,12 @@ import { PackedUserOperation } from "account-abstraction/interfaces/PackedUserOp
1415
/// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1516

1617
contract TestPaymaster is IPaymaster {
18+
IEntryPoint public immutable entryPoint;
19+
20+
constructor(address _entryPoint) {
21+
entryPoint = IEntryPoint(_entryPoint);
22+
}
23+
1724
function validatePaymasterUserOp(
1825
PackedUserOperation calldata,
1926
bytes32,
@@ -24,5 +31,15 @@ contract TestPaymaster is IPaymaster {
2431

2532
function postOp(PostOpMode, bytes calldata, uint256, uint256) external override {}
2633

34+
/// @notice Deposit funds into the EntryPoint for this paymaster
35+
function deposit() external payable {
36+
entryPoint.depositTo{ value: msg.value }(address(this));
37+
}
38+
39+
/// @notice Withdraw funds from the EntryPoint
40+
function withdrawTo(address payable withdrawAddress, uint256 amount) external {
41+
entryPoint.withdrawTo(withdrawAddress, amount);
42+
}
43+
2744
receive() external payable {}
2845
}

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

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22
"rpcUrl": "http://localhost:8545",
33
"chainId": 1337,
44
"deployer": "0xa0Ee7A142d267C1f36714E4a8F75612F20a79720",
5-
"eoaValidator": "0xBf344DA0c5847266B9D7a8fcFcbb9449fc804B6B",
6-
"sessionValidator": "0x2C94D758140037d36c139409c2380Bd4AC3f082b",
7-
"webauthnValidator": "0x7fB73973d71dA68b5B7b9D29AEc7d278A16aA661",
8-
"guardianExecutor": "0x1edB03d812A15782143E05ac212CC74baD076a43",
9-
"accountImplementation": "0xE28FaF4bd406b7AE43e5DaA02cd5a53F96ADAb66",
10-
"beacon": "0x3Cb6Ce4F4733420Ca17dAaC6B11c74a392d7EB10",
11-
"factory": "0x48D1a5AD729a60Ab481cb61468474E36724eF33F",
12-
"testPaymaster": "0x4A37478214848aD7362800934Ba0483Aa15A63aF",
5+
"eoaValidator": "0xc6Dee041f1282a37Dad5693fb40fB7Bc48D9fBcA",
6+
"sessionValidator": "0x94875ccFAD36B6d05E256be5CD8a4e006DEd81c8",
7+
"webauthnValidator": "0x92729Db48438805E124b22c4750D25BEfc4aD27b",
8+
"guardianExecutor": "0xdb2C5779eD86cBbFeD7cc2f184a120f3a6c71f02",
9+
"accountImplementation": "0xd49b4bf75E90f8A839e2a93D8aa950370062259a",
10+
"beacon": "0xED3D305AB07913c83d25993C114F43540D776a3F",
11+
"factory": "0x2A45F0fbAbd71F1557737d13e7ad995b5162904e",
12+
"testPaymaster": "0xDd72e46653662b0118A2743574467FAA582D633e",
1313
"entryPoint": "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108",
1414
"bundlerUrl": "http://localhost:4337"
1515
}

0 commit comments

Comments
 (0)