# 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`). ```bash # 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. ```bash # 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
--rpc