Five precompiled contracts verify NIST post-quantum signatures and hashes inside the EVM. They are active on mainnet from block 9,189,161 and callable by any contract with a plain staticcall.
The five
| Address | Function | Standard | Gas | Input | Output | ||||
|---|---|---|---|---|---|---|---|---|---|
0x0AE1 | Falcon-512 verify | NIST Falcon round 3 | 40,000 flat | `pk(897) \ | \ | sm` | 32-byte word: …01 valid, …00 invalid | ||
0x0AE2 | Falcon-1024 verify | NIST Falcon round 3 | 75,000 flat | `pk(1793) \ | \ | sm` | same | ||
0x0AE3 | ML-DSA-44 verify | FIPS 204 | 55,000 flat | `pk(1312) \ | \ | sig(2420) \ | \ | message` | same |
0x0AE4 | SLH-DSA-SHA2-128s verify | FIPS 205 | 350,000 flat | `pk(32) \ | \ | sig(7856) \ | \ | message` | same |
0x0AE5 | SHAKE256 XOF | FIPS 202 | 60 + 12 per 32-byte word of input plus output | 32-byte big-endian output length, then data | outLen bytes |
Gas is flat, not size-dependent, for the four verifiers.
They never revert
A verification precompile does not revert on malformed input. It returns the 32-byte zero word. That means a caller cannot distinguish "the signature is invalid" from "I framed the input wrongly" by catching a revert. Both come back as zero.
This is the single most common integration failure, and it is why Falcon input framing is a page of its own. Write a positive control into your test: verify a signature you know is good, with framing you know is right, in the same run. If that returns zero, no other zero in that run means anything.
Calling one from Solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
library Falcon512 {
address internal constant PRECOMPILE = address(0x0AE1);
/// @param input pk(897) || sigLen(2 BE) || nonce(40) || message || esig
function verify(bytes memory input) internal view returns (bool) {
(bool ok, bytes memory out) = PRECOMPILE.staticcall(input);
return ok && out.length == 32 && uint256(bytes32(out)) == 1;
}
}
Checking them against the live chain
The five pass the official NIST Known-Answer Tests on chain. You can run one yourself against the public endpoint with eth_call, which costs nothing:
curl -s -X POST -H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"eth_call","params":[{"to":"0x0000000000000000000000000000000000000ae5","data":"0x0000000000000000000000000000000000000000000000000000000000000020616263"},"latest"]}' \
https://rpc.aere.network
That asks 0x0AE5 for 32 bytes of SHAKE256 over the three bytes abc, and the answer is the FIPS 202 value for that input.
Not on mainnet
0x0AE6 (ML-KEM-768 deterministic encapsulation, FIPS 203) and 0x0AE7 exist on the public testnet only. Do not build mainnet code against them.
The caveat that matters
Having these does not make an account post-quantum secure. See Overview, stated precisely for why, in one paragraph, before you build on top of them.