// readiness.js, the quantum-readiness cost simulator. Given an account (its scan // result and, optionally, its enumerated assets) and a target post-quantum // scheme, it produces a readiness REPORT: the color, which assets need moving, // the one-time migration gas, and the ongoing per-authorization cost. // // SOURCING DISCIPLINE (load-bearing, not decoration): // - PQC verify / auth / account-deploy gas is CITED from committed artifacts // (AERE-BENCHMARK-REPORT.md Part C.1, pqc-account.json). Never invented. // - Asset-MOVE gas (ERC-20 transfer/approve) is NOT in any Aere benchmark and // is NOT measured here, so it is a clearly-labeled [VERIFY] planning // estimate, anchored to the one asset-move number this session DID measure // live: a native AERE send estimated at 21,246 gas via eth_estimateGas on // https://rpc.aere.network (chain 2800, ~block 10,416,783, 2026-07-19). // - Gas price is the live 1 Gwei base-fee floor on 2800. import { PQC, CLASSICAL, FALCON512_SOLIDITY_VERIFIER_GAS, AERE_PQC_ACCOUNT_MEASURED_USEROP_GAS } from './precompiles.js'; import { ACCOUNT_DEPLOY_GAS, GAS_PRICE_GWEI } from './simulate.js'; // Native AERE transfer. 21,000 is the fixed EVM base cost for a value-only tx; // the live estimate came back 21,246 (a touch above 21,000 for warm-account // bookkeeping). We use the measured live figure as the planning number. export const NATIVE_SEND_GAS = 21246; // [MEASURED-FRESH] eth_estimateGas, rpc.aere.network, 2026-07-19 export const NATIVE_SEND_GAS_SOURCE = '[MEASURED-FRESH] eth_estimateGas for a native AERE send on https://rpc.aere.network (chain 2800), 2026-07-19 => 21,246 gas (21,000 protocol base + warm-account overhead).'; // ERC-20 move gas: NOT measured on Aere, NOT in the benchmark report. Planning // estimates only, flagged [VERIFY]. Refine per token with eth_estimateGas. export const ERC20_TRANSFER_GAS = 65000; // [VERIFY] cold-recipient transfer, planning estimate export const ERC20_APPROVE_GAS = 46000; // [VERIFY] approve, planning estimate export const MIGRATOR_PER_TOKEN_GAS = 40000; // [VERIFY] one safeTransferFrom hop inside migrate() export const MIGRATOR_BASE_GAS = 30000; // [VERIFY] migrate() call overhead (checks, event) export const ERC20_MOVE_GAS_SOURCE = '[VERIFY] ERC-20 move gas is a planning estimate, NOT measured on Aere and NOT in AERE-BENCHMARK-REPORT.md. Refine with eth_estimateGas against the real token+allowance.'; // Per-scheme ongoing authorization gas. verifyTxGas is the native-precompile // verify-and-record receipt [CITED: AERE-BENCHMARK-REPORT.md Part C.1]. const SCHEME_AUTH = { falcon512: { label: 'Falcon-512 (native precompile)', authGas: PQC.falcon512.verifyAndRecordTxGas, cited: 'AERE-BENCHMARK-REPORT.md Part C.1 (86,336)' }, falcon1024: { label: 'Falcon-1024 (native precompile)', authGas: PQC.falcon1024.verifyAndRecordTxGas, cited: 'AERE-BENCHMARK-REPORT.md Part C.1 (145,496)' }, mldsa44: { label: 'ML-DSA-44 (native precompile)', authGas: PQC.mldsa44.verifyAndRecordTxGas, cited: 'AERE-BENCHMARK-REPORT.md Part C.1 (351,050)' }, slhdsa128s: { label: 'SLH-DSA-128s (native precompile)', authGas: PQC.slhdsa128s.verifyAndRecordTxGas, cited: 'AERE-BENCHMARK-REPORT.md Part C.1 (558,276)' }, hybrid: { label: 'Hybrid ECDSA + Falcon-512 (native)', authGas: PQC.falcon512.verifyAndRecordTxGas + CLASSICAL.ecrecover.marginalGas, cited: 'Part C.1 Falcon-512 86,336 + ecrecover 3,000' }, // The path a currently-DEPLOYED AerePQCAccount actually takes (Solidity Falcon // verifier), measured on-chain. This is the honest default: it is what a // migration TODAY costs per auth, ~122x the native projection. falcon512_solidity: { label: 'Falcon-512 (deployed AerePQCAccount, Solidity verifier)', authGas: AERE_PQC_ACCOUNT_MEASURED_USEROP_GAS, cited: 'pqc-account.json handleOpsGasUsed 10,278,313 (real on-chain receipt)' }, }; export function readinessSchemeList() { return Object.keys(SCHEME_AUTH); } function gasToAere(gas, gwei = GAS_PRICE_GWEI) { return gas * gwei * 1e-9; } /** * Compute the one-time asset-move gas for a set of assets on a given path. * @param {{nativeNeedsMove:boolean, erc20Count:number}} assets * @param {'direct'|'migrator'} path */ export function assetMoveGas(assets, path = 'direct') { const { nativeNeedsMove = false, erc20Count = 0 } = assets; const nativeGas = nativeNeedsMove ? NATIVE_SEND_GAS : 0; let erc20Gas = 0; const breakdown = []; if (nativeNeedsMove) breakdown.push({ item: 'native AERE send', gas: NATIVE_SEND_GAS, source: NATIVE_SEND_GAS_SOURCE }); if (path === 'direct') { erc20Gas = erc20Count * ERC20_TRANSFER_GAS; if (erc20Count) breakdown.push({ item: `${erc20Count} x ERC-20 transfer`, gas: erc20Gas, source: ERC20_MOVE_GAS_SOURCE }); } else if (path === 'migrator') { const approveGas = erc20Count * ERC20_APPROVE_GAS; const migrateGas = erc20Count > 0 || nativeNeedsMove ? MIGRATOR_BASE_GAS + erc20Count * MIGRATOR_PER_TOKEN_GAS : 0; erc20Gas = approveGas + migrateGas; if (erc20Count) breakdown.push({ item: `${erc20Count} x ERC-20 approve`, gas: approveGas, source: ERC20_MOVE_GAS_SOURCE }); if (erc20Count > 0 || nativeNeedsMove) breakdown.push({ item: 'atomic migrate() sweep', gas: migrateGas, source: ERC20_MOVE_GAS_SOURCE }); } else { throw new Error(`unknown path "${path}"`); } return { total: nativeGas + erc20Gas, nativeGas, erc20Gas, breakdown }; } /** * Build a quantum-readiness report for ONE account. * * @param {object} p * @param {object} p.scan a scan report ({ address, kind, readiness, ... }). * @param {object} [p.assets] enumerateAssets() output (native + tokens + movable). * @param {string} [p.scheme] target scheme (default falcon512_solidity, the * honest deployed-reality path). Native projection also shown. * @param {'direct'|'migrator'} [p.path] asset-move path (default direct). * @param {boolean} [p.deployAccount] count the CREATE2 deploy (default: only if * the account is an EOA, since a contract may already be PQC-capable). * @param {number} [p.projectedAuths] ongoing auths to project the running cost. * @param {number} [p.gwei] gas price (default 1). */ export function buildReadinessReport({ scan, assets, scheme = 'falcon512_solidity', path = 'direct', deployAccount, projectedAuths = 1, gwei = GAS_PRICE_GWEI, } = {}) { if (!scan) throw new Error('buildReadinessReport requires a scan report'); const model = SCHEME_AUTH[scheme]; if (!model) throw new Error(`unknown scheme "${scheme}". Known: ${readinessSchemeList().join(', ')}`); const isEoa = scan.kind === 'eoa'; const needDeploy = deployAccount ?? isEoa; // asset picture let nativeNeedsMove = false; let erc20ToMove = []; if (assets) { nativeNeedsMove = !!(assets.native && assets.native.needsMove); erc20ToMove = (assets.tokens || []).filter((t) => t.needsMove); } const erc20Count = erc20ToMove.length; const deployGas = needDeploy ? ACCOUNT_DEPLOY_GAS : 0; const move = assetMoveGas({ nativeNeedsMove, erc20Count }, path); const oneTimeGas = deployGas + move.total; const ongoingAuthGas = model.authGas * projectedAuths; // native-precompile projection for the same scheme (context, not the deployed cost) const nativeProjection = scheme === 'falcon512_solidity' ? { label: SCHEME_AUTH.falcon512.label, authGas: SCHEME_AUTH.falcon512.authGas, cited: SCHEME_AUTH.falcon512.cited } : null; return { address: scan.address, kind: scan.kind, readiness: scan.readiness, readinessReason: scan.reason, assetsToMove: { native: nativeNeedsMove ? { symbol: assets.native.symbol, balanceWei: assets.native.balanceWei } : null, erc20: erc20ToMove.map((t) => ({ token: t.token, symbol: t.symbol, balance: t.balance })), count: (nativeNeedsMove ? 1 : 0) + erc20Count, note: assets ? null : 'No asset enumeration supplied. Pass --rpc (and optional --tokens) to enumerate movable assets.', }, scheme: { key: scheme, ...model }, nativeProjection, gas: { deploy: deployGas, deploySource: needDeploy ? 'pqc-account.json createGasUsed 1,493,084 [CITED]' : 'no deploy (contract already exists / not an EOA)', assetMove: move.total, assetMoveBreakdown: move.breakdown, oneTimeTotal: oneTimeGas, perAuth: model.authGas, perAuthSource: '[CITED: ' + model.cited + ']', projectedAuths, ongoingAuthTotal: ongoingAuthGas, }, cost: { gwei, oneTimeAere: gasToAere(oneTimeGas, gwei), ongoingAuthAere: gasToAere(ongoingAuthGas, gwei), }, scope: 'ACCOUNT/APPLICATION quantum-readiness. This does not, and cannot, make Aere consensus post-quantum (chain 2800 seals with classical secp256k1 QBFT). BN254 ZK verifiers remain classical.', }; } /** Render a readiness report as plain text for the CLI. */ export function formatReadinessReport(r) { const L = []; const aere = (n) => n.toFixed(9) + ' AERE'; L.push(''); L.push(`Quantum-readiness report ${r.address}`); L.push(` status: ${r.readiness} (${r.kind})`); if (r.readinessReason) L.push(` reason: ${r.readinessReason}`); L.push(''); L.push(' Assets to move:'); if (r.assetsToMove.note) { L.push(` - ${r.assetsToMove.note}`); } else if (r.assetsToMove.count === 0) { L.push(' - none detected (nothing to migrate)'); } else { if (r.assetsToMove.native) L.push(` - native AERE: ${r.assetsToMove.native.balanceWei} wei`); for (const t of r.assetsToMove.erc20) L.push(` - ERC-20 ${t.symbol || ''} ${t.token}: ${t.balance} (base units)`); } L.push(''); L.push(' One-time migration gas:'); L.push(` deploy target account: ${r.gas.deploy.toLocaleString().padStart(12)} (${r.gas.deploySource})`); for (const b of r.gas.assetMoveBreakdown) L.push(` ${(b.item + ':').padEnd(24)}${b.gas.toLocaleString().padStart(12)}`); L.push(` one-time TOTAL: ${r.gas.oneTimeTotal.toLocaleString().padStart(12)} = ${aere(r.cost.oneTimeAere)} at ${r.cost.gwei} Gwei`); L.push(''); L.push(' Ongoing per-authorization cost (post-migration):'); L.push(` scheme: ${r.scheme.label}`); L.push(` per auth: ${r.gas.perAuth.toLocaleString()} gas ${r.gas.perAuthSource}`); L.push(` ${r.gas.projectedAuths} projected auth(s): ${r.gas.ongoingAuthTotal.toLocaleString()} gas = ${aere(r.cost.ongoingAuthAere)}`); if (r.nativeProjection) { L.push(` (native-precompile projection for the same scheme: ${r.nativeProjection.authGas.toLocaleString()} gas/auth [CITED: ${r.nativeProjection.cited}], available once the account verifies Falcon via the native 0x0AE1 precompile instead of the Solidity verifier.)`); } L.push(''); const hasErc20Estimate = r.gas.assetMoveBreakdown.some((b) => b.source && b.source.startsWith('[VERIFY]')); L.push(hasErc20Estimate ? ' Note: ERC-20 move gas figures are [VERIFY] planning estimates, not measured on Aere; native-send gas is a fresh live measurement (21,246). PQC auth gas is [CITED].' : ' Note: PQC auth gas is [CITED]; native-send gas is a fresh live measurement (21,246).'); L.push(` Scope: ${r.scope}`); L.push(''); return L.join('\n'); }