Measure an EVM address quantum exposure from its bytecode. Zero dependencies, runs against any node you operate.
Go to file
2026-08-19 15:51:38 +03:00
fixtures The free scan now carries the confidence label, not just the colour 2026-08-17 11:33:06 +03:00
lib Aere PQC migration toolkit: measure an address quantum exposure from its bytecode 2026-08-17 10:31:31 +03:00
notarizare The free scan now carries the confidence label, not just the colour 2026-08-17 11:24:34 +03:00
aere-pqc.js Aere PQC migration toolkit: measure an address quantum exposure from its bytecode 2026-08-17 10:31:31 +03:00
LICENSE Aere PQC migration toolkit: measure an address quantum exposure from its bytecode 2026-08-17 10:31:31 +03:00
package.json Aere PQC migration toolkit: measure an address quantum exposure from its bytecode 2026-08-17 10:31:31 +03:00
plan.js Aere PQC migration toolkit: measure an address quantum exposure from its bytecode 2026-08-17 10:31:31 +03:00
proba-vocabular.mjs Aere PQC migration toolkit: measure an address quantum exposure from its bytecode 2026-08-17 10:31:31 +03:00
raport.js The free scan now carries the confidence label, not just the colour 2026-08-17 10:42:54 +03:00
README.md README facts as of 2026-08-19: consensus (classical ECDSA per block + post-quantum anchor checkpoint, >=3 of 9 since 2026-08-14), nine validators, EVM parity stated precisely, public source links that resolve 2026-08-19 15:51:38 +03:00
scan-gratuit.mjs The free scan now carries the confidence label, not just the colour 2026-08-17 10:42:54 +03:00
scan.js Aere PQC migration toolkit: measure an address quantum exposure from its bytecode 2026-08-17 10:31:31 +03:00
selftest.js The free scan now carries the confidence label, not just the colour 2026-08-17 11:33:06 +03:00
simulate.js Aere PQC migration toolkit: measure an address quantum exposure from its bytecode 2026-08-17 10:31:31 +03:00
VERIFY-US.md Aere PQC migration toolkit: measure an address quantum exposure from its bytecode 2026-08-17 10:31:31 +03:00

Aere Quantum Migration Toolkit

A starting toolkit to assess and migrate EVM accounts toward post-quantum readiness on Aere Network (chain 2800), or on any EVM chain reachable by RPC.

Aere Network is the post-quantum EVM L1: it ships five live NIST post-quantum verification precompiles on mainnet (Falcon-512, Falcon-1024, ML-DSA-44, SLH-DSA-128s, SHAKE256, at 0x0AE1..0x0AE5, activated at block 9,189,161), plus a live Falcon-512-owned smart account and factory. That makes Aere the natural home for tooling that helps others measure their quantum exposure and move to a post-quantum account. This toolkit extends Aere's internal account-migration work outward into something anyone can run.

It is a real product with one unified CLI and zero runtime dependencies (pure Node built-ins, Node 18+):

  1. scan.js / aere-pqc scan, a real PQC-readiness scanner (EOA vs contract, and which signature primitives a contract uses).
  2. lib/migrator.js / aere-pqc build-migration, the transaction-CONSTRUCTION SDK: given a classical ECDSA EOA and a target Falcon-512 public key it derives the counterfactual post-quantum account, enumerates the assets to move (read-only), and emits the exact sequence of UNSIGNED transactions a holder would sign. It never signs, deploys, or moves funds.
  3. lib/readiness.js / aere-pqc readiness + simulate.js / aere-pqc simulate, the quantum-readiness cost simulator: one-time migration gas plus ongoing per-authorization cost, from the measured PQC verify gas.

aere-pqc is the single entry point that wraps scan, simulate, build-migration, and readiness. The lower-level scan.js, plan.js, simulate.js CLIs remain for direct use.

The scope boundary (this never moves)

Migrating an account's authentication to a post-quantum scheme changes how that account authorizes. It does NOT make Aere consensus post-quantum. Mainnet 2800 still seals blocks with classical secp256k1 QBFT, and the on-chain ZK verifiers (BN254 Groth16) are classical. Nothing in this toolkit claims otherwise, and the scanner and simulator both restate the boundary in their output.

Honest status

This is a starting toolkit, not a finished product. What is real and verified is listed under "What actually runs" below. What is heuristic, out of scope, or not yet deployed is flagged inline with [VERIFY] and [MEASURE], in the code and in the reports the tools print.

1. The readiness scanner: scan.js

Given an EVM address, the scanner reports quantum exposure as a colour:

  • RED, classical-only. An EOA (secp256k1 ECDSA authenticated, so Shor-breakable and unable to hold verification code), or a contract with no detectable post-quantum precompile usage.
  • YELLOW, hybrid. A contract that uses both a classical signature primitive (ecrecover at 0x01, or P-256 at 0x100) and Aere's live PQC precompile band.
  • GREEN, PQC-capable. A contract that uses Aere's live native PQC precompiles (0x0AE1..0x0AE5).
# live scan over JSON-RPC (this is the only part that needs network access)
node scan.js 0x465d9E3b476BF98Aa1393079e240Db5D2a9bEA6A --rpc https://rpc.aere.network

# offline: scan a bundled real-bytecode fixture
node scan.js --fixture AerePQCAttestation

# offline: scan bytecode you already have
node scan.js 0xYourContract --bytecode 0x60806040...

# machine-readable
node scan.js 0x465d... --rpc https://rpc.aere.network --json

How it detects, and why it is honest

The scanner does not grep the hex. A naive search for 610ae1 (PUSH2 0x0AE1) false-positives, because those bytes can sit inside another instruction's push data. Two techniques make the detection real:

  1. Opcode-aware decoding. The bytecode is walked as an instruction stream, skipping each PUSH's immediate bytes, so a matched constant is a genuine push.
  2. Local stack simulation to resolve CALL targets. For every CALL/STATICCALL/DELEGATECALL/CALLCODE the scanner recovers the address argument (the second stack item) by simulating the stack per basic block. That distinguishes an actual call to 0x0AE1 from the bare number 0x0AE5 used as a memory length near an unrelated staticcall, which is precisely the false positive a proximity heuristic hits. (An earlier proximity-only version of this tool misread AereHybridAuth as hybrid for exactly that reason; the stack-simulation version reads it correctly.)

The signature-verify addresses 0x0AE1..0x0AE4 (2785..2788) are not round numbers, so a genuine opcode-aligned push of one is a medium-confidence signal even when the call target is staged through memory and cannot be resolved locally. The common literals 0x01, 0x100, and 0x0AE5 are never used as a fallback signal; they count only when stack simulation resolves them as an actual call target.

Scanner limitations (read these)

  • Bytecode scanning only sees NATIVE precompile calls. A contract that verifies Falcon through the SOLIDITY AereFalcon512Verifier (0x4E8e...D8fFC, the path the deployed AerePQCAccount and AereHybridAuth actually take) delegates to a normal contract address, so it reads as RED here even though it is PQC-capable. The bundled AereHybridAuth fixture demonstrates this on purpose. Confirm an auth path against source when the scanner says RED.
  • ecrecover (0x01) detection is high confidence only because stack simulation resolves the call target to the exact address 1. A contract that never calls a signature precompile (authorization is by owner or role) also reads RED, which is correct: it has no quantum-vulnerable signature surface to migrate.
  • The scanner classifies signature-verification exposure. It does not audit key management, upgradeability, or business logic.

2. The migration SDK: lib/migrator.js (transaction construction)

Given a classical ECDSA EOA and a target NIST Falcon-512 public key, this module builds the exact sequence of UNSIGNED transactions a holder would sign to move their assets onto a post-quantum AerePQCAccount. It is the concrete, signable-artifact layer on top of the older lib/migrate.js plan builder.

# demo (uses the live sample account's real Falcon key; demo EOA 0x..dEaD)
node aere-pqc.js build-migration

# real build: derive the target, enumerate a real EOA's assets read-only, and
# attach a live gas estimate to each unsigned tx
node aere-pqc.js build-migration --eoa 0xYourEoa --falcon 0x09... \
  --rpc https://rpc.aere.network --native 1000000000000000 --estimate

# atomic sweep via the (audit-gated, not-yet-deployed) AereAccountMigrator
node aere-pqc.js build-migration --eoa 0xYourEoa --path migrator \
  --migrator 0xVerifiedMigrator --tokens 0xTokenA,0xTokenB --amounts 1000,2000

# machine-readable
node aere-pqc.js build-migration --eoa 0xYourEoa --rpc https://rpc.aere.network --json

Key functions (import ... from '@aere/pqc-migration-toolkit/migrator'):

  • predictTargetAddress({ falconPubKey, salt }): the counterfactual AerePQCAccount address, pure CREATE2 math, no RPC. Mirrors AerePQCAccountFactory.predictAddress and is verified in selftest.js against the live on-chain sample account.
  • enumerateAssets({ rpcUrl, owner, tokens }): READ-ONLY. eth_getBalance for native AERE and eth_call balanceOf(owner) per ERC-20 candidate; returns which assets have a non-zero balance and therefore need moving. Signs nothing.
  • buildMigrationTransactions({ path, eoa, falconPubKey, tokens, nativeWei, ... }): the core. Returns an array of UNSIGNED tx objects, each stamped signed:false, shaped { to, from, value, data, chainId } (nonce/gas left for the signer). Two paths:
    • direct (works today, no extra contract): per-asset ERC-20 transfer + native send straight to the target account. Not atomic.
    • migrator (atomic, all-or-nothing): approve(migrator, amount) per token then one AereAccountMigrator.migrateToPqcAccount(...) that binds the destination to the Falcon key on-chain. REQUIRES an explicit --migrator address, because AereAccountMigrator is repo source in the audit-gated notDeployedHeld set and has NO live mainnet address (see below).
  • estimateMigrationGas(plan, rpcUrl): attaches a read-only eth_estimateGas result to each unsigned tx (or an error string when a step cannot be simulated pre-deploy). Broadcasts nothing.
  • encodeMigrateCalldata, encodeMigrateToPqcAccountCalldata, encodeApproveCalldata, encodeTransferCalldata, encodeCreateAccountCalldata: the individual calldata builders, each verified byte-for-byte against ethers v6 (see "What actually runs").

The calldata encoder (lib/abi.js) is a zero-dependency Solidity ABI encoder for exactly the types the migrator needs (uint256, address, bool, bytes, address[], uint256[]), validated against ethers 6.16.0.

The migration paths

PQC account (Falcon-512 owned). Derive the counterfactual account (step 1, off-chain), deploy it through the live factory with createAccount(falconPubKey, salt) (step 2, permissionless and idempotent), then move assets and roles off the old EOA into it (step 3). After the move, spending authority is the Falcon-512 key only. There is no ECDSA fallback, so register the key in AerePQCKeyRegistry (proof-of-possession) and set PQC social-recovery guardians before moving material value.

Hybrid account (ECDSA + Falcon-512). Register a hybrid identity in AereHybridAuth binding your existing classical address to a Falcon-512 key, then authorize with both legs during the transition. An attacker must break both secp256k1 AND Falcon-512.

EIP-7702 delegation. Aere supports EIP-7702, so an EOA can temporarily delegate execution to account code to batch a migration move in a single EOA transaction.

Founder-gated caveat, and what works today

The AerePQCAccountFactory (0xd5315Ea7...CE58) is live, and createAccount is permissionless, so deploying a PQC account and moving assets into it with the direct path is possible TODAY with no special authority: the whole direct sequence is plain factory + ERC-20 + native transactions the holder signs.

What is NOT deployed:

  • AereAccountMigrator (the atomic migrate / migrateToPqcAccount conduit) is repo source (contracts/pqc/AereAccountMigrator.sol) in the audit-gated notDeployedHeld.externalAuditGated_fundFlow set. It has NO mainnet address. The migrator path therefore refuses to build unless you pass an explicit --migrator address, and it prints a [VERIFY] warning telling you to confirm that address holds the exact audited bytecode before granting it any approval.
  • AereHybridAuth is repo source with no mainnet address, so the hybrid path's deploy step is a founder/deployer step, flagged [VERIFY].

Deploying NEW contracts or a corrected V2 redeploy is signed by the Foundation/deployer key, never by this toolkit.

3. The cost simulator: aere-pqc readiness and simulate.js

Two views on cost, both over the MEASURED PQC verify gas from aerenew/docs/AERE-BENCHMARK-REPORT.md:

  • aere-pqc readiness <address> --rpc <url> is the per-account report: it scans the account, enumerates its movable assets read-only, and prints the one-time migration gas (account deploy + asset moves) plus the ongoing per-authorization cost. Asset-move gas is a clearly-labeled [VERIFY] planning estimate anchored to the one asset-move figure measured live this session (a native AERE send at 21,246 gas via eth_estimateGas); PQC auth gas is [CITED].
  • simulate.js / aere-pqc simulate is the fleet projection across many accounts.
aere-pqc readiness 0xYourEoa --rpc https://rpc.aere.network --tokens 0xTokenA
node simulate.js --count 100 --scheme hybrid --auths 5
node simulate.js --accounts accounts.json --gwei 1
node simulate.js --schemes          # list modeled schemes

Modeled per-authorization gas (native precompile verify-and-record receipt, [CITED: AERE-BENCHMARK-REPORT.md Part C.1]):

Scheme Marginal verify gas Verify-and-record tx gas
Falcon-512 40,000 86,336
Hybrid (ECDSA + Falcon-512) 43,000 89,336
Falcon-1024 75,000 145,496
ML-DSA-44 55,000 351,050
SLH-DSA-128s 350,000 558,276

The falcon512_solidity scheme instead models the CURRENTLY-DEPLOYED AerePQCAccount, which verifies Falcon in the EVM via the Solidity verifier: one authorization measured 10,278,313 gas on-chain [CITED: pqc-account.json], under the EIP-7825 2^24 per-tx cap. These two Falcon paths (native precompile ~86k vs deployed-account Solidity ~10.5M) must not be conflated; the simulator keeps them distinct. Account CREATE2 deploy gas is 1,493,084 [CITED: pqc-account.json].

What actually runs (verified, not asserted)

node selftest.js runs with no network and checks all of this (21 assertions, all PASS):

  • keccak256("") equals the known Ethereum/Keccak vector (the zero-dependency keccak is correct).
  • predictPqcAccountAddress re-derives the LIVE sample AerePQCAccount address 0xa42a5e7F72E46BadC11367650Ec34D676194326f from its real 897-byte Falcon-512 key (proves the CREATE2 derivation is byte-correct against a real deployment).
  • The scanner classifies the four bundled REAL bytecode fixtures correctly: AerePQCAttestation GREEN, AerePQCTxAccount GREEN, AereHybridAuth RED (the Solidity-verifier scope limit), AereFalcon512Verifier RED (its 0x0100 constants are literals, not P-256). A synthetic hybrid reads YELLOW; empty code reads RED (EOA).
  • The migration-SDK calldata builders (migrate, approve, transfer, balanceOf) are byte-identical to values independently generated by ethers 6.16.0 (the encoder in lib/abi.js is a correct Solidity ABI encoder; the ground truth was generated with aerenew/contracts/node_modules/ethers and hardcoded into selftest.js so the check stays zero-dependency).
  • buildMigrationTransactions returns UNSIGNED objects only: every tx is stamped signed:false, none carries an r/s/v/signature field, and the target matches the CREATE2 derivation. The migrator path refuses to build without an explicit migrator address (because it is not deployed).
  • The readiness report composes the CITED deployed-account auth figure (10,278,313 gas) and counts movable assets correctly.

Additionally verified live against https://rpc.aere.network (chain 2800, ~block 10,416,783, 2026-07-19):

  • aere-pqc scan fetched real bytecode and classified the live AerePQCAttestation (0x465d...) as GREEN (4402 bytes) and the live deployer EOA (0xbeB3...) as RED (0 bytes of code).
  • aere-pqc build-migration --eoa 0xbeB3... --native 1000000000000000 --estimate produced two unsigned txs and attached REAL eth_estimateGas results: createAccount 59,064 gas (low because the salt-0 sample account is ALREADY deployed, so the factory's idempotent createAccount short-circuits; a FIRST deploy is the cited 1,493,084), and the native send 21,246 gas.
  • Honest hazard surfaced by the live estimate: auto-draining the FULL native balance of an ACTIVE account fails, because the sender must reserve gas AND the balance can move between the read and the estimate. The tool now reserves a gas buffer by default and supports --native <wei> for a fixed, race-free amount.
  • aere-pqc readiness 0xbeB3... --rpc ... reported a one-time migration cost of 1,514,330 gas (1,493,084 deploy + 21,246 native) = 0.001514330 AERE at 1 Gwei, and an ongoing 10,278,313 gas/auth (deployed reality) vs an 86,336 gas/auth native-precompile projection.

Nothing above broadcasts a transaction. Every network call is a read (eth_getCode, eth_getBalance, eth_call, eth_estimateGas, eth_gasPrice).

Addresses used (chain 2800)

Name Address Status
PQC precompile band 0x0AE1..0x0AE5 live (block 9,189,161)
P-256 (RIP-7212/7951) 0x100 live (Osaka)
ecrecover 0x01 protocol constant
AerePQCAccountFactory 0xd5315Ea7caa60d320c4f34b1bEd70dd9cc02CE58 live
AerePQCAccount (sample) 0xa42a5e7F72E46BadC11367650Ec34D676194326f live
AereFalcon512Verifier (Solidity) 0x4E8e9682329e646784fB3bd01430aA4bA54D8fFC live
AerePQCKeyRegistry 0x1eCa3c5ADcBD0b22636D8672b00faC6D89363691 live
AereEntryPointV2 0x8D6f40598d552fF0Cb358b6012cF4227B86aF770 live
AereHybridAuth (repo source) not deployed [VERIFY]
AereAccountMigrator (repo source) not deployed, audit-gated [VERIFY]

Testnet-only PQC precompiles 0x0AE6 (ML-KEM-768) and 0x0AE7 (Falcon HashToPoint) are NOT on mainnet 2800; a mainnet call to them returns empty. The scanner names them and flags [VERIFY] if seen.

Files

aere-pqc.js      unified CLI: scan | simulate | build-migration | readiness
scan.js          readiness scanner CLI (also aere-pqc scan)
plan.js          migration-plan CLI (older step-by-step plan builder)
simulate.js      migration cost simulator CLI (also aere-pqc simulate)
selftest.js      no-network proof: keccak + CREATE2 + fixtures + calldata + txs
lib/keccak.js    zero-dependency keccak256 + hex/checksum helpers
lib/abi.js       zero-dependency Solidity ABI encoder (validated vs ethers v6)
lib/bytecode.js  opcode-aware decoder + stack-simulation CALL-target resolver
lib/scanner.js   account classifier (EOA vs contract, RED/YELLOW/GREEN)
lib/migrate.js   plan builder + CREATE2 derivation (the original SDK)
lib/migrator.js  transaction-construction SDK (unsigned txs, asset enumeration)
lib/readiness.js per-account quantum-readiness + migration-cost report
lib/simulate.js  fleet cost model over the measured PQC verify gas
lib/precompiles.js  address bands + measured gas, single source of truth
lib/rpc.js       minimal read-only JSON-RPC client (global fetch)
fixtures/        real deployed bytecode extracted from this repo's artifacts

The one hard invariant

This toolkit CONSTRUCTS and SIMULATES. It never signs, deploys, or moves funds. There is no private key anywhere in it. build-migration hands you inert, unsigned transaction objects; you review and sign them in your own wallet. Every network call is read-only. Executing a migration is user-gated (and, for the migrator/hybrid contracts, audit-and-founder-gated).

License

MIT.