aere-docs/AERE-QUANTUM-MIGRATION.md
Aere Network dd1410c266 The unpublished line of work joins the sanitized public line
The published line and the local line of this repository had no common
ancestor: the public one carried the hygiene pass (no host names, no internal
paths), the local one carried a month of corrections that never shipped. This
commit ports the local work onto the public line, keeping the public hygiene
wording wherever the two touched the same sentence, and keeping the public
version of AERE-CROSS-CLIENT-DETERMINISM.md entirely.

Carried: LICENSE/LICENSING corrections, VERIFY-POLICY.md,
CITATIONS-UNRESOLVED.md remeasured 2026-08-11, the 'audited' adjective removed
from next to Bouncy Castle, citation paths rewritten to published form, AIP-8,
the QA consolidation report, the second EIP validation pass, fork-height
corrections, the AereSink / threshold-factory correction, the forge test
floor, and the architecture-map updates.
2026-08-15 13:54:42 +03:00

30 KiB

Aere Quantum Migration: Accounts and Network Keys

Status: user account migration is REAL and TESTED (contract + Hardhat suite, below). Network key rotation is DESIGN ONLY and founder + audit gated. This document is the last quantum-resistance layer at the key-management level (quantum layer 11), and it is honest about which half is a user-callable contract today and which half is a gated network operation.

Scope boundary, stated once and never crossed in this document: moving an account (or a network operator) from a classical ECDSA key to a post-quantum key changes who can authorize. It does NOT make Aere consensus post-quantum. Mainnet chain 2800 still seals blocks with classical secp256k1 QBFT, and the on-chain ZK verifiers (BN254 Groth16) are classical. Validator-key rotation to a post-quantum scheme is therefore coupled to the separate, gated consensus-PQC activation, not implied by anything here.

Two flag conventions are used throughout:

  • [VERIFY] a claim that should be confirmed against live chain state or a deployment record before it is relied on.
  • [MEASURE] a number or outcome that depends on a measurement or an operational run that has not been performed here.

Part A. User account migration (real, tested)

A.1 Why migrate at all: harvest-now-decrypt-later

An Aere externally-owned account (EOA) is authorized by a secp256k1 ECDSA key. Two facts make that a long-term liability once a cryptographically relevant quantum computer exists:

  1. ECDSA over secp256k1 is broken by Shor's algorithm. A sufficiently large quantum computer recovers the private key from the public key.
  2. An EOA reveals its public key the first time it sends a transaction (the public key is recovered from the signature). So the exposure is not hypothetical for any account that has ever transacted; the key material an attacker needs is already on chain.

The threat is not "funds move today." It is "an adversary records the chain now and breaks the keys later." That is the harvest-now-decrypt-later threat applied to account authorization: long-dormant, high-value holdings sitting under a classical key are exactly the accounts most worth attacking on a horizon of years. The mitigation is to move such holdings, while the classical key is still safe, onto an account whose spending authority is a post-quantum key.

Aere already ships the destinations for that move as live primitives. What was missing, and what this document delivers, is a clean, no-custody path to perform the move. That gap is quantum layer 11 in aerenew/build-tracker/control.json.

A.2 The destinations: Aere's live post-quantum account primitives

The migration does not invent a new account type. It targets the ones Aere already runs:

Primitive What it is Auth Status
AerePQCAccount ERC-4337 v0.7 smart account owned solely by a NIST Falcon-512 key (897-byte pubkey) Falcon-512 only, no ECDSA fallback live sample 0xa42a5e7F...4326f [VERIFY]
AerePQCAccountFactory CREATE2 factory; account address is a pure function of (falconPubKey, salt) n/a (permissionless deploy) live 0xd5315Ea7...CE58 [VERIFY]
AereHybridAuth Defense-in-depth: authorizes only if BOTH an ECDSA leg and a Falcon-512 leg verify over the same 32-byte hash ECDSA AND Falcon-512 repo source, no mainnet address [VERIFY]
AereFalcon512Verifier Live Solidity Falcon-512 verifier the accounts delegate to n/a live 0x4E8e9682...D8fFC [VERIFY]
AerePQCKeyRegistry Proof-of-possession key registry n/a live 0x1eCa3c5A...3691 [VERIFY]

Two destination shapes matter for a holder:

  • PQC account (Falcon-512 only). Spending authority becomes a single lattice key. Strongest post-quantum posture, but there is no ECDSA fallback: lose the Falcon key and the account is gone. Register the key in AerePQCKeyRegistry and set PQC social-recovery guardians BEFORE moving material value.
  • Hybrid account (ECDSA + Falcon-512, via AereHybridAuth). Transition-era: an attacker must break BOTH secp256k1 and Falcon-512. Good for a staged move while a holder still wants a classical leg. AereHybridAuth is repo source with no mainnet address as of 2026-07-19, so standing it up is a founder/deployer step [VERIFY].

The counterfactual address of a Falcon key's PQC account is pure CREATE2 math and can be derived off-chain (see the migration toolkit, aerenew/pqc-migration-toolkit, predictPqcAccountAddress, verified there against the live sample account). The account can receive assets at that address before it is deployed, and be deployed later via createAccount, which is permissionless and idempotent.

A.3 The contract: AereAccountMigrator

File: aere-contracts/contracts/pqc/AereAccountMigrator.sol (solc 0.8.23, OpenZeppelin SafeERC20 + ReentrancyGuard).

It is a pure conduit that atomically moves a caller-specified set of ERC-20 balances, and optionally native AERE, from the caller (the old EOA) to a destination post-quantum account, in one transaction authorized by the caller's ECDSA key. Two entrypoints:

  • migrate(destination, tokens[], amounts[], moveNative): move to an explicit destination address.
  • migrateToPqcAccount(factory, falconPubKey, salt, expectedDestination, tokens[], amounts[], moveNative): DERIVE the destination from a Falcon-512 key via the live AerePQCAccountFactory.predictAddress, so the holder gets cryptographic assurance they are sweeping into the post-quantum account their Falcon key controls, and nowhere else. expectedDestination is an optional guard: if non-zero it must equal the derived address, else the call reverts.

A read-only predictPqcAccount(factory, falconPubKey, salt) lets a frontend confirm the destination before signing.

Authorization: pre-approval, or EIP-7702

The move is authorized by the caller's classical ECDSA key. The migrator does not invent an authorization scheme; it uses ordinary ERC-20 allowances. Two supply patterns, both documented:

  1. Pre-approval (default, works today, no protocol feature). The holder sends one approve(migrator, amount) per ERC-20, then calls migrate. Each safeTransferFrom consumes the allowance and moves the tokens straight to the destination.
  2. EIP-7702 delegation (single-transaction UX). Aere supports EIP-7702 (see aerenew/docs/AERE-EIP-COMPATIBILITY-MATRIX.md, row 7702 "Supported", the EOA-delegation primitive Aere carries in place of EIP-3074). The EOA can, in one transaction, temporarily set its account code to a batching delegate that both approves and calls migrate, removing the separate approve transactions. The migrator is agnostic to which pattern is used; under EIP-7702 the caller is still the EOA and every invariant below is unchanged.

No custody, no admin (the core safety property)

  • In migrate the caller is the from of every SafeERC20.safeTransferFrom, and the caller-specified destination is the to. Tokens move caller to destination in one hop. The migrator is never the recipient, so it never holds an ERC-20 balance.
  • Native AERE (msg.value) is forwarded, in full, to that same destination, inside the same call, guarded by nonReentrant.
  • There is no owner, no admin, no upgrade, no initializer. There is no withdraw, rescue, or sweep function, so anything accidentally sent to the migrator can never be pulled out by anyone (stuck-but-safe, not drainable).
  • There is no code path that sends value to any address other than the caller-specified destination.

Fail-closed and atomic

  • destination must be non-zero (ZeroDestination).
  • Token and amount arrays must be equal length (LengthMismatch); each token non-zero (ZeroToken); each amount non-zero (ZeroAmount).
  • Native is strict: moveNative true requires non-zero msg.value (ZeroNative); any msg.value sent while moveNative is false reverts (StrayNative), so value can never be stranded. An empty request (no tokens, no native) reverts (NothingToMigrate).
  • A single failing transfer reverts the WHOLE transaction (SafeERC20 reverts on a false/failing ERC-20), so the migration is all-or-nothing. It can never leave a holder with a partially drained old account.

The Migrated event

Migrated(address indexed oldAccount, address indexed newAccount, address[] tokens, uint256[] amounts, uint256 nativeAmount) binds the old classical account, the new post-quantum account, and the exact asset set that moved. This is the on-chain, auditable record of a migration.

A.4 The user flow

  1. Choose a destination. Generate (or reuse) a Falcon-512 key; derive its counterfactual AerePQCAccount address off-chain (toolkit predictPqcAccountAddress, or the on-chain view predictPqcAccount). For a hybrid transition, register an AereHybridAuth identity instead.
  2. Prepare recovery FIRST. If the destination is Falcon-only, register the key in AerePQCKeyRegistry (proof-of-possession) and set PQC social-recovery guardians before moving material value. A Falcon-only account has no ECDSA fallback.
  3. Approve. From the old EOA, approve(migrator, amount) per ERC-20 (or bundle the approvals with the migrate via EIP-7702).
  4. Migrate. Call migrateToPqcAccount(...) (recommended, destination is bound to your Falcon key) or migrate(destination, ...). One transaction moves the whole asset set plus optional native AERE to the destination. The Migrated event is your receipt.
  5. Deploy the account when convenient. AerePQCAccountFactory.createAccount is permissionless and idempotent; the account can be deployed before or after the assets arrive at its counterfactual address.

The migration toolkit (aerenew/pqc-migration-toolkit) is the companion that scans an account's quantum exposure (RED/YELLOW/GREEN), derives the destination address, and prints a step-by-step plan and a gas estimate. This contract is the on-chain executor for step 4.

A.5 Test result (real)

Suite: aere-contracts/test/account-migrator.test.js. Run from aerenew/contracts with npx hardhat test test/account-migrator.test.js.

Result on 2026-07-19: 9 passing.

The destination in the strong-path tests is a REAL AerePQCAccount deployed through the REAL AerePQCAccountFactory (the Falcon verifier is mocked because a genuine Falcon-512 signature cannot be produced inside the hardhat process; the CREATE2 derivation and every asset movement are fully real). Covered:

  • a user migrates ERC-20 balances from an EOA to a destination in one tx (both migrate and the Falcon-derived migrateToPqcAccount);
  • native AERE forwards to the destination, with nothing stuck in the migrator;
  • a zero destination reverts (ZeroDestination);
  • a failed token transfer reverts the whole migration, and the first (would-be successful) token is NOT moved (atomicity);
  • the migrator sends only to the specified destination; an unrelated third party receives nothing;
  • no admin function exists (no owner/withdraw/rescue/sweep/transferOwnership/ initialize in the ABI), and tokens force-sent to the migrator cannot be pulled out by anyone;
  • fail-closed native handling (StrayNative, ZeroNative, NothingToMigrate) and malformed asset sets (LengthMismatch, ZeroToken, ZeroAmount).

Part B. Network key rotation (design only, founder + audit gated)

This half is DESIGN. Nothing here is a today-action, and nothing here is executed by any contract in this repository. It is documented so the end-to-end quantum-resistance picture is complete and honest about what remains.

There are two network-level classical keys, and they are NOT the same kind of problem.

B.1 Foundation operator key rotation (operational key management) [MEASURE]

What it is. The Foundation operator key is the ECDSA key that holds privileged roles on operational contracts (for example NavOracle attestation, compliance registries, the deployer/owner roles that have not yet been handed to the Timelock). It is a normal account key, not a consensus key.

Why rotate it. Same harvest-now-decrypt-later logic as a user account: a long-lived operator key under ECDSA is a standing liability on a multi-year horizon.

How, honestly. This is an operational key-management step, not a protocol change:

  1. Stand up the post-quantum destination for the operator authority. The natural choice is an AerePQCAccount (Falcon-512) or, better for an operator, a post-quantum threshold account (the AereThresholdAccount family, PQC t-of-n 4337) so operator authority is not a single key. [VERIFY] which threshold account is the intended operator destination.
  2. Transfer each privileged role from the old ECDSA operator to the new post-quantum account. On contracts that expose role transfer (owner / role-grant), this is a sequence of ordinary role-transfer transactions. On immutable core-economic contracts (Sink, sAERE, burn) there is no admin to rotate, by design, so they need nothing.
  3. Where governance is the intended long-term authority, the path is role to Timelock, and the Post-Quantum Governance design (aerenew/docs/AERE-POST-QUANTUM-GOVERNANCE.md) covers granting a PQC threshold account the proposer/executor role on the Timelock.

Gating. This is founder-gated because it moves real privileged authority over live contracts, and it is signed by the Foundation key, not by any contract here. It is NOT coupled to consensus. The cost/outcome of a full rotation across the live role set is [MEASURE] (it depends on the exact contracts and roles enumerated at rotation time). No role is rotated by publishing this document.

B.2 Validator signing key rotation (coupled to consensus-PQC) [MEASURE]

What it is. Each validator seals QBFT blocks with a classical secp256k1 key. This is the consensus signing key, not an application account key.

The honest coupling. Today validator seals are classical ECDSA QBFT. Rotating a validator's signing key to a post-quantum scheme is NOT an isolated key swap: the consensus protocol itself has to accept and verify post-quantum seals. In other words, a validator cannot "rotate to a Falcon seal" while the rest of the network still expects an ECDSA seal. Validator-key rotation to PQ is therefore coupled to the consensus-PQC activation, which is tracked as its own gated item:

  • The in-place PQC activation work (hybrid ECDSA + Falcon BLOCKING seal, activated via an anchor contract on the live chain, proven at small N in isolation) is the mechanism that would let the seal set become post-quantum without a re-genesis.
  • That flip is HIGH risk (it can halt the chain), and its gates are: N greater than or equal to 9 validators, a soak period, an external consensus audit, and the founder's explicit GO. See the mainnet queue in aerenew/build-tracker/control.json ("Consens post-quantum LIVE", risk MARE).

So the sequence is: consensus-PQC activation FIRST (gated), and validator signing keys rotate to their post-quantum form AS PART OF that activation, not before it and not independently. Doing validator-key rotation "early" has no meaning while consensus verifies classical seals.

Gating. Founder-gated AND external-audit-gated AND dependent on the consensus-PQC item. Not a today-action. Nothing in this repository activates it. The outcome (fork-freedom, halt/recover behavior at the target N) is [MEASURE] against a real soak at N greater than or equal to 9.

B.3 Summary of the network half

Network key Kind of change Coupled to consensus? Gate Today-action?
Foundation operator key Operational role transfer to a PQC account No Founder No (founder-signed op)
Validator signing keys Consensus seal scheme change Yes Founder + external audit + N>=9 + soak No

The single most important honesty point: none of this makes consensus post-quantum by itself. The user account migration in Part A is a real, deployable-today, no-custody contract that hardens ACCOUNT authorization. The network half in Part B is the operator-key rotation (an operational step) plus the validator-key rotation (inseparable from the gated consensus-PQC flip). This is the last quantum-resistance layer, and it is largely gated, not a switch to flip tonight.


Part C. Authority residue: why moving the assets is not finishing the job

Added 2026-07-19. Part A moves an account's ASSETS. This part addresses what Part A leaves behind, which is the more dangerous half.

C.1 The gap

After a perfect Part A migration, the old classical EOA is empty. It is not harmless. It still carries:

  1. Every ERC-20 allowance it ever granted. approve state lives on the token contract keyed by owner, and sweeping a balance does not touch it. Any token that later arrives at the old EOA is immediately drainable by every spender still holding a non-zero allowance.
  2. Any role, ownership, or membership granted to it on other contracts. Sweeping an ERC-20 balance does not transfer an onlyOwner role.
  3. Its status as a valid payment destination. Airdrops, refunds, vesting unlocks, exchange withdrawals, and counterparties working from an address book keep sending to it. The account keeps re-accruing value under a Shor-breakable key.
  4. Its status as a valid signer for any EIP-712 or EIP-1271 flow that checks only ECDSA.

So an account that has "migrated" can be empty today and hold a meaningful balance again in six months, under exactly the key the migration was meant to retire. The adversary in the model does not need to act now. Harvest-now-decrypt-later means they break the key years later and then collect whatever has pooled there since.

C.2 Which accounts are actually exposed, precisely

The exposure that matters is exposure of the PUBLIC KEY, not of the address. An Aere address is keccak(pubkey)[12:], a hash, and a hash is not Shor-breakable. The public key itself is what Shor's algorithm needs.

Account state Public key on chain? Exposed to a future quantum adversary?
Never sent a transaction (receive-only) No. Only the address hash is public NOT exposed today. The preimage has never been published
Has sent at least one transaction Yes. The pubkey is recoverable from the signature EXPOSED. The material an adversary needs is already public and already archived
Has signed any off-chain message that was published (EIP-191, EIP-712, a permit) Yes, same recovery applies EXPOSED, even with zero on-chain transactions
Deployed contract / smart account No secp256k1 key of its own Not exposed as a key. Exposure is whatever its OWNER key is

The practical reading: any address that has ever spent anything is already exposed, and that is the overwhelming majority of active accounts. Nothing can un-expose them. The only remaining lever is to stop keeping value there, and to stop the old key from carrying authority. That is what Part A and this part do respectively.

A receive-only account is genuinely in a better position, and it has a strictly better migration: move the funds in a single transaction to a post-quantum account. That first spend does expose the key, but it exposes it at the moment the value is already leaving, which is the one moment exposure costs nothing.

This document does NOT claim a count or percentage of exposed accounts on chain 2800. That would require indexing the full chain history and it was not measured.

C.3 What is a contract, and what deliberately is not

Two things are needed. Only one of them should be a contract, and saying so plainly is the point.

Allowance revocation is NOT a new contract. approve can only be called by the token owner, so no third-party contract can revoke on an EOA's behalf. The revocation has to be executed BY the account. Aere Network already ships the tool: AereDelegate7702V2.executeBatch in aerenew/contracts/contracts/delegation batches the approve(spender, 0) calls into one EIP-7702 transaction from the EOA itself. Writing a second contract for this would add a security surface and buy nothing, so none was written. Enumerating the live spender set per token is an indexer job and belongs in the migration toolkit, not in Solidity.

Retirement declaration IS a contract, because it needs to be permanent, public, and machine-readable, and no existing primitive provides that.

C.3.1 Evidence that no account-level retirement registry already existed

"Nothing existing provides it" is the kind of claim that has to be auditable rather than asserted, so here is the full search and the full result. Run from aerenew/contracts/contracts:

grep -rnE "[Rr]etire|RETIRE" --include=*.sol .

Result on 2026-07-20: 68 matching lines across 9 files. 46 of those lines are in the new pqc/AereAccountRetirement.sol itself, leaving 22 pre-existing lines across 8 files. An earlier build note cited only four hits, which was a filtered subset presented as the whole result. That was wrong and this section replaces it. The conclusion is unchanged, but it is now checkable. Every pre-existing hit:

File Lines What it actually is Account-level retirement registry?
AereCoinbaseSplitterV2.sol 1 Prose comment saying the V1 contract was retired No. English usage in a comment, no code
aere-contracts/contracts/faucet/AereFaucetV2.sol 1 Comment: the faucet can be "retired" by setting dripAmount to 0 No. Describes a parameter, not a registry
aere-contracts/contracts/modular/AerePQCEncryptedIntent.sol 5 retireEncapKey(), EncapKeyRetired, an active flag No. Retires one advertised ML-KEM encapsulation key so no NEW intent targets it. Scoped to a key, not to the account, and the account stays fully active
aere-contracts/contracts/modular/AerePQCKeyExchange.sol 7 retireKemKey(), KemKeyRetired, active flag, "register / rotate / retire" No. Same shape: retires one advertised ML-KEM key for handshake targeting. The account keeps transacting
aere-contracts/contracts/mpc/AereThresholdRegistry.sol 3 Status.RETIRED enum member, CommitteeRetired event, one comment No, and weaker than it looks. The subject is a threshold COMMITTEE, not an EOA. Also, Status.RETIRED is never assigned and CommitteeRetired is never emitted anywhere in the file: the only status writes are ACTIVE (line 174, 358) and PAUSED (line 350). The state is declared and unreachable
aere-contracts/contracts/pqc/AereHybridAuthorizer.sol 1 Comment: the Foundation "retires a scheme" on the registry No. Subject is a signature SCHEME, protocol-wide
aere-contracts/contracts/pqc/AerePQCOrderAuthorizer.sol 1 Same scheme-retirement comment No. Same, subject is a scheme
aere-contracts/contracts/pqc/AerePQCKeyRegistry.sol 3 Rotation "retires the old one", plus append-only key lists that include retired keys No. Subject is a PQC KEY belonging to an identity. Retiring a key is the opposite direction of travel: it presumes the identity continues, under a new key. There is no way to mark the classical account itself as retired, and no successor pointer

The pattern across all 22 is the same and it is the reason the gap was real. Every pre-existing use retires a KEY, a SCHEME or a COMMITTEE, always as a rotation step that presumes the subject carries on under new material. None of them takes an EOA as the subject, none of them is a one-way latch, and none of them publishes a successor account. AerePQCKeyRegistry is the closest and still does not fit: it would let an identity mark a Falcon key stale, never the secp256k1 account.

Search limits, stated: this is a text search over *.sol under aerenew/contracts/contracts only. It would not catch a registry that implements the same semantics without ever using the word "retire" (for example one built around "deprecate", "sunset" or "revoke" on an account subject), and it does not cover non-Solidity sources or deployed-but-unsourced contracts. Those are the conditions under which the conclusion could still be wrong.

C.4 AereAccountRetirement, the one-way latch

File: aerenew/contracts/contracts/pqc/AereAccountRetirement.sol (solc 0.8.23, no dependencies, no admin, no payable function, holds nothing).

An account calls retire(successor) and irreversibly declares its classical key retired, optionally naming the post-quantum account that replaces it. retireToPqcAccount(factory, falconPubKey, salt, expectedSuccessor) derives the successor from a Falcon-512 key through an AerePQCAccountFactory rather than taking a literal address. Passing the zero successor is legitimate and supported, for an owner who does not want to publicly link the old address to the new one.

What deriving the successor does and does not guarantee. Because the pointer can never be corrected, it is worth being exact about which mistakes this removes.

It removes the transcription class: a mistyped, truncated, wrong-checksum or clipboard-swapped successor address. The caller never supplies the successor at all, so there is nothing to mistype; the address is CREATE2 math over the Falcon public key the caller holds. That is a real and worthwhile guarantee, and it is why the entrypoint exists.

It does NOT remove the wrong-successor class in general. factory is a caller-supplied address and the contract does not validate it. There is no allowlist, no code check and no interface assertion, so a wrong or hostile factory returns a wrong successor and that successor is latched permanently. A hostile factory can return an address it controls. What the design does is move the trust from "did I type twenty bytes correctly" to "do I trust this factory", which is a better question to have to answer, not the absence of one.

The caller must therefore supply a factory they trust. Verify the factory address out of band against the published deployment record before calling, and prefer passing a non-zero expectedSuccessor so the derived address must additionally match one reviewed off chain. Those two checks together, not the function alone, are what make the permanent statement safe.

An earlier revision of this section and of the contract NatSpec said this made the wrong-successor mistake "impossible". That was an overstatement of a narrower true guarantee and has been corrected here and in aerenew/contracts/contracts/pqc/AereAccountRetirement.sol.

Why the latch is one-way. The adversary is someone who breaks the secp256k1 key LATER and can then sign anything the original owner could. If retirement were revocable that adversary would un-retire the account and continue. If the successor were mutable they would repoint it at themselves and harvest anyone honoring the pointer. Immutability is what lets the declaration survive the compromise it exists to outlive. The honest owner acts once, while the key is still safe, and the statement outlives the key. This is the property the test suite targets directly.

Integrators read isRetired(account), successorOf(account), or call the reverting guard requireActive(account) at the top of a sensitive function.

C.5 What this honestly cannot do

Stated plainly, because a registry is easy to oversell:

  • It cannot stop native AERE or a non-integrating ERC-20 from being sent to a retired address. Base-protocol transfers consult no registry, and making them do so would be a consensus change. Aere is not making one. Consensus on chain 2800 is and remains classical secp256k1 ECDSA QBFT.
  • It cannot revoke an allowance. See C.3.
  • It cannot help an account whose key is ALREADY broken. An adversary holding the key can retire it too, or simply move the assets. This works only in the window where the honest owner acts first, which is the entire argument for migrating early rather than waiting for a quantum event.
  • It binds only the contracts, wallets, indexers and counterparties that CHOOSE to read it. It is an opt-in enforcement surface and an advisory signal, not a protocol rule.

Its value is being a permanent, checkable, machine-readable statement of intent that an attacker who later owns the key cannot retract.

C.6 Test result (real)

Suite: aerenew/contracts/test/account-retirement.test.js. Run from aerenew/contracts:

npx hardhat test test/account-migrator.test.js test/account-retirement.test.js

Result on 2026-07-19: 20 passing (9 pre-existing migrator tests, 11 new retirement tests). Covered by the new tests: retirement with and without a published successor; Falcon-derived successor through the real factory; mismatch guard; the latch holds against an attacker who has the key (cannot un-retire, cannot repoint, via either entrypoint); retirement is strictly self-scoped so no third party or admin can retire another account; no admin surface and no payable function; the self-referential successor is rejected; requireActive guards correctly; a never-retired account reads as a clean zero record.

The "no payable function" assertion was checked against a positive control: the same ABI inspection reports 2 payable functions on AereAccountMigrator and 0 on AereAccountRetirement, so the assertion is not passing vacuously.

Deployment status: NOT DEPLOYED. This contract exists in the repository and passes its suite. It is not on mainnet 2800 and no transaction was sent. Deploying it is founder-gated.


Files

  • aere-contracts/contracts/pqc/AereAccountMigrator.sol (contract)
  • aere-contracts/test/account-migrator.test.js (Hardhat suite, 9 passing)
  • aerenew/contracts/contracts/pqc/AereAccountRetirement.sol (contract, Part C, NOT deployed)
  • aerenew/contracts/test/account-retirement.test.js (Hardhat suite, 11 passing)
  • aere-contracts/contracts/delegation/AereDelegate7702V2.sol (executeBatch, the existing tool for batch allowance revocation; no new contract was written for revocation)
  • aerenew/pqc-migration-toolkit/ (scanner + derivation + cost simulator)
  • aere-contracts/contracts/pqc/AerePQCAccount.sol, AerePQCAccountFactory.sol, AereHybridAuth.sol (destinations)
  • aerenew/docs/AERE-POST-QUANTUM-GOVERNANCE.md (Timelock role grant to a PQC threshold account, the governance path for operator authority)
  • aerenew/docs/AERE-EIP-COMPATIBILITY-MATRIX.md (EIP-7702 support)