Skip to main content

Architecture overview

The PQ Smart Account stack adds quantum-resistant authorization to existing EVM smart accounts. No protocol changes, no new chains, no hard forks. Drop in an ERC-7579 validator module, point it at a Stylus verifier on Arbitrum, and selected operations now require an ML-DSA-65 signature instead of an ECDSA one.

Status: Proof of concept. End-to-end validated on a local Nitro devnode. Not audited. Not deployed on mainnet. See Production limitations.

Three layers

flowchart TD
subgraph OffChain["Off-Chain Tools"]
CLI["pq-keygen / pq-sign / pq-verify<br/>(Rust CLI)"]
Snap["MetaMask Snap<br/>(pq-snap)"]
Demo["WalletConnect Demo<br/>(demo/)"]
end

Bundler["Alto Bundler :4337"]

subgraph Arbitrum["Arbitrum (On-Chain)"]
EP["EntryPoint v0.7"]
Kernel["Kernel v3 Smart Account"]

subgraph Validation["Validation Pipeline"]
PQ["PQValidatorModule<br/>(Solidity · ERC-7579)<br/>Stores 1,952-byte public keys"]
Stylus["MLDSAVerifier<br/>(Stylus · Rust/WASM)<br/>ML-DSA-65 verify()"]
end

EP -->|"handleOps()"| Kernel
Kernel -->|"IValidator.validateUserOp"| PQ
PQ -->|"verify(pubKey, msgHash, sig)"| Stylus
Stylus -->|"bool"| PQ
PQ -->|"0 = success / 1 = failure"| Kernel
end

CLI -->|"eth_sendUserOperation"| Bundler
Snap -->|"eth_sendUserOperation"| Bundler
Demo -->|"eth_sendUserOperation"| Bundler
Bundler -->|"handleOps()"| EP
  1. Off-chain tools — ML-DSA key generation, signing, and UserOp submission. Available as a Rust CLI (pq-keygen, pq-sign, pq-verify), a MetaMask Snap, and a WalletConnect dapp.
  2. Solidity validator module (PQValidatorModule) — ERC-7579 compliant. Stores per-account public keys, routes verification calls.
  3. Stylus verifier (MLDSAVerifier) — ML-DSA-65 signature verification in Rust/WASM at ~374K gas — 10–100× lower cost than pure EVM.

End-to-end transaction flow

Step │ Actor │ Action
──────┼────────────────────┼─────────────────────────────────────────
1 │ dApp │ Constructs UserOp calldata (swap, transfer, vote)
2 │ dApp / Wallet │ Fills UserOp:
│ │ sender: smart account address
│ │ nonce: PQ validator nonce
│ │ verificationGasLimit ≥ 2M
3 │ EntryPoint (query) │ getUserOpHash(userOp) → userOpHash
4 │ User (off-chain) │ Signs userOpHash with ML-DSA private key
│ │ → 3,309-byte signature
5 │ Bundler │ Receives userOp via eth_sendUserOperation
│ │ Simulates validation → accepts to mempool
6 │ Bundler │ Calls EntryPoint.handleOps([userOp], beneficiary)
7 │ EntryPoint │ Calls account.validateUserOp(userOp, userOpHash)
8 │ Kernel │ Reads nonce → identifies PQ validator (non-root)
│ │ Checks selector whitelist
│ │ Delegates to PQValidatorModule.validateUserOp()
9 │ PQ Validator │ Loads stored 1,952-byte public key
│ │ Calls Stylus: verify(pk, userOpHash, sig)
10 │ Stylus Verifier │ ML-DSA-65 verify (FIPS 204) → bool (~374K gas)
11 │ EntryPoint │ If valid → executes UserOp calldata

The dApp target contract sees a normal msg.sender — it does not need to know the transaction was PQ-verified.

What a dApp integration looks like

From a dApp's perspective, PQ-secured transactions are transparent. The dApp constructs its intent as normal calldata; only the signing step and UserOp nonce encoding change:

// dApp constructs a normal interaction
const callData = kernel.interface.encodeFunctionData("execute", [
targetContract, // e.g. Uniswap router
value, // ETH value
swapCalldata, // e.g. swapExactTokensForTokens(...)
]);

// UserOp uses PQ nonce + ML-DSA signature instead of ECDSA
const userOp = {
sender: kernelAccountAddress,
nonce: pqValidatorNonce, // ← encodes PQ validator
callData: callData, // ← standard dApp intent
signature: mlDsaSignature, // ← 3,309 bytes (vs 65 for ECDSA)
verificationGasLimit: 2_000_000 // ← higher for ML-DSA
};

Design decisions

  • Arbitrum Stylus — native Rust/WASM execution. ML-DSA verification at ~374K gas versus millions in pure EVM. Today this is the only viable platform for on-chain lattice crypto. See Stylus maturity assessment.
  • ML-DSA-65 (FIPS 204) — NIST Level 3, best balance of security and signature size for on-chain verification. See ML-DSA library comparison.
  • ERC-7579 module pattern — plug-and-play with existing accounts (Kernel, Safe, Rhinestone). Users keep their existing key for low-stakes operations and upgrade specific selectors to PQ.
  • Kernel v3 — ERC-7579 native, full E2E validated on local devnode.
  • Alto bundler — open-source ERC-4337 bundler that works on Nitro devnode with --chain-type arbitrum --safe-mode false.
  • Stateless Stylus verifier — public keys live in Solidity and are passed as calldata. Avoids cross-runtime storage issues and keeps the Stylus contract a pure function.