aere-research/research/specs/spec-account-abstraction.md
Aere Network 6cb0140fae Republished from a clean root: the compiled artifact is gone from history, and the local line of work joins the sanitized public line
The public history carried kat/__pycache__/mlkem768_reference.cpython-314.pyc,
a compiled Python artifact embedding the operator's absolute local path. Text
secret scanners do not read compiled binaries, which is exactly how it slipped
through, and removing it from the tip would have left it reachable through the
old root commits. So this repository is republished from a single clean root.

This root also carries, from the previously unpublished line of work:
- corrected LICENSE year, LICENSING.md, VERIFY-POLICY.md, and
  CITATIONS-UNRESOLVED.md remeasured 2026-08-11 (101 paths, README aligned)
- O-018: run_consensus_verification.py ran 19 of 29 models and reported PASS;
  it now runs all 29, and computemarket_smt.py gains resolveByTimeout /
  reclaimUnsettled cases plus a negative control
- O-006: the word 'audited' removed from next to Bouncy Castle, twice, after a
  concurrent edit resurrected it
- O-014: prior art named and dated - Algorand's native falcon_verify shipped
  about ten months before AERE's precompiles; the primacy claim is withdrawn
  where it was implied
- bench/ scripts parametrized so they actually run for an outsider (the
  earlier textual sanitization left $STAGING unexpanded inside Python strings)
- AIP-2/AIP-3 errata with measured figures, spec remeasurements at 2026-08-01,
  and the spec-zk-stack retractions (owner is an operational key, not the
  Foundation; 'maximally sound' withdrawn; aggregator V1 deprecated)
The redacted bench-host environment files from the sanitized line are kept
exactly as published; the unredacted local variants are not carried.
2026-08-15 13:52:14 +03:00

301 lines
40 KiB
Markdown

# AERE Account Abstraction and Onboarding Stack
**Chain:** AERE Network, chain ID 2800 (`0xAF0`)
**Consensus:** Hyperledger Besu QBFT, 0.5-second target blocks, sub-second finality, seven validators
**EVM ruleset:** Pectra (Prague/Cancun) plus Fusaka (Osaka), functional parity with Ethereum mainnet
**Source of truth for addresses:** `aerenew/sdk-js/src/addresses.ts`
**Contract source:** `aerenew/contracts/contracts/`
This document specifies the wallet and onboarding layer of AERE: passkey smart accounts, the ERC-4337 EntryPoint, the four gasless paymasters, EIP-7702 EOA delegation, the ERC-7579 modular account line, ERC-6551 token-bound accounts, and the post-quantum smart account. It states plainly what is live on mainnet 2800 today, what is source-complete but not yet deployed, which live contracts are deprecated and what replaced them, and where the current design has seams. Honesty about the seams is deliberate: this is an engineering reference, not marketing copy.
**Revised 2026-08-01.** Every address, code size and balance below was re-read from `https://rpc.aere.network` at head block 11,810,041. That pass found four claims in this document that the chain contradicted, in both directions: contracts described as unbuilt that are live (Section 6, Section 8), and contracts described as canonical that our own registry marks flawed (Section 2.1, Section 5). Each correction is stated in place, next to the sentence it corrects, rather than applied silently.
---
## 1. Substrate: what the chain gives the wallet layer
Two hardfork features do the heavy lifting for account abstraction on AERE.
**RIP-7951 secp256r1 (P-256) precompile at `0x100`.** Fusaka activated at unix `1780220351`, block `2,106,597`. From that block onward the P-256 verification precompile is unconditionally present at address `0x100`. It takes a 160 byte input (`msgHash || r || s || x || y`, five 32 byte words) and returns a 32 byte word whose low bit is set on a valid signature. The fixed cost is **6,900 gas**, measured live on chain 2800 on 2026-07-19 with a calibrated probe (flat across 0/160/161-byte input). Corrected from a previous "roughly 3,450 gas": 3,450 is the price in RIP-7212, the original Layer-2 spec, which RIP-7951 doubled to 6,900 when standardizing for mainnet while keeping the same address, input and output format. Compare roughly 250,000 gas for a pure-Solidity P-256 verification. This is what makes native passkey (WebAuthn) signatures affordable to check on chain, so a user's Face ID, Touch ID, Windows Hello, Android biometric, YubiKey, or EU Digital Identity Wallet key can directly authorize account operations with no seed phrase.
**EIP-7702 EOA delegation (Pectra).** An existing externally owned account can sign a 7702 authorization tuple that makes its account temporarily execute a designated contract's deployed bytecode, while remaining controlled by the original secp256k1 key. AERE uses this to upgrade a plain MetaMask style EOA into a batching, session-key, passkey-capable account without moving funds to a new address.
One substrate constraint shapes the account designs below. AERE's own EntryPoint keeps a strictly sequential, single-dimension per-sender nonce in its own storage (no 2D nonce keys of the kind canonical ERC-4337 v0.7 uses for validator routing). Accounts that want to route between multiple validators therefore cannot encode the route in a nonce key, and instead route on a signature prefix byte. This is called out explicitly in the modular account (Section 6).
---
## 2. Passkey smart accounts (live)
The passkey account is a smart contract wallet whose owner set can be WebAuthn P-256 keys, plain EOA keys, or a mix. It is the production onboarding path for "no seed phrase" users.
### 2.1 Components and addresses
| Component | Address (chain 2800) | Live code | Status |
|---|---|---|---|
| `AereEntryPointV2` | `0x8D6f40598d552fF0Cb358b6012cF4227B86aF770` | 3,127 B | Live |
| `AerePasskeyAccountFactoryV2Fixed` | `0x8FA2B45D63EBaa4551A55d7f33a82d597e423428` | 9,675 B | **Live, CANONICAL. Create new wallets here.** |
| `AerePasskeyAccountFactoryV2` | `0x5FFa9a6487DA4641a1A1e7900ff2bD4525D34fdA` | 9,722 B | **FLAWED. Do NOT create new wallets here.** |
| `AerePasskeyAccountFactory` (V1) | `0xfB0eF980667A79Fe1AB69c5f2d512118F1B30739` | 5,547 B | Legacy, retained for the original demo account |
**Correction of 2026-08-01, and it matters more than most.** An earlier revision of this spec listed `0x5FFa9a64…` as "Live (canonical)" in this table and again in the address appendix, and named no successor. The canonical registry begins that very entry with the word `FLAWED` and carries the line "Do NOT create new wallets here" immediately above it: the accounts that factory deploys carried an `ecrecover`-zero authorization bypass. A reader following the earlier revision would have created a wallet on the factory our own registry tells them to avoid. The corrected factory `AerePasskeyAccountFactoryV2Fixed` `0x8FA2B45D…` has been live with 9,675 bytes of code the whole time and appeared nowhere in this spec. Both facts are stated here rather than fixed silently, because the earlier text is what integrators were reading.
`0x5FFa9a64…` is left on chain and named here on purpose: wallets already minted from it exist, and their holders need to be able to find out what they are holding. Sample account minted by the corrected factory: `AerePasskeyAccountV2_sampleFixed` `0x7C599babc66c350A6cE76673868253e2288D75C1`, 8,430 bytes.
Source: `aere-contracts/contracts/passkey/AerePasskeyAccountV2.sol`, `MultiOwnable.sol`, `WebAuthn.sol`, `AerePasskeyAccountFactoryV2.sol`.
### 2.2 WebAuthn verification (`WebAuthn.sol`)
`WebAuthn.verify` is a precompile-only port adapted from Coinbase's `base-org/webauthn-sol` library. Given the 32 byte challenge, a `requireUV` flag, the parsed `WebAuthnAuth` assertion, and the public key `(x, y)`, it:
1. Rejects malleable signatures (`s > n/2`, guarding against the secp256r1 curve order half).
2. Confirms `clientDataJSON` contains `"type":"webauthn.get"` at the declared `typeIndex`.
3. Reconstructs `"challenge":"<base64url(challenge)>"` and confirms it appears at `challengeIndex`, binding the signed browser payload to the on-chain challenge.
4. Checks the authenticator data flags: User Present always, User Verified when `requireUV` is set (the passkey account passes `requireUV = true`, so a biometric or PIN gesture is required, not a bare presence tap).
5. Computes `sha256(authenticatorData || sha256(clientDataJSON))` and calls the `0x100` precompile.
The FreshCryptoLib fallback path present in the upstream library is stripped, because the precompile is guaranteed on AERE from the Fusaka block.
### 2.3 Ownership model (`MultiOwnable.sol`)
Owners are stored as raw bytes: 32 bytes for an EOA (a `uint160` address left-padded, top 12 bytes must be zero) or 64 bytes for a P-256 public key (`x || y`). Each owner has a monotonic index that is never reused. The authorization gate is a self-call check: `onlyOwner` requires `msg.sender == address(this)`. That means every owner-management action (add EOA owner, add passkey owner, remove owner) must arrive as an authenticated self-call through one of the account's execute paths. Any single registered owner can add or remove other owners, and the set can never be emptied (`removeOwnerAtIndex` reverts `LastOwner` at count 1).
This is the account's recovery model in its live form: keep at least one owner, and optionally pre-register a backup key (for example a hardware EOA) so loss of the primary passkey does not lose the account. A guardian-and-timelock recovery flow is a separate module, `AereSocialRecoveryModule` `0x077514DB…`, which is deployed on mainnet 2800 (Section 6) but is not part of the passkey account itself and is not wired into live passkey wallets.
### 2.4 Authorization paths (`AerePasskeyAccountV2.sol`)
The account exposes three ways to authorize a call, all resolving through the same `_verifyWrappedSignature` routine that selects the owner by index and dispatches to ECDSA recovery (EOA owners, EIP-191 personal_sign envelope, MetaMask compatible) or WebAuthn (passkey owners):
- **ERC-4337 path.** `validateUserOp` is callable only by the account's `entryPoint`. It verifies the wrapped signature over `userOpHash`, returns `0` on success or `1` (`SIG_VALIDATION_FAILED`) on failure per spec (it does not revert on a bad signature), and forwards any `missingAccountFunds` prefund to the EntryPoint. Execution is dispatched by `executeFromEntryPoint` / `executeBatchFromEntryPoint`, both EntryPoint-gated.
- **Direct passkey path.** `executeWithPasskey` / `executeBatchWithPasskey` let the Foundation relayer (or the user self-relaying) drive the account without a bundler. The signer signs a domain-separated `getExecuteChallenge` that binds `address(this)`, `block.chainid`, a monotonic `passkeyNonce`, and the call payload, giving replay protection independent of the EntryPoint nonce.
- **EIP-1271.** `isValidSignature` wraps the queried hash in an `AereAccount1271(address account,uint256 chainid,bytes32 hash)` digest before verifying, preventing cross-account signature replay. This makes the account usable with Permit2, OpenSea, Snapshot, and dapp login flows.
### 2.5 Deployment (`AerePasskeyAccountFactoryV2.sol`)
A CREATE2 factory. `predictAddress(initialOwners[], salt)` returns the counterfactual address; the initial owner set is mixed into the salt, so the same passkey with a different initial owner set produces a different address and multi-owner accounts cannot be front-run at their counterfactual address. `createAccount` is idempotent (returns the existing account if already deployed) and pins every new account to the factory's immutable `defaultEntryPoint`.
---
## 3. ERC-4337 EntryPoint (live) and an honest note on two EntryPoints
AERE ships two EntryPoint contracts, and the distinction matters for anyone integrating.
**`AereEntryPointV2`** at `0x8D6f40598d552fF0Cb358b6012cF4227B86aF770` (source `aere-contracts/contracts/passkey/AereEntryPointV2.sol`) is the account-facing EntryPoint. It implements the canonical `handleOps(PackedUserOperation[], beneficiary)` flow: a validation phase that calls `account.validateUserOp` for every op and reverts the whole batch on any non-zero return, then an execution phase that calls `sender.call(op.callData)`, meters gas, and charges the paymaster's deposit if `paymasterAndData` names one, else the account's own deposit. It computes the canonical v0.7 `userOpHash` (binding the op fields, `address(this)`, and `block.chainid`), keeps per-account deposits, and emits standard `UserOperationEvent` records so bundlers and indexers can subscribe. AERE hosts its own relayer while bootstrapping; the contract is shaped so external bundlers (Pimlico, Stackup, ZeroDev) can target it via per-chain config.
**`AereEntryPoint`** at `0x19773ba45287A64B05d0BCBD59D1371BF51Bd5D2` (source `aere-contracts/contracts/paymaster/AereEntryPoint.sol`) is a lightweight, paymaster-oriented EntryPoint from the Tier-1.3 stack. It implements the deposit and stake surface (`depositTo`, `balanceOf`, `withdrawTo`, `addStake`, `unlockStake`, `withdrawStake`) plus a `relayUserOp` that calls a paymaster's `validatePaymasterUserOp`, executes the sender call, and deducts the paymaster's deposit.
Two honest consequences of this split:
1. **The four paymasters bind to the staking-capable EntryPoint interface.** `PaymasterBase` (Section 4) declares an `IEntryPoint` with `addStake` / `unlockStake` / `withdrawStake`. `AereEntryPointV2` exposes deposit accounting but not staking, whereas the lightweight `AereEntryPoint` implements the full stake surface. The paymaster validation hook (`validatePaymasterUserOp`) is therefore exercised through the lightweight EntryPoint's `relayUserOp` path or through an off-chain relayer that calls the hook directly.
2. **`AereEntryPointV2.handleOps` charges a paymaster's deposit but does not itself call `validatePaymasterUserOp`.** It reads the paymaster address from `paymasterAndData[0:20]` and debits that deposit for `actualGasUsed * maxFee`, but the paymaster's own policy checks (per-sender caps, token pull, quota) are not invoked inside `handleOps`. Unifying paymaster validation into `handleOps` (so a single EntryPoint runs both account and paymaster validation in one batch) is a roadmap item. Until then, gasless sponsorship runs through the relayer-plus-lightweight-EntryPoint path where the hook is actually called.
Also note `handleOps` charges `actualGasUsed * maxFee` (the max fee, not an effective basefee-plus-tip price) and does not implement the full canonical prefund-and-refund economics. It is a compatible subset sufficient for the hosted relayer, not a byte-for-byte reimplementation of eth-infinitism v0.7.
---
## 4. The four gasless paymasters (live)
All four inherit `PaymasterBase` (`aere-contracts/contracts/paymaster/PaymasterBase.sol`), an in-house `Ownable` (OpenZeppelin v4.9) base that exposes `validatePaymasterUserOp` / `postOp` guarded by `onlyEntryPoint`, plus deposit and stake helpers. Each paymaster overrides `_validatePaymasterUserOp` with its own sponsorship policy. "Gasless" here means the user needs no native AERE: either the Foundation, a dApp, the user's stake, or an ERC-20 covers the gas.
### 4.1 Address map
| Paymaster | Address (chain 2800) | Status |
|---|---|---|
| `AereOnboardingPaymaster` | `0x4058E406475Dbed7056Aee0c808f293F05fEa879` | Live |
| `AereAppPaymasterFactory` | `0xEC22603E8712cBc5c31E53370D10f1a80CcB4DF0` | Live (permissionless factory) |
| `AereTokenPaymasterV2` | `0x217f56a5b0C7f35abe4D2fff924A6c13B85d7243` | Live (canonical) |
| `AereStakeQuotaPaymasterV2` | `0xE50464ca7E8E7F542D1816B3172a2330cFE384E8` | Live (canonical) |
| `AereTokenPaymaster` (V1) | `0xEb6e2Eb24e597C85392DdCD68a1F9b654FffdcB2` | Deprecated, do not use |
| `AereStakeQuotaPaymaster` (V1) | `0xD16C86D792444c2667A26dB62f01b90FC0DaB87b` | Deprecated, do not use |
### 4.2 `AereOnboardingPaymaster` (Foundation-funded)
The first-touch paymaster. It sponsors a hard-capped number of UserOps per address (`sponsoredOpsPerSender`, default 3, lifetime, never resets) under a sitewide per-UTC-day cap (`dailySitewideCap`, default 100) for Sybil resistance, with an optional target-contract whitelist. When its Foundation-funded balance drains it refuses all ops until a governance top-up. The design intent recorded in source is that 3 sponsored ops per address plus a sitewide daily cap lets a fixed, small AERE budget onboard a large number of first-time users without an automated refill.
### 4.3 `AereAppPaymaster` via `AereAppPaymasterFactory` (dApp-funded)
Any dApp calls `createPaymaster()` on the factory (no fee, permissionless) and becomes owner of its own `AereAppPaymaster`. The dApp funds it from its own treasury and sets a required target whitelist, an optional sender allowlist, and an optional per-sender lifetime cap. The Foundation contributes nothing. This is the standard pattern for a game, marketplace, or app to sponsor its own users' gas.
### 4.4 `AereTokenPaymasterV2` (pay gas in an ERC-20)
Lets a user pay for gas in any whitelisted ERC-20 rather than native AERE. It reads the token's USD price and the AERE/USD price from an oracle, converts the op's AERE cost to a token amount, applies a markup in basis points (`markupBps`, default 500 = 5%, capped at 3000), and pulls the tokens via `transferFrom` (the user pre-approves). V2 fixes a V1 medium-severity bug: V1's `withdrawToken` used `transferFrom(address(this), ...)`, which needs a self-allowance that never exists, permanently stranding collected token revenue; V2 uses `transfer`, the correct primitive for moving the contract's own balance, so the Foundation can sweep collected fees. Honest dependency: the paymaster's oracle reference is immutable and, per the registry, currently resolves to the legacy price source; repointing it to `AereOracleV2` requires a fresh paymaster deploy.
### 4.5 `AereStakeQuotaPaymasterV2` (stake AERE, get free transactions)
A Tron-style UX: a user's effective AERE stake buys a daily quota of sponsored UserOps (`quotaPerDay = stakedAERE * txPerKiloAerePerDay / 1000`, default 20 free ops per 1000 AERE staked, resetting each UTC day, no rollover). Effective stake is the sum of delegated stake across the validator set, the user's own validator self-stake, and active locked-staking positions. V2 fixes two confirmed high-severity liveness bugs in V1: V1 called a non-existent `balanceOf` on the real staking contract (so the delegated read always returned 0), and V1 decoded `AereLockedStaking.getLock` with the wrong field order (so any real lock hard-reverted validation and the user could never be sponsored). V2 reads the real public getters and aggregates them, matches the real `getLock` field order (amount first), bounds both scan loops at 100 entries so validation cannot be griefed out of gas, and wraps every external read in try/catch so a paused source degrades the quota to 0 rather than bricking validation. It reads the canonical `AereStakingV2` (`0x1D95eF6D17aeAB732dF914Ba2d018c270BC155FC`) and `AereLockedStaking` (`0x21108c28A849b05aE6b7a3a5bc435C9Bc897E7Ad`) getters.
---
## 5. EIP-7702 EOA delegation (live)
For users who already hold a MetaMask style EOA and do not want a new address, EIP-7702 lets that EOA point its delegation tuple at a shared implementation and gain smart-account behavior in place.
| Component | Address (chain 2800) | Live code | Status |
|---|---|---|---|
| `AereDelegate7702V2` | `0xC6d18e0Ce1B6467d952e31315505E0190a4A3310` | 4,535 B | **CANONICAL. Delegate here.** |
| `AereDelegate7702_v1_DEPRECATED_FLAWED` | `0x5673D92080efbd0987402E9335c14200d0a5EaeF` | 4,003 B | **DEPRECATED, two session-key scope escapes.** |
| `AereDelegationRegistry` | `0x6c25c07D134713b6C2F8E19D807423f022903D63` | 1,913 B | Live, informational only |
**Correction of 2026-08-01.** An earlier revision of this spec presented `0x5673D920…` as the live 7702 delegation target with no qualification, and did not mention `AereDelegate7702V2` at all. The canonical registry names `0x5673D920…` `AereDelegate7702_v1_DEPRECATED_FLAWED` for two confirmed session-key scope escapes, and `0xC6d18e0C…` has been live with 4,535 bytes of code the whole time. Worse, the earlier revision described the **defect itself as a feature**: it wrote that `addSessionKey` lets you "register a scoped secondary key with an expiry and a selector allowlist", which is precisely the too-narrow scoping model V2 exists to replace. A selector allowlist alone does not bound what a session key can move; that is the escape. The text below is retained for the V1 contract it describes, with that correction attached.
Because EIP-7702 transmits only code, re-pointing a delegation is a signature, not a migration. An EOA currently delegated to `0x5673D920…` should re-point to `0xC6d18e0C…`.
**`AereDelegate7702`** (`aere-contracts/contracts/delegation/AereDelegate7702.sol`) is the code an EOA delegates to. Because EIP-7702 transmits only code, not storage, every EOA delegating to this one implementation gets independent storage at its own account, and can re-point its delegation at any time with no migration. Its surface:
- `executeBatch(Call[])`: atomic multicall, authorized by the EOA's own transaction signature. The owner check is `msg.sender == address(this)`, which under 7702 is the EOA acting on itself. A transient reentrancy guard protects the batch path.
- `executeWithPasskey(Call, P256Signature)`: a single call authorized by a registered WebAuthn passkey, verified through the `0x100` precompile, with a relayer paying gas. A round-3 fix binds the signed `clientDataJSON` to the specific call by recomputing the expected challenge `keccak256(address(this), passkeyNonce, call)`, base64url-encoding it, and requiring the canonical `"challenge":"<expected>"` substring, which closes a replay hole where an unrelated WebAuthn login signature could have authorized arbitrary calls. A `passkeyNonce` gives replay protection.
- `addSessionKey` / `revokeSessionKey` / `executeWithSessionKey`: register a secondary key with an expiry and a **selector allowlist**, for low-risk, popup-free dApp flows, plus a kill switch. **Read this as the V1 design, not as a recommendation.** A selector allowlist constrains which functions a session key may call and does not constrain what it may move through them; that is one of the two scope escapes for which the canonical registry marks this contract deprecated. `AereDelegate7702V2` `0xC6d18e0C…` is the hardened replacement.
- `setPasskey(x, y)`: register one WebAuthn key. Phase 1 supports a single passkey; the source notes a Phase 2 extension to a MultiOwnable-style set is planned.
**`AereDelegationRegistry`** is a purely informational, permissionless index: an EOA self-registers (`register()`), the registry records the account's delegated code hash and timestamps, and dApps query it to render a "smart-wallet enabled" badge, discover batch-eligible EOAs, and coordinate paymaster eligibility. It holds no funds and has no authority over any account; wrong data only affects the registrant's own UX.
---
## 6. ERC-7579 modular accounts, session keys, social recovery (LIVE on mainnet 2800)
**Correction of 2026-08-01, and this is the largest one in this document.** An earlier revision of this section was headed "in development, not yet on mainnet" and said the modular account line was "not present in the canonical registry (`addresses.ts`) and is therefore not deployed to mainnet 2800", closing with "No mainnet address is cited because none exists yet."
**Every part of that is false.** All four contracts are in the canonical registry and all four carry live code, measured at head block 11,810,041 on `https://rpc.aere.network`:
| Component | Address (chain 2800) | Registry | Live code | Status |
|---|---|---|---|---|
| `AereModularAccountFactory` | `0xE3f45Ed4a81f982fF25ad172A72456a1833a440E` | `addresses.ts:378` | 6,271 B | Live |
| `AereSessionKeyValidatorV2` | `0xC06EAe63Ed12307F56A1506917C48C027D8852ff` | `addresses.ts:381` | 6,165 B | **Live, CANONICAL** |
| `AereSessionKeyValidator_v1_DEPRECATED` | `0x6e03A3D7A4c90d6f8dD6F0BA1F6e8aB1F8990D26` | `addresses.ts:380` | 4,698 B | **Deprecated, per-batch value-cap bypass** |
| `AereSocialRecoveryModule` | `0x077514DB2a85F239145537e8334CC99d42c9D812` | `addresses.ts:382` | 4,034 B | Live |
This was the single largest gap between what we shipped and what we said we shipped. It is recorded here rather than quietly corrected, because "in development" is a claim a reader makes decisions on.
Two things that were true in the earlier revision remain true and are not softened by the correction. These contracts have **no external security audit**, and their on-chain usage is thin. Being deployed is not being proven.
Source files: `aere-contracts/contracts/modular/AereModularAccount.sol`, `AereModularAccountFactory.sol`, `AereSessionKeyValidator.sol`, `AereSessionKeyValidatorV2.sol`, `AereSocialRecoveryModule.sol`.
**`AereModularAccount`** is an ERC-4337 account with ERC-7579-style pluggable modules of two types: validators (type 1, signature validation) and executors (type 2, may call `executeFromExecutor` and `setRootOwner`). It targets `AereEntryPointV2` exactly, and because that EntryPoint uses a single-dimension nonce, the account routes validation on the first signature byte: `0x00` selects the root-owner ECDSA fallback (`0x00 || 65-byte sig` over the EIP-191 envelope), `0x01` selects an installed validator module (`0x01 || 20-byte validator address || payload`). Validators return the canonical 4337 validation-data packing (`authorizer | validUntil<<160 | validAfter<<208`); since `AereEntryPointV2` reverts on any non-zero return and cannot unpack time bounds, the account unpacks the packing itself, enforces the time window against `block.timestamp`, and returns strictly `0` or `1`. It supports `execute` / `executeBatch`, EIP-1271 with the same domain-wrapping convention as the passkey account, and root-owner rotation gated to a self-call or an installed executor.
**`AereSessionKeyValidator` / `AereSessionKeyValidatorV2`** (ERC-7579 type 1) grant a session key a window (`validAfter`, `validUntil`), a per-op native value cap, and an allowlist of `(target, selector)` pairs, with `0x00000000` as the native-transfer sentinel. V2 is a hardened redeploy of the module logic that fixes a confirmed per-batch value-cap bypass (V1 checked each `executeBatch` sub-call's value independently against the per-op cap with no running sum, so a session key could move `maxValuePerOp * N` in one batch); V2 caps the sum of sub-call values, adds a defense-in-depth rule that a session key can never call account-admin selectors (`installModule`, `uninstallModule`, `setRootOwner`, `execute`, `executeBatch`) back into its own account regardless of the allowlist, and adds an optional rolling cumulative spend cap.
**`AereSocialRecoveryModule`** (ERC-7579 type 2 executor) is the guardian-based recovery the live passkey account lacks. An M-of-N guardian set, per account, drives an `initiateRecovery` then `supportRecovery` flow; after a 48 hour timelock and at least the threshold approvals, any guardian may `executeRecovery`, which calls `account.setRootOwner`. The current owner or the account can `cancelRecovery` at any time before execution. Guardian and threshold changes are owner-only (they require `msg.sender == account`).
When this line is audited and deployed, its addresses will be added to `addresses.ts` and this section updated to "live".
---
## 7. ERC-6551 token-bound accounts (live)
ERC-6551 gives every NFT its own wallet. These are account and registry primitives, not tokens: they mint nothing and have no supply.
| Component | Address (chain 2800) |
|---|---|
| `ERC6551Registry` | `0x7fFdA0AcDeB919938dB91dbEa779D841c833EF68` |
| `AereTokenBoundAccount` | `0xBc5e24180f3F75DC6b1965E4aaD3D4F184b6F9E2` |
**`ERC6551Registry`** (`aere-contracts/contracts/tokenbound/ERC6551Registry.sol`) is the verbatim `erc6551/reference` registry: a stateless, owner-less CREATE2 factory that deploys deterministic ERC-1167 minimal-proxy accounts bound to a `(chainId, tokenContract, tokenId)` tuple. The source notes the canonical public deployment lives at `0x000000006551c19487814612e58FE06813775758` on chains that received the deterministic Nick-factory deploy; on AERE the identical source was deployed at the address above (the address differs only because the deterministic factory salt was not replayed here, the bytecode and behavior are identical).
**`AereTokenBoundAccount`** (`aere-contracts/contracts/tokenbound/AereTokenBoundAccount.sol`) is the implementation the proxies point at. Its sole authority source is `ownerOf(tokenId)` on the bound ERC-721, read live. Whoever holds the NFT controls the account: they can `execute` arbitrary calls (operation 0, CALL only; DELEGATECALL and CREATE are rejected so the account can never be hijacked into changing its own logic), and the account produces EIP-1271 signatures on their behalf via OpenZeppelin `SignatureChecker` (so both EOA and smart-contract holders work). It also implements the ERC-4337 path against `AereEntryPointV2` (hardcoded as `ENTRY_POINT`), so a bundler or paymaster can sponsor the NFT wallet's gas. A live demo binds `AereNFT` (`0x3f9A9D9CAB005327869396C69bE226ef98039f1c`) token #1 to account `0x82D24cC4E09CaBfD9B00233438B8B6321CBC13Dd`.
---
## 8. Post-quantum smart account (live, roadmap #270)
The most experimental account in the stack, and the one whose scope needs the most careful statement. It is a working ERC-4337 v0.7 account whose sole owner is a NIST Falcon-512 lattice public key, with no classical ECDSA fallback.
| Component | Address (chain 2800) | Live code | Balance at head |
|---|---|---|---|
| `AerePQCAccountFactory` | `0xd5315Ea7caa60d320c4f34b1bEd70dd9cc02CE58` | 4,727 B | 0 AERE |
| `AerePQCAccount_sample` | `0xa42a5e7F72E46BadC11367650Ec34D676194326f` | 3,463 B | **0.03654 AERE** |
| `AereFalcon512Verifier` | `0x4E8e9682329e646784fB3bd01430aA4bA54D8fFC` | 7,333 B | 0 AERE |
**`AerePQCAccount_sample` is withdrawn as a reference example (2026-08-01).** An earlier revision presented `0xa42a5e7F…` as the account to look at when integrating. Two measured facts change that recommendation. First, it is not empty: it holds 0.03654 AERE at head, which makes it the only funded contract in this family apart from `AereEntryPointV2` (0.020438 AERE). Second, our internal audit records it as the one finding in this area that touches funds: on the deployed code the authorization path and the payment path are not separated, and the live factory still mints accounts with that same property. Neither the amount nor the mechanism makes this urgent, but a contract with a known unseparated authorization path and a non-zero balance is not a reference example, and this spec should not have been pointing integrators at it.
**NOT MEASURED, stated as such:** we did not reproduce a Falcon-512 signature against this account ourselves. Doing so would have meant running heavy proving work on live infrastructure. What is measured above is the balance and the code size; the audit finding is cited from the audit, not re-derived here.
**`AerePQCAccount`** (`aere-contracts/contracts/pqc/AerePQCAccount.sol`) owns exactly one Falcon-512 public key (897 bytes, NIST encoding, header byte `0x09`). Every authorization, a bundled UserOperation, an EIP-1271 check, or the direct `executeWithFalcon` path, is decided by the live on-chain `AereFalcon512Verifier`, which runs real lattice cryptography: SHAKE256 HashToPoint, 14-bit public-key decode, compressed-signature decode, negacyclic NTT multiply mod q=12289 in the ring, centering, and an l2-norm bound check. The signature envelope is `abi.encode(bytes nonce, bytes compSig)` (the 40-byte Falcon salt and the compressed s2 body); the message handed to the verifier is the raw 32 bytes of the relevant hash, which for a UserOperation is the userOpHash that already binds sender and chain. `validateUserOp` returns `0` or `1` per spec. `isValidSignature` is a `view`, so a post-quantum signature can be checked via `eth_call` without sending a transaction. An earlier revision presented that purely as an integration convenience. State it with its limit instead: a free `view` check tells you that a signature verifies against the key the contract holds, at the moment you ask. It is not an authorization, it writes nothing, and on this deployed code it is not separated from the payment path (see above). Treat it as a signature-verification oracle, not as a gate. `AerePQCAccountFactory` is a CREATE2 factory keyed on `(falconPubKey, salt)`, mirroring the passkey factory, binding each account to an immutable EntryPoint and Falcon verifier.
**Honest scope, stated exactly as the registry frames it.** This is Falcon-512 (NIST security level 1), not Falcon-1024. A Falcon-512 verify is heavy: roughly 10.5M gas per PQC authorization. That is real, and a full UserOperation through `AereEntryPointV2.handleOps` was demonstrated end to end with estimated 10,641,716 and measured 10,278,313 gas used, `opSuccess=true`. Those two figures are measured and stand.
**The comparison against a per-transaction cap is downgraded to NOT MEASURED.** An earlier revision asserted that the 10.5M figure "fits under the Fusaka EIP-7825 per-transaction cap of 2^24 = 16,777,216 gas". We have not confirmed that cap on the live chain. An `eth_call` carrying gas 0x2000000, which is 33,554,432 and therefore above the stated cap, succeeded without error, and `eth_estimateGas` does not discriminate. The only conclusive probe is a transaction above the cap, which we do not send. What we did measure is the header `gasLimit` at head, `0x1fffffffffffff` = 9,007,199,254,740,991, identical to genesis, so a 16,777,216 cap does not come from the block limit and cannot be inferred from it. The honest form of the claim is: the operation was executed on chain and succeeded, which is stronger evidence than a comparison against an unverified constant. A native SHAKE or keccak precompile would cut this sharply and is tracked as a separate roadmap item. PQC authorization here is app-layer and account-layer: it secures how this account authorizes operations. **It does not change consensus. AERE's validators sign classical secp256k1 ECDSA under QBFT. There is no post-quantum consensus on chain 2800 and this document never claims one.** That statement is not softened by anything below.
**The native PQC precompiles ARE live on mainnet 2800 (correction of 2026-08-01).** An earlier revision of this sentence said they "are proposed and in development on a scratch fork, not live on mainnet 2800". That is false, and provably so from any public RPC. They activated with the AerePQC fork at block 9,189,161: Falcon-512 at `0x0AE1`, Falcon-1024 at `0x0AE2`, ML-DSA-44 at `0x0AE3`, SLH-DSA-128s at `0x0AE4`, SHAKE256 at `0x0AE5`.
Verified directly rather than asserted. Calling `0x0AE5` with the documented framing, a 32-byte output-length word followed by the raw input bytes, returns for input `abc`:
```
0x483366601360a8771c6863080cc4114d8db44530f8f1e1ee4f94ea37e78b5739
```
and for empty input:
```
0x46b9dd2b0ba88d13233b3feb743eeb243fcd52ea62b81b82b50c27646ed5762f
```
Both match the FIPS 202 vectors exactly. A control call to `0x0AEF`, where no precompile exists, returns `0x`.
Two boundaries on that correction, and neither is optional. **`0x0AE6` and `0x0AE7` are TESTNET ONLY**; calling either on mainnet 2800 returns `0x`, which we measured, and no AERE document may present them as live on mainnet. And the gas cost of the Falcon-512 account path stated above is unchanged by the precompiles being live: a SHAKE precompile helps the hashing step, it does not make a 10.5M gas lattice authorization cheap. That remains a roadmap item. On the broader PQC verifier suite that this account draws on, we are not aware of any other public chain that verifies all of these on-chain; that is the claim, stated with that exact hedge, and nothing stronger. We are not the first public chain to verify a post-quantum signature on-chain, and we do not claim to be: Algorand shipped the native Falcon-1024 `falcon_verify` AVM opcode in a stable release roughly ten months before AERE's precompiles activated at block 9,189,161 (2026-07-12), and places Falcon deeper in its stack than AERE does, inside its own state proofs. That prior art, with sources and dates, is set out in `aere-research/research/pqc-onchain-verification.md`, Section 8.
---
## 9. Honest limitations
This layer is real and on chain, but a credible reader should weigh it against the following, all true today.
- **Decentralization.** AERE runs seven QBFT validators under one operator, one client (Besu), with no external security audit of these contracts and thin real-world usage. The wallet layer inherits that trust profile. Sponsorship and relaying currently depend on a Foundation-hosted relayer.
- **Two EntryPoints, one seam.** As described in Section 3, the account-facing `AereEntryPointV2` and the paymaster-facing lightweight `AereEntryPoint` are distinct, and `handleOps` charges but does not itself invoke the paymaster validation hook. Gasless flows run through the relayer-plus-lightweight-EntryPoint path. Unifying the two is a roadmap item.
- **EntryPoint economics are a subset.** `AereEntryPointV2` uses `maxFee` for gas charging and does not implement full canonical prefund-and-refund. It is sufficient for the hosted relayer, not a drop-in reimplementation of eth-infinitism v0.7.
- **Recovery.** The live passkey account's recovery is "keep at least one owner / add a backup owner". Guardian-and-timelock social recovery is a separate module, `AereSocialRecoveryModule` `0x077514DB…`, which is deployed (Section 6) but is not wired into the live passkey accounts.
- **ERC-7579 modular line IS live, and unaudited.** An earlier revision of this list said the modular account, its factory, both session-key validators and the social recovery module "carry no mainnet address and must not be presented as deployed". They all carry mainnet addresses and are all in the canonical registry; Section 6 has the measured code sizes. The limitation that survives is the real one: none of them has an external security audit and none has meaningful usage. Deployed is not audited.
- **Deprecated contracts are cited alongside their replacements, not silently.** Three entries in this document name a deprecated contract: the passkey factory `0x5FFa9a64…` (Section 2.1), the 7702 delegation target `0x5673D920…` (Section 5), and the session-key validator `0x6e03A3D7…` (Section 6). Each is printed with what is wrong with it and with the canonical successor. Integrators must point at the successors.
- **Post-quantum cost.** The Falcon-512 account is genuinely quantum-resistant at the account layer but costs roughly 10.5M gas per authorization and is level 1, not 1024. It is a real primitive with a heavy price, pending a SHAKE precompile.
- **Deprecated contracts remain on chain.** The V1 token and stake-quota paymasters, and the V1 passkey factory, are retained at their addresses; integrators must point at the canonical V2 addresses listed above.
---
## 10. Address appendix (verbatim from `addresses.ts`)
**EntryPoints**
- `AereEntryPointV2` (accounts): `0x8D6f40598d552fF0Cb358b6012cF4227B86aF770`
- `AereEntryPoint` (lightweight, paymaster relay): `0x19773ba45287A64B05d0BCBD59D1371BF51Bd5D2`
**Passkey wallets**
- `AerePasskeyAccountFactoryV2Fixed` (CANONICAL, create new wallets here): `0x8FA2B45D63EBaa4551A55d7f33a82d597e423428`
- `AerePasskeyAccountV2_sampleFixed`: `0x7C599babc66c350A6cE76673868253e2288D75C1`
- `AerePasskeyAccountFactoryV2` (**FLAWED, do NOT create new wallets here**): `0x5FFa9a6487DA4641a1A1e7900ff2bD4525D34fdA`
- `AerePasskeyAccountFactory` (V1, legacy): `0xfB0eF980667A79Fe1AB69c5f2d512118F1B30739`
**ERC-7579 modular line** (live; see Section 6)
- `AereModularAccountFactory`: `0xE3f45Ed4a81f982fF25ad172A72456a1833a440E`
- `AereSessionKeyValidatorV2` (CANONICAL): `0xC06EAe63Ed12307F56A1506917C48C027D8852ff`
- `AereSessionKeyValidator_v1_DEPRECATED`: `0x6e03A3D7A4c90d6f8dD6F0BA1F6e8aB1F8990D26`
- `AereSocialRecoveryModule`: `0x077514DB2a85F239145537e8334CC99d42c9D812`
**Paymasters**
- `AereOnboardingPaymaster`: `0x4058E406475Dbed7056Aee0c808f293F05fEa879`
- `AereAppPaymasterFactory`: `0xEC22603E8712cBc5c31E53370D10f1a80CcB4DF0`
- `AereTokenPaymasterV2`: `0x217f56a5b0C7f35abe4D2fff924A6c13B85d7243`
- `AereStakeQuotaPaymasterV2`: `0xE50464ca7E8E7F542D1816B3172a2330cFE384E8`
- `AereTokenPaymaster` (V1, deprecated): `0xEb6e2Eb24e597C85392DdCD68a1F9b654FffdcB2`
- `AereStakeQuotaPaymaster` (V1, deprecated): `0xD16C86D792444c2667A26dB62f01b90FC0DaB87b`
**EIP-7702**
- `AereDelegate7702V2` (CANONICAL, delegate here): `0xC6d18e0Ce1B6467d952e31315505E0190a4A3310`
- `AereDelegate7702_v1_DEPRECATED_FLAWED`: `0x5673D92080efbd0987402E9335c14200d0a5EaeF`
- `AereDelegationRegistry`: `0x6c25c07D134713b6C2F8E19D807423f022903D63`
**ERC-6551**
- `ERC6551Registry`: `0x7fFdA0AcDeB919938dB91dbEa779D841c833EF68`
- `AereTokenBoundAccount`: `0xBc5e24180f3F75DC6b1965E4aaD3D4F184b6F9E2`
- Demo NFT (`AereNFT`): `0x3f9A9D9CAB005327869396C69bE226ef98039f1c`
- Demo bound account: `0x82D24cC4E09CaBfD9B00233438B8B6321CBC13Dd`
**Post-quantum account**
- `AerePQCAccountFactory`: `0xd5315Ea7caa60d320c4f34b1bEd70dd9cc02CE58`
- `AerePQCAccount_sample`: `0xa42a5e7F72E46BadC11367650Ec34D676194326f`
- `AereFalcon512Verifier`: `0x4E8e9682329e646784fB3bd01430aA4bA54D8fFC`
**Supporting**
- `AereStakingV2`: `0x1D95eF6D17aeAB732dF914Ba2d018c270BC155FC`
- `AereLockedStaking`: `0x21108c28A849b05aE6b7a3a5bc435C9Bc897E7Ad`
- Foundation (owner of most `Ownable` contracts): `0x0243A4f47D44b40b65D33f20329dE20D00c6f3C3`. Measured at head on 2026-08-02 across all 191 address entries in `sdk-js/src/addresses.ts`: 71 answer `owner()`, 57 return this account, and 14 return an operational deployment account still awaiting transfer to governance. The word "all" was wrong and is corrected here rather than repeated.
**ERC-7579 modular line, corrected 2026-08-02.** This list previously read "in development, no mainnet address" for the whole line, which contradicted Section 6 and the honest-limits list in this same document. Measured at head: `AereModularAccountFactory` `0xE3f45Ed4a81f982fF25ad172A72456a1833a440E` (6,271 bytes), `AereSessionKeyValidatorV2` `0xC06EAe63Ed12307F56A1506917C48C027D8852ff` (6,165 bytes, canonical), `AereSessionKeyValidator_v1_DEPRECATED` `0x6e03A3D7A4c90d6f8dD6F0BA1F6e8aB1F8990D26` (4,698 bytes, superseded because its batch path capped value per sub-call instead of per batch) and `AereSocialRecoveryModule` `0x077514DB2a85F239145537e8334CC99d42c9D812` (4,034 bytes) are all deployed and all in the canonical registry. The one member of the line genuinely absent from the registry is the account implementation `AereModularAccount` itself. What remains true, and is the limitation that matters: none of this line has had an external audit, and none of it is wired into the live passkey accounts.