#!/usr/bin/env node // aere-pqc.js, the unified Aere Quantum Migration Toolkit CLI. One entry point // wrapping the whole product: scan an account, simulate fleet migration cost, // build an unsigned migration transaction sequence, and print a per-account // quantum-readiness report. // // aere-pqc scan
--rpc // aere-pqc simulate --count 100 --scheme hybrid --auths 5 // aere-pqc build-migration --eoa 0x.. --falcon 0x09.. [--path direct|migrator] // aere-pqc readiness
--rpc [--tokens 0x..,0x..] [--scheme ..] // // Nothing in this CLI signs, deploys, or broadcasts. The only network calls are // read-only JSON-RPC (eth_getCode/getBalance/call/estimateGas/gasPrice). import { readFileSync } from 'node:fs'; import { fileURLToPath } from 'node:url'; import { dirname, join } from 'node:path'; import { scanLive, scanBytecode, isValidAddress } from './lib/scanner.js'; import { simulateMigration, formatSimulation, schemeList } from './lib/simulate.js'; import { buildMigrationTransactions, enumerateAssets, estimateMigrationGas, predictTargetAddress, } from './lib/migrator.js'; import { buildReadinessReport, formatReadinessReport, readinessSchemeList } from './lib/readiness.js'; const __dirname = dirname(fileURLToPath(import.meta.url)); const DEFAULT_RPC = 'https://rpc.aere.network'; const isTTY = process.stdout.isTTY; const C = isTTY ? { b: (s) => `\x1b[1m${s}\x1b[0m`, dim: (s) => `\x1b[2m${s}\x1b[0m`, g: (s) => `\x1b[32m${s}\x1b[0m`, y: (s) => `\x1b[33m${s}\x1b[0m` } : { b: (s) => s, dim: (s) => s, g: (s) => s, y: (s) => s }; function loadSampleKey() { const p = join(__dirname, '..', 'contracts', 'deployments', 'pqc-account.json'); return JSON.parse(readFileSync(p, 'utf8')).sampleAccount.falconPubKey; } function parseArgs(argv) { const a = { _: [] }; for (let i = 0; i < argv.length; i++) { const t = argv[i]; if (t.startsWith('--')) { const key = t.slice(2); const next = argv[i + 1]; if (next === undefined || next.startsWith('--')) { a[key] = true; } else { a[key] = next; i++; } } else a._.push(t); } return a; } function topUsage(code = 0) { console.log(`${C.b('Aere Quantum Migration Toolkit')} (chain 2800) Commands ${C.b('scan')}
--rpc classify quantum exposure (RED/YELLOW/GREEN) ${C.b('simulate')} --count [--scheme ] fleet migration-cost projection ${C.b('build-migration')} --eoa 0x.. --falcon 0x09.. build UNSIGNED migration txs ${C.b('readiness')}
--rpc one-account readiness + cost report Global --rpc JSON-RPC endpoint (default ${DEFAULT_RPC}) --json machine-readable output --help this help; or " --help" Scope: this classifies and CONSTRUCTS/SIMULATES migration of ACCOUNT/APPLICATION quantum exposure. It never signs, deploys, or moves funds, and it does NOT make Aere consensus post-quantum (2800 seals with classical secp256k1 QBFT).`); process.exit(code); } // ── scan ───────────────────────────────────────────────────────────────────── async function cmdScan(args) { const addr = args._[0]; const rpc = args.rpc === true ? DEFAULT_RPC : (args.rpc || DEFAULT_RPC); let report; if (args.fixture) { const path = join(__dirname, 'fixtures', args.fixture + '.hex'); report = scanBytecode(`fixture:${args.fixture}`, readFileSync(path, 'utf8').trim()); } else if (args.bytecode) { report = scanBytecode(addr || '(provided bytecode)', args.bytecode); } else if (addr && isValidAddress(addr)) { report = await scanLive(addr, rpc); } else { console.error('scan needs
--rpc , or --fixture , or --bytecode 0x..'); process.exit(1); } if (args.json) { console.log(JSON.stringify(report, (_k, v) => (typeof v === 'bigint' ? '0x' + v.toString(16) : v), 2)); return; } const badge = report.readiness === 'GREEN' ? C.g(' GREEN ') : report.readiness === 'YELLOW' ? C.y(' YELLOW ') : ' RED '; console.log(`\n[${badge}] ${C.b(report.address)}`); console.log(` kind: ${report.kind}${report.live ? ` chainId: ${report.live.chainId} code: ${report.live.codeBytes} bytes` : ''}`); if (report.reason) console.log(` ${C.dim(report.reason)}`); if (report.migration) console.log(` ${C.b('migration')}: ${report.migration}`); console.log(''); } // ── simulate ───────────────────────────────────────────────────────────────── function cmdSimulate(args) { if (args.schemes) { console.log(schemeList().join('\n')); return; } let accounts; if (args.accounts) { accounts = JSON.parse(readFileSync(args.accounts, 'utf8')); } else { const count = parseInt(args.count, 10) || 1; accounts = Array.from({ length: count }, () => ({ scheme: args.scheme || 'falcon512', authsPerAccount: args.auths ? parseInt(args.auths, 10) : 1, deployAccount: !args['no-deploy'], })); } const sim = simulateMigration(accounts, { gweiPrice: args.gwei ? parseFloat(args.gwei) : undefined }); if (args.json) { console.log(JSON.stringify(sim, null, 2)); return; } console.log('\n' + formatSimulation(sim) + '\n'); } // ── build-migration ────────────────────────────────────────────────────────── async function cmdBuildMigration(args) { const falcon = args.falcon && args.falcon !== true ? args.falcon : loadSampleKey(); const usingSample = !(args.falcon && args.falcon !== true); const eoa = args.eoa && args.eoa !== true ? args.eoa : '0x000000000000000000000000000000000000dEaD'; const usingDemoEoa = !(args.eoa && args.eoa !== true); const salt = args.salt ? parseInt(args.salt, 10) : 0; const path = args.path && args.path !== true ? args.path : 'direct'; const rpc = args.rpc === true ? DEFAULT_RPC : args.rpc; // asset selection: explicit --tokens list (with optional --amounts) or, if // --rpc is given, enumerate real balances read-only. let tokens = []; let nativeWei = 0n; const tokenAddrs = args.tokens && args.tokens !== true ? String(args.tokens).split(',').map((s) => s.trim()).filter(Boolean) : []; let enumerated = null; // When moving native AERE from the SAME EOA that pays gas, you cannot move the // ENTIRE balance: the tx itself needs gas. Reserve a buffer (default 0.01 AERE, // override with --native-reserve ) so the native move can actually settle. const nativeReserve = args['native-reserve'] && args['native-reserve'] !== true ? BigInt(args['native-reserve']) : 10000000000000000n; // 1e16 wei = 0.01 AERE let reservedNote = null; const explicitNative = args.native && args.native !== true ? BigInt(args.native) : null; // native amount: explicit --native wins everywhere; else --no-native = 0; else // (only with live enumeration) auto-drain balance minus a gas reserve. if (explicitNative != null) nativeWei = explicitNative; else if (args['no-native']) nativeWei = 0n; if (rpc && !usingDemoEoa) { enumerated = await enumerateAssets({ rpcUrl: rpc, owner: eoa, tokens: tokenAddrs }); const full = BigInt(enumerated.native.balanceWei); if (explicitNative != null) { reservedNote = `native move = explicit --native ${nativeWei} wei (current balance ${full} wei).`; } else if (!args['no-native']) { if (path === 'direct') { nativeWei = full > nativeReserve ? full - nativeReserve : 0n; if (full > 0n) reservedNote = `native move = balance ${full} minus a ${nativeReserve} wei gas reserve = ${nativeWei} wei. NOTE: draining an ACTIVE account races its own spending; prefer --native for a fixed amount.`; } else { nativeWei = full; // migrator forwards msg.value; the EOA still pays gas, so leave headroom beyond this if (full > 0n) reservedNote = `native move = full balance ${full} wei; ensure the migrate tx has gas headroom beyond this (migrator forwards msg.value).`; } } tokens = enumerated.tokens.filter((t) => t.needsMove).map((t) => ({ token: t.token, amountWei: t.balance })); } else if (tokenAddrs.length && args.amounts && args.amounts !== true) { const amts = String(args.amounts).split(',').map((s) => s.trim()); tokens = tokenAddrs.map((t, i) => ({ token: t, amountWei: BigInt(amts[i] || '0') })); } else { tokens = tokenAddrs.map((t) => ({ token: t, amountWei: 1n })); // placeholder amount; user sets real value } const plan = buildMigrationTransactions({ path, eoa, falconPubKey: falcon, salt, tokens, nativeWei, deployAccount: !args['no-deploy'], migrator: args.migrator && args.migrator !== true ? args.migrator : undefined, bindFalcon: !args['no-bind'], }); if (rpc && args.estimate) await estimateMigrationGas(plan, rpc); if (args.json) { console.log(JSON.stringify(plan, (_k, v) => (typeof v === 'bigint' ? v.toString() : v), 2)); return; } console.log(`\n${C.b('Aere post-quantum migration')} (path: ${plan.path})`); if (usingSample) console.log(C.dim(" (demo: target derived from the live sample account's real Falcon-512 key)")); if (usingDemoEoa) console.log(C.dim(' (demo EOA 0x..dEaD; pass --eoa 0xYourAccount for a real build)')); console.log(` from EOA: ${plan.eoa}`); console.log(` factory (live): ${plan.factory}`); console.log(` target account: ${C.b(plan.target)}`); if (enumerated) console.log(` enumerated: ${enumerated.movable} movable asset(s) via ${rpc}`); if (reservedNote) console.log(` ${C.dim(reservedNote)}`); console.log(''); console.log(` ${C.b('Unsigned transactions')} (${plan.transactions.length}). Sign each in your own wallet, in order:`); for (const item of plan.transactions) { console.log(`\n Step ${item.step}. [${item.kind}] ${C.dim('(signed: ' + item.signed + ')')}`); console.log(` ${item.description}`); console.log(` to: ${item.tx.to}`); console.log(` value: ${item.tx.value}`); console.log(` data: ${item.tx.data.length > 50 ? item.tx.data.slice(0, 50) + '..' : item.tx.data} (${(item.tx.data.length - 2) / 2} bytes)`); if (item.gasEstimate != null) console.log(` gas (live estimate): ${item.gasEstimate.toLocaleString()}`); if (item.gasEstimateError) console.log(` gas: ${C.dim('estimate n/a (' + item.gasEstimateError + ')')}`); for (const w of item.warnings || []) console.log(` ${C.y('warn')}: ${w}`); } if (plan.warnings.length) { console.log(`\n ${C.b('Warnings')}:`); for (const w of plan.warnings) console.log(` - ${w}`); } console.log(`\n ${C.dim(plan.scope)}\n`); } // ── readiness ──────────────────────────────────────────────────────────────── async function cmdReadiness(args) { const addr = args._[0]; if (!addr || !isValidAddress(addr)) { console.error('readiness needs
'); process.exit(1); } const rpc = args.rpc === true ? DEFAULT_RPC : (args.rpc || DEFAULT_RPC); const scan = await scanLive(addr, rpc); const tokenAddrs = args.tokens && args.tokens !== true ? String(args.tokens).split(',').map((s) => s.trim()).filter(Boolean) : []; const assets = await enumerateAssets({ rpcUrl: rpc, owner: addr, tokens: tokenAddrs }); const report = buildReadinessReport({ scan, assets, scheme: args.scheme && args.scheme !== true ? args.scheme : 'falcon512_solidity', path: args.path && args.path !== true ? args.path : 'direct', projectedAuths: args.auths ? parseInt(args.auths, 10) : 1, gwei: args.gwei ? parseFloat(args.gwei) : undefined, }); if (args.json) { console.log(JSON.stringify(report, null, 2)); return; } console.log(formatReadinessReport(report)); } async function main() { const argv = process.argv.slice(2); const cmd = argv[0]; if (!cmd || cmd === '--help' || cmd === '-h') topUsage(0); const args = parseArgs(argv.slice(1)); if (args.help) { console.log(`See: aere-pqc ${cmd} ... (schemes: sim=${schemeList().join(',')} ; readiness=${readinessSchemeList().join(',')})`); return; } try { if (cmd === 'scan') await cmdScan(args); else if (cmd === 'simulate') cmdSimulate(args); else if (cmd === 'build-migration') await cmdBuildMigration(args); else if (cmd === 'readiness') await cmdReadiness(args); else { console.error(`unknown command: ${cmd}`); topUsage(1); } } catch (e) { console.error(`error: ${e.message}`); process.exit(1); } } main();