How to migrate your Solidity contracts to post-quantum cryptography
QuantumScan detected the vulnerability. Here is the concrete migration path for every pattern — from today's audit to the long-term quantum-safe architecture.
← Back to Solidity scannerThese are the contracts that 80%+ of DeFi protocols inherit. Every token using ERC20Permit is affected.
Migration timeline
Run QuantumScan. Document every ecrecover(), EIP-712, and permit() usage. Add findings to your audit report.
Deploy Account Abstraction (ERC-4337) wrappers with ML-DSA support alongside existing ECDSA. Dual-sign critical operations.
NIST/CNSA 2.0 deadline. Replace ECDSA signers with ML-DSA (FIPS 204). Ethereum expected to have PQC precompile by then.
Migration path by pattern
Stop adding new ecrecover() calls to contracts. Document all existing usages.
Deploy a Smart Account (ERC-4337) with a custom signature validator. The validator can support ML-DSA (CRYSTALS-Dilithium) signatures off-chain verified on-chain.
// ERC-4337 custom validator — replace ecrecover
function validateUserOp(UserOperation calldata op, bytes32 hash, uint256 missingFunds)
external returns (uint256) {
// verify ML-DSA signature instead of ECDSA
require(mlDsaVerify(hash, op.signature, ownerPubKey), "Invalid PQC sig");
return 0;
}Off-chain: generate ML-DSA key pairs and sign with @noble/post-quantum. On-chain: verify via custom precompile or ZK proof.
import { ml_dsa65 } from '@noble/post-quantum/ml-dsa';
const keys = ml_dsa65.keygen();
const sig = ml_dsa65.sign(keys.secretKey, message);
const valid = ml_dsa65.verify(keys.publicKey, message, sig);Map every EIP712Domain in your codebase. These are the migration targets.
Add a parallel ML-DSA signature field to your typed data structs. Verify both ECDSA and ML-DSA. Accept only ML-DSA once quantum computers become a real threat.
struct PermitWithPQC {
address owner;
address spender;
uint256 value;
uint256 deadline;
bytes ecdsaSig; // legacy — remove post-migration
bytes mlDsaSig; // ML-DSA FIPS 204 — quantum-safe
}No PQC EIP-712 standard exists yet. Monitor EIPs on ethereum/EIPs. The hybrid approach above is the interim path.
Assembly-level ecrecover calls are harder to audit and migrate. First refactor to ECDSA.recover() from OpenZeppelin — same vulnerability, but much easier to swap later.
// BEFORE (assembly — hard to migrate)
assembly {
let success := staticcall(gas(), 0x1, ptr, 0x80, ptr, 0x20)
signer := mload(ptr)
}
// AFTER (library — easy to replace with ML-DSA later)
address signer = ECDSA.recover(hash, v, r, s);Apply the ERC-4337 ML-DSA migration path from the ecrecover pattern above.
Chainlink DON threshold ECDSA migration is outside your control. Monitor their public roadmap. No PQC Chainlink interface exists today.
Deploy an oracle circuit breaker that can freeze price updates if the oracle is compromised. This limits blast radius during a quantum attack window.
// Circuit breaker — freeze if oracle is flagged
modifier oracleNotFrozen() {
require(!oracleFrozen, "Oracle frozen: quantum risk flag active");
_;
}Use a median of multiple oracle sources (Chainlink + Pyth + UMA). If any one is compromised, the median prevents manipulation.
Add a guardian function to disable permit() if a quantum threat becomes active. Classic approve() is not quantum-safe either, but removes the off-chain signature vector.
bool public permitEnabled = true;
function setPermitEnabled(bool enabled) external onlyOwner {
permitEnabled = enabled;
emit PermitStatusChanged(enabled);
}
function permit(...) public override {
require(permitEnabled, "permit() disabled: PQC migration in progress");
super.permit(...);
}Replace ERC-2612 permit with an ERC-4337 user operation that uses ML-DSA signature. No ERC standard exists yet — track EIP proposals.
Document all current signer addresses. Plan rotation schedule — replace existing secp256k1 signers with PQC signers using Safe's owner management functions.
Safe supports custom modules. Deploy a PQC Safe Module that verifies ML-DSA signatures as an additional signer type, without replacing ECDSA signers immediately.
// Safe PQC Module (conceptual)
contract PQCSafeModule is Module {
mapping(address => bytes) public pqcPublicKeys; // ML-DSA pub keys
function execPQCTransaction(
address safe, address to, uint256 value,
bytes calldata data, bytes calldata mlDsaSig
) external {
require(mlDsaVerify(keccak256(data), mlDsaSig, pqcPublicKeys[msg.sender]));
ISafe(safe).execTransactionFromModule(to, value, data, Enum.Operation.Call);
}
}Safe team is aware of the PQC migration need. Monitor github.com/safe-global/safe-contracts for PQC proposals.
PQC tools for Solidity developers
MIT-licensed JS/TS library implementing ML-KEM, ML-DSA, SLH-DSA. Zero dependencies. By Paul Miller (creator of noble-curves used in ethers.js).
Allows any signature scheme — including ML-DSA — as wallet authentication. The main migration bridge for secp256k1 wallets and contracts.
Allows EOAs to execute code, enabling PQC key upgrades for regular wallets without deploying new contracts.
STARK-based proofs are hash-based and quantum-resistant. ZK proofs of ML-DSA signatures can be verified on EVM via StarkNet bridge.
EIPs to watch
Account Abstraction — enables custom PQC signature validators. Live on mainnet.
Set EOA code — allows PQC key migration for regular wallets. Included in Pectra.
QuBit — P2QRH (Pay to Quantum Resistant Hash) for Bitcoin. Reference for Ethereum parallel.
Scan your contracts now
Get the full vulnerability report before starting your migration. Free, open-source, no account required.