ERC-7579 validator module
PQValidatorModule is an ERC-7579 validator module written in Solidity that stores each account's ML-DSA-65 public key and delegates all signature verification to the Stylus verifier.
Compatible with any ERC-7579 smart account (Kernel, Safe, Rhinestone). Tested end-to-end with Kernel v3.
Architecture
flowchart TD
EP[EntryPoint v0.7] -->|validateUserOp| K[Kernel v3<br/>Smart Account]
K -->|IValidator.validateUserOp| PQ[PQValidatorModule]
PQ -->|IMLDSAVerifier.verify<br/>pubKey + msgHash + sig| S[Stylus Verifier<br/>Rust/WASM]
S -->|bool| PQ
PQ -->|0 = success<br/>1 = failure| K
subgraph Storage
PQ ---|mapping: address → bytes| DB[(Public Keys<br/>1,952 bytes each)]
end
Contracts
| File | Purpose |
|---|---|
src/PQValidatorModule.sol | Validator module: onInstall / onUninstall, validateUserOp, isValidSignatureWithSender (ERC-1271) |
src/interfaces/IMLDSAVerifier.sol | Interface to the Stylus verifier: verify(bytes, bytes32, bytes) → bool |
Build and test
# Build
forge build --root evm/
# Test (15 test cases)
forge test --root evm/ -vvv
# Format check
forge fmt --root evm/ --check
Deployment
Automated
./scripts/dev-stack.sh # deploys verifier + module
Manual
# 1. Deploy (requires Stylus verifier address)
BYTECODE=$(jq -r '.bytecode.object' evm/out/PQValidatorModule.sol/PQValidatorModule.json)
ARGS=$(cast abi-encode "constructor(address)" $STYLUS_VERIFIER)
cast send --private-key $PK --rpc-url $RPC --create "${BYTECODE}${ARGS:2}"
# 2. Install on Kernel account (1,952-byte ML-DSA public key as init data)
# moduleType = 1 (validator), initData = raw public key bytes
# 3. Grant selector access for non-root usage
# Kernel.grantAccess(selector, pqModule) for execute(bytes32,bytes)
Kernel integration notes
- Non-root validator nonce key:
0x0001{20-byte validator address}0000. verificationGasLimitmust be at least 2,000,000 (Stylus verify ~374K + Kernel overhead).- Kernel must be compiled with
FOUNDRY_PROFILE=deploy(via-ir) to stay under the 24 KB contract size limit.
Production limitations
| Limitation | Detail |
|---|---|
| Mock-based tests | Unit tests use vm.mockCall to stub the Stylus verifier. Real integration requires a running devnode. |
| No aggregation | Single-validator, non-aggregated path only. |
| Verifier upgrade = redeploy | Immutable verifier address means upgrading the ML-DSA implementation requires deploying a new module. |
| No key rotation | Once installed, the public key can only be changed by uninstalling and reinstalling the module. |
| ERC-1271 sender allowlist | isValidSignatureWithSender currently accepts any sender. Per-account allowlisting is on the roadmap. |
Related
- Stylus ML-DSA-65 verifier — the cryptographic primitive this module delegates to
- Gas and cost model — measured gas costs
- Production limitations and roadmap — full list of outstanding items