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

40 KiB

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.