aere-docs/AERE-NEW-CONTRACTS-DEPLOY-RUNBOOK.md
Aere Network e4cead319d Initial public release
Aere Network public source. Everything here can be checked against the live
chain (chain id 2800, https://rpc.aere.network).

Scope note, stated up front rather than buried: consensus on chain 2800 is
classical secp256k1 ECDSA QBFT. The post-quantum work in this repository is at
the signature, precompile, account and transport layers. Nothing here makes the
consensus post-quantum, and no document in it should be read as claiming so.
2026-07-20 01:01:36 +03:00

522 lines
37 KiB
Markdown

# Aere Network: new-contracts deployment-readiness runbook
Scope: the ~17 new Solidity contracts added under `aerenew/contracts/contracts/` during this build loop.
Source review: `aerenew/docs/AERE-SECURITY-REVIEW-NEW-CONTRACTS.md` (no CRITICAL, no HIGH).
Applied fixes: `aerenew/docs/AERE-SECURITY-FIXES.md` (M1 fixed, L1 x3 fixed, L2 fixed, L3 documented).
Solc 0.8.23, OpenZeppelin v4.9.x.
STATUS: NOTHING HERE IS DEPLOYED. Every contract below is founder-gated. This document is the exact,
ordered procedure to deploy and verify each one WHEN the founder greenlights it. It does not deploy
anything. Confirmed on 2026-07-19 that none of these 17 contracts has a `deployments/*.json` entry and
none appears in `aerenew/sdk-js/src/addresses.ts`. Activation is the founder's decision, and where noted
it also requires an external audit and, for three of them, a zk circuit that does not yet exist.
---
## 0. Safety rules (read first, applies to every step)
1. DEPLOY FROM THE DESIGNATED DEPLOYER ONLY. The frontier / inert deployer key is
`0xbeB33D20dFBBD49eC7AC1F617667f1f02dfd6465`. Ownable contracts start owned by this key and are then
transferred to the Foundation `0x0243A4f47D44b40b65D33f20329dE20D00c6f3C3` (or the Timelock) per the
per-contract founder actions below. Do not deploy from any other key.
2. SECRET-SCAN THE REPO BEFORE ANY PUBLISH. Run the clean-repo secret scan before pushing source,
verification metadata, or any artifact to a public location. No keys, no infra hostnames.
3. NEVER TOUCH THE LIVE VALIDATORS OR THE LIVE INFRA BOX. Do not run heavy compute, provers, or deploy
jobs on the infra box that serves the live RPC / explorer / relayer / oracle. Deploy transactions are
submitted to `https://rpc.aere.network`; proving and vkey derivation run on a dedicated non-infra box
that is deleted after use. Confirm RPC stays sub-second after any activity.
4. NO NEW TOKEN. None of these contracts introduces an ERC-20 or a wrapped/governance token, and none may
be modified to do so. Rewards and payments settle in native AERE or an existing allowlisted ERC-20.
5. NEVER MOVE RESERVE OR FOUNDATION FUNDS. These deploys spend only gas from the deployer key. No transfer
of Mining Reserve, Ecosystem Reserve, Team Reserve, Airdrop Reserve, or Foundation holdings is part of
any step here.
6. CODE-READY IS NOT ACTIVATION. Passing tests and a clean self-review make a contract code-ready. Going
live on mainnet 2800 is a separate founder decision, and for the fund-flow contracts and the
circuit-gated verifiers it is also gated on an external audit and, respectively, on a real SP1 circuit.
7. NONE OF THESE CHANGES CONSENSUS. Every contract is application / account / settlement layer. Mainnet
2800 still seals blocks with classical secp256k1 QBFT. The Falcon / ML-DSA precompiles and the SP1
gateway are application-layer primitives. Never describe any of this as making consensus post-quantum.
---
## 1. Live addresses these contracts wire to
All values below are LIVE on mainnet 2800 and were read from `aerenew/sdk-js/src/addresses.ts` and
`aerenew/docs/AERE-EIP-COMPATIBILITY-MATRIX.md`. Pass these EXACT values as constructor arguments where a
contract needs them.
| Ref | Live address | Notes |
|-----|--------------|-------|
| Falcon-512 precompile | `0x0AE1` | Live, activated block 9,189,161. Used by name in-contract (constant), not a constructor arg. |
| ML-DSA-44 precompile | `0x0AE3` | Live, activated block 9,189,161. Used by name in-contract (constant). |
| SP1VerifierGateway | `0x9ca479C8c52C0EbB4599319a36a5a017BCC70628` | Stable Succinct gateway; `verifyProof` reverts on an invalid proof. |
| AerePQCKeyRegistry | `0x1eCa3c5ADcBD0b22636D8672b00faC6D89363691` | PQC public-key registry; `verifyWithKey` / `statusOf` / `schemeOf`. |
| AereAgentDID | `0xce641d7d7C10553D82b06B7C21d423550e7522C5` | Falcon-rooted agent identity; `agentExists` / `getAgent` / `keyRegistry`. |
| AereAIReputation | `0x781ef746c08760aa854cDa4621d54db6734bfeBF` | Composable reputation; attestor set is Foundation-only and immutable. |
| AerePQCAccountFactory | `0xd5315Ea7caa60d320c4f34b1bEd70dd9cc02CE58` | CREATE2 factory; `predictAddress(falconPubKey, salt)`. Runtime ref for the migrator, not a constructor arg. |
| AERE402FacilitatorV2 | `0xFC2f7FAa94919caF1126b3c995F0F6AcCef291de` | Canonical agentic settlement rail; `settle(...)`. (V1 `0xbA6e…4E56` is deprecated, do not wire.) |
| AereAgentV2 | `0x3FAcb997eb4252341052e7c84a86fCa0513c4490` | Machine-account registry the facilitator debits. |
| Foundation | `0x0243A4f47D44b40b65D33f20329dE20D00c6f3C3` | Recommended owner / arbiter / governance target. Single-key EOA today, not a multisig. |
| Deployer | `0xbeB33D20dFBBD49eC7AC1F617667f1f02dfd6465` | The only key these are deployed from. |
| AereZkQbftLightClient | `0xCaDA54FAb6E7AE311d240Cf0C2Df45e974156488` | Origin-side finality client the destination settler's repayment path composes with (reference, not a constructor arg). |
| AereOutboundVerifierV2 | `0x08b68bd553116Dffb99E648cb764AA93930da96F` | Origin-side inclusion-proof pattern the settler documents (reference only). |
---
## 2. Topologically-ordered deploy list
Deploy in this order. A contract may be deployed only after every contract it takes as a constructor
argument already exists. Contracts inside the same wave have no dependency on each other.
WAVE A (no constructor dependency on any sibling; wire only to live addresses or nothing):
- A1. `pqc/AereAccountMigrator` (no constructor args)
- A2. `oracle/AereRandomnessBeaconV2` (no constructor args; the BLS library is internal / inlined, no linking)
- A3. `AereRecoveryRegistry` (no constructor args)
- A4. `intents/AereDestinationSettler` (no constructor args)
- A5. `pqfinality/AerePQAttestationKeyRegistry` (no constructor args; Ownable, owner = deployer)
- A6. `compliance/AereBitstringStatusList(nonRevocationVerifier)` (pass `address(0)`, see B/notes)
- A7. `erc8004/AereIdentityRegistry8004(did)` (wires to live AereAgentDID)
- A8. `erc8004/AereValidationRegistry8004(keyRegistry, did)` (wires to live registry + DID)
- A9. `erc8004/AereAP2MandateVerifier(keyRegistry, settlementExecutor)` (wires to live registry)
- A10. `compliance/AereTrustRegistry(keyRegistry)` (wires to live registry; Ownable)
- A11. `agentic/AereVectorStore(facilitator)` (wires to live AERE402FacilitatorV2)
- A12. `depin/AereComputeMarketV3(zkVerifier, arbiter, governance)` (wires to live SP1 gateway + Foundation)
WAVE B (depends on a Wave A output):
- B1. `erc8004/AereReputationRegistry8004(reputation, identity)` (identity = A7)
- B2. `compliance/AereVerifiableCredential(trustRegistry, statusList)` (trustRegistry = A10, statusList = A6)
WAVE C (CIRCUIT-GATED, do NOT deploy until the SP1 circuit and its real vkey exist):
- C1. `mpc/AerePQAggregateVerifier(gateway, aggregateProgramVKey)` (gateway = live SP1; vkey is [VERIFY])
- C2. `pqfinality/AereFinalityCertificateVerifier(gateway, programVKey, registry)` (registry = A5; vkey is [VERIFY])
WAVE D (depends on a Wave C output, so also circuit-gated):
- D1. `modular/AerePQAggregateModule(verifier)` (verifier = C1)
Note: A6 through A12 have no dependency on A1 through A5; they are grouped in Wave A only because their
sole dependencies are LIVE addresses. B1/B2 must wait for A7 / A10 / A6. C1/C2/D1 must wait for their real
vkeys and, for C2 and D1, for A5 / C1.
---
## 3. Gate classification
| Contract | Gate | Rationale |
|----------|------|-----------|
| AereAccountMigrator | DEPLOY-WHEN-READY (external audit recommended: moves user funds) | Pure conduit, no custody, no admin. Review verdict CLEAN. |
| AereRandomnessBeaconV2 | DEPLOY-WHEN-READY | Real EIP-2537 BLS verify, fail-closed, no admin. CLEAN. |
| AereRecoveryRegistry | DEPLOY-WHEN-READY | Append-only, no admin, Falcon fail-closed. CLEAN (I4 cosmetic). |
| AereDestinationSettler | DEPLOY-WHEN-READY; repayment loop is [MEASURE] | Contract deploys with zero args; end-to-end solver repayment needs an off-chain SP1 prover + an origin-chain verifier that do not exist yet. External audit recommended (fund-flow). |
| AereIdentityRegistry8004 | DEPLOY-WHEN-READY | Controller-gated metadata over the live DID. CLEAN. |
| AereValidationRegistry8004 | DEPLOY-WHEN-READY | PQC-verified validation, no admin. CLEAN (I5 spam note). |
| AereAP2MandateVerifier | FOUNDER-CONFIG at deploy: `settlementExecutor` MUST be non-zero | L3: the open path (executor == 0) is replayable and is TEST-ONLY. Production must pin the AERE402 executor. |
| AereTrustRegistry | FOUNDER-ACTION: transfer owner to Foundation, then accredit issuers | Ownable trusted-list operator by design. CLEAN. |
| AereVerifiableCredential | DEPLOY-WHEN-READY (after A10 + A6) | Binds accreditation + PQC + validity + revocation, fail-closed. CLEAN. |
| AereBitstringStatusList | DEPLOY-WHEN-READY; zk non-revocation path is CIRCUIT-GATED | Deploy with verifier `address(0)`; the SP1 non-revocation circuit is [MEASURE], so `nonRevocationVKey` stays 0 (path fail-closed OFF) until it exists. CLEAN (I2 by-design). |
| AereComputeMarketV3 | EXTERNAL-AUDIT-GATED (highest-value fund flow) + FOUNDER-CONFIG (arbiter, governance) | Escrow + disputes + PQC settlement. Review CLEAN, L1 fixed, but this is the one contract that custodies escrowed rewards and bonds. |
| AereVectorStore | DEPLOY-WHEN-READY (external audit recommended: routes AERE402 funds) | L2 fixed (receipt bound to provider). Settles on the live rail, custodies no balance across calls. |
| AerePQAttestationKeyRegistry | FOUNDER-ACTION: enroll validators, each validator registers own PQ key; owner to Timelock later | Append-only, no key-forgery path. CLEAN. Only meaningful in concert with C2. |
| AereReputationRegistry8004 | FOUNDER-ACTION for WRITE-THROUGH: register adapter as attestor on a reputation instance; owner to Foundation, add feedback authors | M1 fixed (owner-curated authors). READ path works against the live instance today; write-through needs a Foundation governance step. |
| AerePQAggregateVerifier | DEPENDENCY-GATED: do NOT deploy until the SP1 aggregation circuit exists and its real vkey is known | `aggregateProgramVKey` is IMMUTABLE. The ML-DSA-65 aggregation circuit is [MEASURE], not built. L1 fixed (code-less gateway guard). |
| AereFinalityCertificateVerifier | DEPENDENCY-GATED: do NOT deploy until the PQ-finality aggregation circuit exists and its real vkey is known | `programVKey` is IMMUTABLE. The circuit is [MEASURE], not built. Already carries the `VerifierHasNoCode` guard. |
| AerePQAggregateModule | DEPENDENCY-GATED (through C1) | Cannot be meaningfully deployed before C1, which is circuit-gated. CLEAN. |
Summary:
- DEPLOY-WHEN-READY (low risk, code-ready today): AereAccountMigrator, AereRandomnessBeaconV2,
AereRecoveryRegistry, AereDestinationSettler, AereIdentityRegistry8004, AereValidationRegistry8004,
AereVerifiableCredential, AereBitstringStatusList, AereVectorStore.
- FOUNDER-CONFIG / FOUNDER-ACTION before they are useful: AereAP2MandateVerifier (executor),
AereTrustRegistry (owner + accredit), AereReputationRegistry8004 (attestor grant),
AerePQAttestationKeyRegistry (enroll + keys), AereComputeMarketV3 (arbiter + governance).
- EXTERNAL-AUDIT-GATED before mainnet (fund flow): AereComputeMarketV3 (strongest), and recommended for
AereVectorStore, AereDestinationSettler, AereAccountMigrator.
- MUST NOT DEPLOY until a circuit exists: AerePQAggregateVerifier, AereFinalityCertificateVerifier,
AerePQAggregateModule, and the zk non-revocation feature of AereBitstringStatusList.
---
## 4. Per-contract runbook
Each entry: constructor arguments (with the real value to pass), deploy order / dependencies, post-deploy
verification, and the gate. Post-deploy `eth_getCode` should be non-empty for every contract; that check
is implied and not repeated each time.
### A1. pqc/AereAccountMigrator
- Constructor arguments: NONE. `contract AereAccountMigrator is ReentrancyGuard` has no constructor.
- Dependencies / order: none. Wave A.
- Runtime references (not constructor args): `migrateToPqcAccount` and `predictPqcAccount` take a `factory`
argument at call time. Pass the live AerePQCAccountFactory `0xd5315Ea7caa60d320c4f34b1bEd70dd9cc02CE58`.
- Post-deploy verification:
- Confirm NO admin: the ABI has no `owner()`, no `withdraw` / `rescue` / `sweep`. A call to `owner()`
must revert (function does not exist).
- `predictPqcAccount(0xd531…CE58, <897-byte Falcon pubKey>, <salt>)` returns the same address as the live
factory's own `predictAddress(pubKey, salt)` (CREATE2 parity).
- A `migrate` with `moveNative=false` and non-zero `msg.value` reverts `StrayNative`; with an empty token
array and `moveNative=false` reverts `NothingToMigrate`.
- Run `aerenew/contracts/test/account-migrator.test.js` against a fork / live-like config.
- Gate: DEPLOY-WHEN-READY. External audit recommended because it moves user balances (no custody, but it
is the tx that sweeps an EOA). Verdict CLEAN.
### A2. oracle/AereRandomnessBeaconV2
- Constructor arguments: NONE. Constants only (`DRAND_CHAIN_HASH`, `DRAND_PERIOD`, `DRAND_GENESIS`). The
`AereDrandQuicknetBLS` library is an internal library (all functions `internal` / `private`), so it is
inlined at compile time and needs no separate deployment or `--libraries` linking.
- Dependencies / order: none. Wave A. Relies on live EIP-2537 precompiles `0x05`, `0x0b`, `0x0f`, `0x10`.
- Post-deploy verification:
- `DRAND_CHAIN_HASH()` == `0x52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971`,
`DRAND_PERIOD()` == 3, `DRAND_GENESIS()` == 1692803367.
- `verifyRound(round, sig)` with a REAL drand quicknet round returns true; with a tampered signature
returns false (fail-closed). Confirm NO admin (no verification-off switch, no `owner()`).
- Run `aerenew/contracts/test/AereRandomnessBeaconV2.test.js`.
- Gate: DEPLOY-WHEN-READY. CLEAN. Honest note: drand BLS is classical, verifying it does not make the
randomness post-quantum.
### A3. AereRecoveryRegistry
- Constructor arguments: NONE. Falcon-512 precompile `0x0AE1` is a hard-coded constant
(`PRECOMPILE_FALCON512`).
- Dependencies / order: none. Wave A. Requires the live Falcon-512 precompile (block 9,189,161+).
- Post-deploy verification:
- `PRECOMPILE_FALCON512()` == `0x0AE1`; `RECORD_DOMAIN()` == `keccak256("AereRecoveryRegistry.v1.record")`.
- Register a watchtower Falcon key via `registerOperator(pubKey)`, read back `getOperator` / `nonceOf`.
- `submitRecovery` with a valid Falcon signature over `recordChallenge(...)` appends a record and bumps
the operator nonce; a tampered signature reverts `PQCVerificationFailed` and appends nothing.
- Confirm NO owner / admin (append-only, no `owner()`).
- Run `aerenew/contracts/test/AereRecoveryRegistry.test.js`.
- Gate: DEPLOY-WHEN-READY. CLEAN (I4: `getRecord` on a bad id reverts `UnknownOperator`, cosmetic).
### A4. intents/AereDestinationSettler
- Constructor arguments: NONE. `AERE_CHAIN_ID` (2800) and `FILLED_EVENT_SIG` are constants.
- Dependencies / order: none for the deploy. Wave A.
- Post-deploy verification:
- `AERE_CHAIN_ID()` == 2800; `FILLED_EVENT_SIG()` ==
`keccak256("Filled(bytes32,address,address,uint256,address,bytes32,uint64,uint64)")`.
- A `fill` where the delivered token / recipient do not match the declared output reverts `OutputMismatch`;
a second `fill` of the same order reverts `AlreadyFilled`; a good fill emits `Filled`, records the
`FillRecord`, and `fillCommitment(orderId)` returns a non-zero binder.
- Confirm NO custody / admin (solver-to-recipient direct transfer, no `owner()`).
- Run `aerenew/contracts/test/destination-settler.test.js`.
- Gate: DEPLOY-WHEN-READY for the on-chain filling side. The origin-chain repayment loop (SP1 prover over
the `qbft-lightclient-guest` ELF plus an AereOutboundVerifierV2-style verifier deployed on the origin
chain) is [MEASURE] and NOT part of this deploy; the settler is complete without it, but a solver is not
actually repaid until that off-chain / second-chain machinery exists. External audit recommended
(fund-flow, even though this contract takes no custody). CLEAN (I1: fee-on-transfer output token note).
### A5. pqfinality/AerePQAttestationKeyRegistry
- Constructor arguments: NONE. `is Ownable`; OpenZeppelin `Ownable` sets `owner = msg.sender` (the deployer).
- Dependencies / order: none for the deploy. Wave A. It is a constructor dependency of C2
(AereFinalityCertificateVerifier), so it must exist before C2.
- Post-deploy verification:
- `owner()` == deployer; `validatorCount()` == 0; `validatorSetRoot()` is a deterministic value over an
empty set.
- After the founder action below: `validators()` returns the enrolled set in enrollment order,
`isSetComplete()` becomes true once every enrolled validator has a key, and `validatorSetRoot()` changes
on each enroll / key rotation.
- Confirm a non-validator cannot register a key for another address (`registerAttestationKey` uses
`msg.sender`), and a non-owner `enrollValidator` reverts `Ownable: caller is not the owner`.
- Run `aerenew/contracts/test/AerePQFinalityCertificate.test.js` (covers registry + verifier).
- Gate: FOUNDER-ACTION. The owner (Foundation, later the Timelock, founder-signed) enrolls the validator
addresses; then EACH validator registers its OWN hash-based attestation key. Transfer ownership to the
Foundation / Timelock after deploy. CLEAN. Note: this registry is only useful once C2 is live, which is
circuit-gated.
### A6. compliance/AereBitstringStatusList
- Constructor arguments:
1. `nonRevocationVerifier_` (address): PASS `address(0)`. This keeps the zk non-revocation path
fail-closed OFF (`nonRevocationConfigured()` stays false) until the SP1 non-revocation circuit exists.
The constructor guards a NON-ZERO but code-less address with `VerifierHasNoCode` (L1 fix); the zero
address is explicitly allowed. Only pass the live SP1 gateway `0x9ca4…0628` here if you intend to wire
the zk path, and even then the vkey stays 0 until the circuit is built.
- `ADMIN` is set to `msg.sender` (the deployer) and is IMMUTABLE; it cannot be transferred. It can only
publish the non-revocation vkey, nothing else.
- Dependencies / order: none for the deploy. Wave A. It is a constructor dependency of B2
(AereVerifiableCredential), so it must exist before B2.
- Post-deploy verification:
- `ADMIN()` == deployer; `NON_REVOCATION_VERIFIER()` == `address(0)` (if you passed zero);
`nonRevocationConfigured()` == false; `nonRevocationVKey()` == `bytes32(0)`.
- `createList(purpose, capacity)` returns a listId; `setStatus` / `revoke` are controller-only;
revocation bits are terminal (clearing reverts `RevocationIsTerminal`), suspension bits are reversible.
- `verifyNonRevocation(...)` reverts `NonRevocationNotConfigured` while the circuit is unset (so callers
can never mistake "unconfigured" for "not revoked").
- Run `aerenew/contracts/test/aere-pq-screen.test.js` (covers trust registry + VC + status list).
- Gate: DEPLOY-WHEN-READY for the bitstring mechanism. The zk non-revocation feature is CIRCUIT-GATED:
`ADMIN` calls `setNonRevocationVKey(<real vkey>)` only once the SP1 non-revocation circuit is built.
CLEAN (I2: status roots are controller-attested by design; `getWord` allows independent recomputation).
### A7. erc8004/AereIdentityRegistry8004
- Constructor arguments:
1. `did` (address): the live AereAgentDID `0xce641d7d7C10553D82b06B7C21d423550e7522C5`. The constructor
reads `keyRegistry()` from the DID and stores it as `KEY_REGISTRY`, so you do NOT pass the key registry
separately. It reverts `ZeroAddress` on a zero DID or a DID that returns a zero key registry.
- Dependencies / order: needs the live AereAgentDID. Wave A. It is a constructor dependency of B1
(AereReputationRegistry8004), so it must exist before B1.
- Post-deploy verification:
- `DID()` == `0xce64…22C5`; `KEY_REGISTRY()` == `0x1eCa…3691` (confirms the DID rooted into the expected
live key registry).
- `register(agentId, domain, cardURI)` succeeds only for `msg.sender == KEY_REGISTRY.ownerOf(agentId)`
(the current Falcon-root controller) and reverts `NotController` otherwise; `resolveByDomain` /
`getAgent` return the bound metadata; `didString(agentId)` == `did:aere:2800:<agentId>`.
- Confirm NO owner / admin.
- Run `aerenew/contracts/test/erc8004-adapters.test.js`.
- Gate: DEPLOY-WHEN-READY. CLEAN. [VERIFY] against the finalized ERC-8004 identity interface: Aere anchors
the id to the Falcon-rooted DID id rather than minting a sequential ERC-721 id (documented divergence).
### A8. erc8004/AereValidationRegistry8004
- Constructor arguments:
1. `keyRegistry_` (address): live AerePQCKeyRegistry `0x1eCa3c5ADcBD0b22636D8672b00faC6D89363691`.
2. `did_` (address): live AereAgentDID `0xce641d7d7C10553D82b06B7C21d423550e7522C5`.
Reverts `ZeroAddress` on either being zero.
- Dependencies / order: needs live registry + DID. Wave A.
- Post-deploy verification:
- `KEY_REGISTRY()` == `0x1eCa…3691`; `DID()` == `0xce64…22C5`.
- `precompileFor(1)` == `0x0AE1` (Falcon-512), `precompileFor(3)` == `0x0AE3` (ML-DSA-44).
- `requestValidation(validatorKeyId, serverAgentId, dataHash)` requires an ACTIVE Falcon/ML-DSA validator
key and an existing server agent; `respondValidation` accepts only a valid PQC signature over
`responseChallenge(...)` and reverts `PQCValidationFailed` on a tampered proof; a request completes
exactly once.
- Confirm NO owner / admin.
- Run `aerenew/contracts/test/erc8004-adapters.test.js`.
- Gate: DEPLOY-WHEN-READY. CLEAN (I5: permissionless request creation is storage-spam only, harmless).
### A9. erc8004/AereAP2MandateVerifier
- Constructor arguments:
1. `keyRegistry_` (address): live AerePQCKeyRegistry `0x1eCa3c5ADcBD0b22636D8672b00faC6D89363691`.
2. `settlementExecutor_` (address): [VERIFY: supply the AERE402 settlement-executor that will call
`authorizeSpend` atomically with settlement. MUST be non-zero in production.] Passing `address(0)`
enables the OPEN accounting path, which the security review (L3) and the fixes doc flag as TEST-ONLY:
a public standing Falcon signature is replayable by anyone to exhaust a mandate's cap. There is no live
facilitator today that calls `authorizeSpend`, so this address is a founder integration decision (the
candidate is AERE402FacilitatorV2 `0xFC2f…91de` or a purpose-built executor once wired). `SETTLEMENT_EXECUTOR`
is IMMUTABLE, so this is a deploy-time choice, not a later setter.
- Dependencies / order: needs live registry. Wave A. The executor address should exist first if a real
contract is used.
- Post-deploy verification:
- `KEY_REGISTRY()` == `0x1eCa…3691`; `SETTLEMENT_EXECUTOR()` == the non-zero executor you passed.
- `verifyMandate(m, sig)` (view) returns true for a valid Falcon mandate signature, false otherwise
(never reverts).
- `authorizeSpend(m, amount, sig)` from an address other than `SETTLEMENT_EXECUTOR` reverts
`NotSettlementExecutor`; from the executor with a valid signature within the window it records the spend
and enforces the cap; a cap overflow reverts `MandateCapExceeded`.
- Confirm NO owner / admin and NO fund custody (cap is accounting only).
- Run `aerenew/contracts/test/erc8004-adapters.test.js` (includes the settlement-executor-gate test).
- Gate: FOUNDER-CONFIG at deploy. Production MUST pass a non-zero `settlementExecutor_`. CLEAN except the
documented L3 open-path risk, which the non-zero executor closes.
### A10. compliance/AereTrustRegistry
- Constructor arguments:
1. `keyRegistry_` (address): live AerePQCKeyRegistry `0x1eCa3c5ADcBD0b22636D8672b00faC6D89363691`.
`is Ownable`; owner = deployer at construction. The constructor also reserves issuer index 0 as an inert
placeholder.
- Dependencies / order: needs live registry. Wave A. It is a constructor dependency of B2
(AereVerifiableCredential), so it must exist before B2.
- Post-deploy verification:
- `KEY_REGISTRY()` == `0x1eCa…3691`; `owner()` == deployer; `issuerCount()` == 0; `NO_KEY()` ==
`type(uint256).max`.
- After ownership transfer + accreditation: `accreditIssuer` is owner-only; `isAccredited(id, type)` is
true only for an ACTIVE issuer with the type in scope; `revokeIssuer` is terminal (a revoked issuer can
never be reinstated); `verifyIssuerSignature` is fail-closed.
- A non-owner `accreditIssuer` / `suspendIssuer` / `revokeIssuer` reverts `Ownable: caller is not the owner`.
- Run `aerenew/contracts/test/aere-pq-screen.test.js`.
- Gate: FOUNDER-ACTION. After deploy, transfer ownership to the Foundation (the trusted-list operator),
then accredit real issuers. CLEAN. Deploying does not assert any real institution is accredited.
### A11. agentic/AereVectorStore
- Constructor arguments:
1. `facilitator` (address): the live AERE402 settlement rail. PASS AERE402FacilitatorV2
`0xFC2f7FAa94919caF1126b3c995F0F6AcCef291de` (canonical; V1 `0xbA6e…4E56` is deprecated). [VERIFY:
confirm the deployed facilitator's `settle(uint256,address,address,uint256,bytes32,uint256,uint256,bytes)`
ABI matches the `IAERE402Facilitator` interface before relying on paid queries. The store only calls
`settle`; the `consumed` view in the interface is not called.] Reverts `ZeroAddress` on zero.
- Dependencies / order: needs the live facilitator. Wave A.
- Post-deploy verification:
- `FACILITATOR()` == `0xFC2f…91de`; `COMMIT_DOMAIN()` and `QUERY_DOMAIN()` match the constants.
- `createStore(...)` fixes owner + pricing at creation (no setters); a post-quantum store's `commit`
requires a valid Falcon signature over `commitChallenge(...)` (fail-closed), a classical store's requires
`msg.sender == owner`.
- L2 fix check: `paidQuery(..., provider, ...)` records the receipt bound to `provider`; `attestRetrieval`
by any address other than that provider reverts `NotReceiptProvider` and leaves the receipt unconsumed;
the bound provider consumes it successfully.
- Confirm NO owner / admin and no balance held across calls (delta-measured forward to recipient).
- Run `aerenew/contracts/test/aere-vector-store.test.js`.
- Gate: DEPLOY-WHEN-READY. External audit recommended (routes AERE402 payments). CLEAN with L2 fixed.
### A12. depin/AereComputeMarketV3
- Constructor arguments:
1. `zkVerifier` (address): live SP1VerifierGateway `0x9ca479C8c52C0EbB4599319a36a5a017BCC70628`. The
constructor reverts `VerifierHasNoCode` if this address has no code (L1 fix), so it MUST be the live
gateway.
2. `arbiter` (address): [FOUNDER DECISION. Recommended: Foundation `0x0243A4f47D44b40b65D33f20329dE20D00c6f3C3`.]
Can only route a DISPUTED job's escrow; cannot drain. IMMUTABLE.
3. `governance` (address): [FOUNDER DECISION. Recommended: Foundation `0x0243…f3C3`.] Can only flip the
ERC-20 reward-token allowlist; cannot move funds. IMMUTABLE.
Reverts `ZeroAddress` on any of the three being zero.
- Dependencies / order: needs the live SP1 gateway (code check) and the two chosen roles. Wave A.
- Post-deploy verification:
- `ZK_VERIFIER()` == `0x9ca4…0628`; `ARBITER()` == chosen arbiter; `GOVERNANCE()` == chosen governance;
`FALCON512_PRECOMPILE()` == `0x0AE1`.
- `setTokenAllowed` reverts `NotGovernance` for a non-governance caller; `resolveDispute` reverts
`NotArbiter` for a non-arbiter caller.
- `isSolvent(address(0))` and `isSolvent(<token>)` are true after any sequence of job lifecycle calls
(the per-asset solvency invariant: balance >= totalLiabilities). A ZK_VERIFIED job pays only inside
`submitResultZK` after `verifyProof`; a `pqcSettlement` job's payout requires a valid Falcon signature
over `settlementDigest(...)` or reverts `NotPQCAuthorized`.
- Confirm NO owner and no sweep (only arbiter routing + governance allowlist).
- Run `aerenew/contracts/test/AereComputeMarketV3.test.js` (includes the L1 construction-guard test).
- Gate: EXTERNAL-AUDIT-GATED before mainnet (it is the highest-value fund-flow contract: escrowed rewards +
provider / challenger bonds). FOUNDER-CONFIG for arbiter and governance. CLEAN, L1 fixed.
### B1. erc8004/AereReputationRegistry8004
- Constructor arguments:
1. `reputation` (address): an AereAIReputation instance. For the READ path, pass the LIVE
`0x781ef746c08760aa854cDa4621d54db6734bfeBF`. For WRITE-THROUGH, pass the reputation instance that
LISTS THIS ADAPTER as an attestor. [VERIFY: the live instance's attestor set is Foundation-only and
immutable, so write-through requires a NEW AereAIReputation instance that includes this adapter; supply
that instance's address if write-through is intended.]
2. `identity` (address): the AereIdentityRegistry8004 deployed in A7.
`is Ownable`; owner = deployer. Reverts `ZeroAddress` on either being zero.
- Dependencies / order: needs A7 (identity adapter) and a reputation instance. Wave B.
- Post-deploy verification:
- `REPUTATION()` == the instance passed; `IDENTITY()` == the A7 address; `owner()` == deployer.
- `getScore(agentId)` / `getStats(agentId)` read straight from the live reputation (READ works today).
- M1 fix check: `giveFeedback` from an address NOT in `isAuthorizedFeedbackAuthor` reverts
`NotAuthorizedFeedbackAuthor` and writes nothing; after `addFeedbackAuthor(x)` (owner-only), `x` can
submit feedback in `{-1,0,+1}`; `removeFeedbackAuthor` re-blocks; only the owner manages the allowlist.
- `canWriteThrough()` is true only when the reputation instance lists this adapter as an attestor
(`AdapterNotAttestor` otherwise).
- Run `aerenew/contracts/test/erc8004-adapters.test.js`.
- Gate: FOUNDER-ACTION for write-through. READ path is deploy-when-ready against the live instance. For
write-through the Foundation must deploy / point a reputation instance that lists this adapter, then the
owner authorizes feedback authors. Transfer ownership to the Foundation. M1 fixed.
### B2. compliance/AereVerifiableCredential
- Constructor arguments:
1. `trustRegistry_` (address): the AereTrustRegistry deployed in A10.
2. `statusList_` (address): the AereBitstringStatusList deployed in A6.
Reverts `ZeroAddress` on either being zero.
- Dependencies / order: needs A10 and A6. Wave B.
- Post-deploy verification:
- `TRUST_REGISTRY()` == A10 address; `STATUS_LIST()` == A6 address; `DID_METHOD()` == `"did:aere"`;
`VC_DOMAIN()` matches the constant.
- `verifyPresented(c, sig)` returns true only when accreditation + Falcon signature + validity +
not-revoked all hold (fail-closed, never reverts). `anchorCredential` reverts `NotAccredited` /
`NotYetValid` / `Expired` / `SignatureInvalid` on the respective failure and records nothing;
`anchorCredentialWithDid` enforces the `did:aere:` prefix and the `subjectDidHash` match.
- `isValid(id)` flips to false the instant the issuer is suspended / revoked in A10 or the credential's
status bit is set in A6.
- Confirm NO owner / admin.
- Run `aerenew/contracts/test/aere-pq-screen.test.js`.
- Gate: DEPLOY-WHEN-READY (after A10 + A6). CLEAN.
### C1. mpc/AerePQAggregateVerifier
- Constructor arguments:
1. `gateway` (address): live SP1VerifierGateway `0x9ca479C8c52C0EbB4599319a36a5a017BCC70628`. The
constructor reverts `VerifierHasNoCode` if code-less (L1 fix), so it MUST be the live gateway.
2. `aggregateProgramVKey` (bytes32): [VERIFY: supply the REAL SP1 verification key of the audited ML-DSA-65
aggregation circuit at deploy time.] This value is IMMUTABLE and pins the one circuit this verifier will
ever accept. The circuit is [MEASURE] in `docs/AERE-PQ-AGGREGATE.md` and is NOT implemented, so a real
vkey does not exist yet. Deploying now would permanently pin a placeholder. Reverts `ZeroVKey` on zero.
- Dependencies / order: Wave C. It is a constructor dependency of D1 (AerePQAggregateModule).
- Post-deploy verification (only meaningful once a real circuit + vkey exist):
- `SP1_GATEWAY()` == `0x9ca4…0628`; `AGGREGATE_PROGRAM_VKEY()` == the pinned real vkey.
- `registerCommittee(root, t, n, scheme)` enforces `1 <= t <= n` and a non-zero scheme; `verifyAggregate`
is fail-closed on every binding mismatch (root / threshold / size / scheme / digest / below-threshold)
and delegates the one cryptographic check to `verifyProof`.
- Confirm NO owner / admin and no fund custody.
- Run `aerenew/contracts/test/AerePQAggregate.test.js` (uses a MOCK gateway; the real circuit is not
exercised, so passing tests do NOT prove end-to-end aggregation).
- Gate: DEPENDENCY-GATED. DO NOT DEPLOY until the SP1 aggregation circuit exists and its real vkey is
derived on a dedicated non-infra box. L1 fixed.
### C2. pqfinality/AereFinalityCertificateVerifier
- Constructor arguments:
1. `gateway` (address): live SP1VerifierGateway `0x9ca479C8c52C0EbB4599319a36a5a017BCC70628`. Reverts
`VerifierHasNoCode` if code-less (guard already present).
2. `programVKey` (bytes32): [VERIFY: supply the REAL SP1 vkey of the audited PQ-finality aggregation
circuit at deploy time.] IMMUTABLE; pins the one circuit. The circuit is [MEASURE] in
`docs/AERE-PQ-FINALITY-CERTIFICATE.md` and is NOT implemented. Reverts `ZeroVKey` on zero.
3. `registry` (address): the AerePQAttestationKeyRegistry deployed in A5. Reverts `ZeroRegistry` on zero.
The constructor also derives `attestationDomain = keccak256(DOMAIN_PREFIX, chainid, registry)`.
- Dependencies / order: needs A5 and a real vkey. Wave C.
- Post-deploy verification (only meaningful once a real circuit + vkey exist):
- `SP1_GATEWAY()` == `0x9ca4…0628`; `PROGRAM_VKEY()` == the pinned real vkey; `REGISTRY()` == A5 address;
`attestationDomain()` == `keccak256(abi.encode(DOMAIN_PREFIX, 2800, <A5 address>))`.
- `currentQuorumThreshold()` == ceil(2N/3) for the live registry set (N=7 gives 5). `verifyCertificate`
is fail-closed on root / size / domain / quorum mismatch; a rotation in A5 invalidates old certificates.
- Confirm NO owner / admin.
- Run `aerenew/contracts/test/AerePQFinalityCertificate.test.js` (MOCK gateway; real circuit not exercised).
- Gate: DEPENDENCY-GATED. DO NOT DEPLOY until the PQ-finality aggregation circuit exists and its real vkey
is known, AND A5 is deployed with the validator set enrolled and keyed. Additive to consensus, never a
consensus flip. CLEAN, already carries the `VerifierHasNoCode` guard.
### D1. modular/AerePQAggregateModule
- Constructor arguments:
1. `verifier` (address): the AerePQAggregateVerifier deployed in C1. Reverts `BadVerifier` on zero.
- Dependencies / order: needs C1. Wave D. Transitively circuit-gated.
- Post-deploy verification:
- `VERIFIER()` == C1 address; `isModuleType(1)` == true; `name()` == `"aere.pq-aggregate-validator.1.0.0"`.
- `onInstall(abi.encode(committeeId))` reverts `UnknownCommittee` unless the committee is registered in C1;
`validateUserOp` returns 0 only when the signature carries a valid aggregate proof for the account's
committee over `userOpHash`, else 1 (never reverts on a malformed signature).
- Confirm NO owner / admin (singleton, per-account state keyed by `msg.sender`).
- Run `aerenew/contracts/test/AerePQAggregate.test.js`.
- Gate: DEPENDENCY-GATED (through C1). [VERIFY] the ERC-7579 signature-unwrapping against the specific
account stack (Safe7579 / Kernel / Nexus) you install it under. CLEAN.
---
## 5. Consolidated [VERIFY] list (must be supplied at deploy time)
Addresses:
- AereAP2MandateVerifier constructor arg 2, `settlementExecutor_`: the non-zero AERE402 settlement-executor
that calls `authorizeSpend`. MUST NOT be `address(0)` in production (L3). Founder integration decision.
- AereReputationRegistry8004 constructor arg 1, `reputation`: `0x781e…feBF` for READ; for WRITE-THROUGH, a
reputation instance that lists this adapter as an attestor (the live instance's attestor set is immutable
and Foundation-only, so a new instance is required).
- AereVectorStore constructor arg 1, `facilitator`: recommended AERE402FacilitatorV2 `0xFC2f…91de`; confirm
the deployed `settle` ABI matches the interface.
- AereComputeMarketV3 constructor args 2 and 3, `arbiter` and `governance`: founder-chosen (Foundation
`0x0243…f3C3` recommended).
vkeys (IMMUTABLE, pin a circuit that does NOT yet exist, so these contracts must NOT deploy until the
circuit is built and its real vkey is derived on a dedicated non-infra box):
- AerePQAggregateVerifier constructor arg 2, `aggregateProgramVKey`: real SP1 vkey of the ML-DSA-65
aggregation circuit ([MEASURE]).
- AereFinalityCertificateVerifier constructor arg 2, `programVKey`: real SP1 vkey of the PQ-finality
aggregation circuit ([MEASURE]).
- AereBitstringStatusList post-deploy `setNonRevocationVKey(vkey)`: real SP1 vkey of the zk non-revocation
circuit ([MEASURE]); set by `ADMIN` (the deployer) only once the circuit exists. The contract itself
deploys today with `nonRevocationVerifier_ = address(0)`.
Interface confirmations ([VERIFY] against a finalized external spec, not a deploy blocker):
- ERC-8004 identity / reputation / validation selector conformance (the adapters keep ERC-8004 semantics
but anchor ids to the Falcon-rooted DID id by design).
- x402 / AP2 mandate field set (AereAP2MandateVerifier struct).
- W3C VC 2.0 JSON member mapping (AereVerifiableCredential anchors a digest, not the JSON-LD document).
- ERC-7579 signature unwrapping for the target account stack (AerePQAggregateModule).
---
## 6. What is NOT in this runbook
- No consensus change. None of these deploys touches validator keys, QBFT config, or genesis.
- No fund movement beyond deployer gas. No Reserve / Foundation transfer is part of any step.
- No off-chain build. The SP1 aggregation circuits (finality, committee, non-revocation) and the
destination-settler origin-side repayment prover are [MEASURE] research / engineering items tracked
separately; this runbook covers only the on-chain contract deploys and their verification.