aere-research/research/specs/spec-antimev-mempool.md
Aere Network 4a0b48588c 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:02:30 +03:00

323 lines
32 KiB
Markdown

# AERE Anti-MEV Design: Threshold-Encrypted Mempool and Batch-Atomic Fair Settlement
Subsystem specification. Status as of 2026-07-11. Chain ID 2800 (Hyperledger Besu QBFT, 0.5-second blocks, seven validators, one operator).
## 0. Honesty banner (read first)
This document describes two complementary, **application-layer, opt-in** anti-MEV subsystems that AERE has built and, in part, demonstrated on chain 2800:
1. **AereShutterMempool** (V0 orchestration, V2/V3 threshold-BLS): a Shutter-style threshold-encrypted mempool that hides transaction contents until after an epoch's ordering is fixed.
2. **AereSettlement + AereSolverRegistry + AereVaultRelayer**: a CoW-style batch-atomic settlement DEX where users sign gasless orders and an authorised solver settles the whole batch in one transaction.
What this is **not**, stated plainly so no reader is misled:
- This is **not** base-consensus MEV protection. AERE runs Hyperledger Besu QBFT. A QBFT chain cannot gain a protocol-level encrypted mempool or enshrined proposer-builder separation (PBS) without a client fork. Both subsystems live entirely in smart-contract and off-chain-service space. Validators still sign classical QBFT and order the underlying commit/settle transactions like any other transaction.
- The threshold-encrypted mempool subsystem (V2/V3) is a **proof-of-concept**. Its contracts were deployed to chain 2800 and exercised end-to-end, but they are **not** registered in the canonical SDK address registry (`aerenew/sdk-js/src/addresses.ts`). Their addresses come only from repository deployment artifacts and are labeled as such throughout. Treat them as in-development, not production-registered.
- The batch-settlement contracts (AereSettlement stack) **are** in the canonical registry and were deployed 2026-05-31, but the off-chain solver service, the order-book API, and the front-end intent toggle that would make them a live product are **not yet deployed** (roadmap items recorded in the deployment manifest). On-chain, the settlement contract enforces atomicity and each user's own limit price; it does **not** enforce a uniform clearing price or capture surplus. Those are solver-trust and Phase-2 items, detailed in section 6.
- Both subsystems depend on a **single trusted coordinator/sequencer role** and (for V2/V3) a **trusted-dealer Shamir setup**. The solver set is a **permissioned allowlist** with a single bootstrap solver today. These are real centralisation weaknesses, called out in section 8.
Every contract address quoted below is either verbatim from `sdk-js/src/addresses.ts` (marked CANONICAL) or verbatim from a repository deployment artifact under `contracts/deployments/` (marked POC-ARTIFACT). No address is invented.
---
## 1. Threat model and design goals
Maximal Extractable Value (MEV) on a public chain is extracted in the gap between when a transaction is broadcast and when it is finally included and ordered. A party that can see pending transaction contents and influence ordering can front-run, back-run, and sandwich user trades. On AERE the primary ordering authority in that gap is the QBFT block proposer and, for the batch-settlement path, the solver that assembles a batch.
AERE attacks the problem from two directions at once:
- **Hide the contents** so that whoever orders transactions cannot tell what they contain until ordering is already fixed. This is the encrypted-mempool subsystem.
- **Remove the intra-batch ordering surface** so that a set of trades clears together, atomically, in one transaction, leaving no slot for an inserted sandwich. This is the batch-settlement subsystem.
Design goals, in priority order:
1. A proposer or sequencer must not be able to reorder or front-run based on plaintext it saw before the ordering was committed.
2. Any accepted decryption key must be provably the real threshold key, not a forgery, so decryption cannot be produced early or by a minority.
3. Liveness must not be held hostage by a single withheld reveal.
4. A user's downside must be bounded by its own signed limit, regardless of solver behaviour, and enforced atomically.
Non-goals for the current phase: trustless data-availability sampling, a distributed key generation (DKG) that removes the dealer, permissionless bonded solvers, on-chain uniform-price verification, and surplus capture. All are named in the roadmap (section 9).
---
## 2. Architecture overview
```
┌─────────────────────────────────────────────┐
user (gasless) │ ENCRYPTED MEMPOOL (opt-in, app-layer) │
───────────────► │ AereShutterMempool V2/V3 (PoC on 2800) │
ciphertext + │ commit → order → threshold-decrypt → reveal│
commitment └───────────────┬─────────────────────────────┘
│ (optional executor hook,
│ NOT wired to settlement today)
┌─────────────────────────────────────────────┐
user (gasless) │ BATCH SETTLEMENT (opt-in, app-layer) │
EIP-712 order │ AereSettlement + Registry + VaultRelayer │
───────────────► │ solver assembles batch → settle() atomic │
└─────────────────────────────────────────────┘
```
The two subsystems are conceptually a pipeline: encrypt order flow so the ordering party is blind, then settle the resulting batch atomically. **Honest note:** they are independent contracts today. AereShutterMempool V2/V3 expose an optional `executor` address that receives `execute(uint64,uint256,bytes)` calls on reveal, but AereSettlement does **not** implement that interface, so nothing wires the mempool's reveal path into the settlement contract at present. Integrating the two (mempool executor to a settlement adapter) is a roadmap item. Each subsystem is described independently below.
---
## 3. Subsystem A1: AereShutterMempool V0 (orchestration, code-only)
Source: `aerenew/contracts/contracts/mempool/AereShutterMempool.sol`. Compiled artifact present; **no deployment record exists** in `contracts/deployments/`, so V0 is treated as a code-only reference stage that V2/V3 supersede.
V0 establishes the on-chain commit/reveal skeleton without real threshold cryptography. Its stated model:
- **Keyper epochs.** The Foundation opens a keyper epoch (`openEpoch`) carrying a Merkle root of keyper BLS pubkeys and a hash of the aggregated public key. Opening a new epoch implicitly closes the prior one. At most one epoch is open at a time.
- **Ciphertext commitment.** Anyone submits an encrypted envelope with `commit` (or `commitForEpoch` to pin the expected epoch). Storage records the submitter, a SHA-256 hash of the ciphertext blob, an off-chain URI (ipfs/https), the block, and the epoch. `envelopeId = keccak256(epochId, ciphertextHash, sender)`.
- **Key release.** After `startBlock + REVEAL_DELAY_BLOCKS`, the Foundation posts the epoch decryption-key commitment (`releaseDecryptionKey`).
- **Reveal.** `markRevealed` checks that the submitted plaintext binds to the committed ciphertext and released key via `keccak256(abi.encodePacked(plaintext, releasedKey)) == ciphertextHash`, then flags the envelope decrypt-eligible.
The V0 invariant is timing-based: ciphertexts are visible but undecipherable at block N, ordering is finalised by consensus at block N, and the key is released only at block `N + REVEAL_DELAY_BLOCKS`, so post-hoc reordering gains nothing.
The contract records several audit fixes in-source, worth citing because they show the failure modes that motivated V2:
- MED #31: the constructor now requires `revealDelayBlocks >= 1` (a zero delay would defeat protection).
- HIGH #6: `commitForEpoch` lets a caller pin the expected epoch, so a reordered `openEpoch` cannot silently attach a ciphertext to the wrong keyper key and make it permanently undecryptable.
- HIGH #4: `commit` refuses to overwrite an existing envelope slot (no double-reveal via re-commit).
- HIGH #5: `releaseDecryptionKey` is one-shot per epoch (no key overwrite).
- HIGH #3: `markRevealed` is gated to the Foundation and requires the actual plaintext bytes, so nobody can forge a `Revealed` event with an arbitrary plaintext hash.
**Honest limitation of V0.** The "decryption key" is a keccak placeholder, not a real threshold key. The Foundation is the sole releaser and revealer. V0 is orchestration plumbing, not cryptographic anti-MEV. V2 replaces the placeholder with a real threshold-BLS scheme verified on-chain.
---
## 4. Subsystem A2: AereShutterMempool V2 (threshold-BLS, PoC deployed)
Source: `aerenew/contracts/contracts/mempool/AereShutterMempoolV2.sol`.
POC-ARTIFACT address (from `contracts/deployments/shutter-mempool-v2.json`, **not** in the canonical SDK registry): `0x135100D523edA8D0AdC70e08af905dE5042A9310`, deploy tx `0x87ad0d549c396bd5a002b6f9d984483ee3e16567261ff552fd3ce83fb00d3471`, deploy block 8934371, coordinator `0xbeB33D20dFBBD49eC7AC1F617667f1f02dfd6465`.
### 4.1 Cryptographic construction
V2 implements Boldyreva threshold BLS over the BLS12-381 curve and verifies it on-chain with the EIP-2537 pairing-check precompile at address `0x0f` (0x00...0f). EIP-2537 is live on chain 2800 via the Pectra ruleset (BLS12-381 precompiles at 0x0b through 0x11). The contract comment records that the precompile was probed live on 2800: a valid pairing identity returns 1, garbage input reverts.
Notation used in the contract and in this spec:
- Committee master secret `s`, Shamir `(t, N)`-shared off-chain by a dealer.
- Group public key `PK = s * g2` in G2. This is the epoch encryption public key.
- Keyper public keys `P_i = s_i * g2` in G2. These are the published verifiable-secret-sharing (VSS) commitments.
- Epoch identity `H1 = hashToCurve(epoch tag)` in G1.
- Epoch decryption key `DK = s * H1` in G1. This is the threshold signature over the epoch tag.
- Keyper share `sig_i = s_i * H1` in G1.
- `DK` is reconstructed off-chain from `t` shares by Lagrange interpolation, then verified on-chain.
Two on-chain pairing equalities, both expressed as a product-equals-identity so the single `0x0f` call decides them:
- Share validity: `e(sig_i, g2) == e(H1, P_i)`, checked as `e(sig_i, g2) * e(-H1, P_i) == 1`.
- DK validity: `e(DK, g2) == e(H1, PK)`, checked as `e(DK, g2) * e(-H1, PK) == 1`.
A share or a reconstructed DK that fails its pairing reverts, so a keyper cannot post an inconsistent share and no forged DK is ever accepted. Point encodings follow EIP-2537: a G1 point is 128 bytes, a G2 point is 256 bytes, and the two-pair pairing input is `2 * 384 = 768` bytes. The helper `_pairingCheck2` concatenates the four operands, staticcalls `0x0f`, and returns true only when the 32-byte output decodes to 1.
### 4.2 Epoch lifecycle and phases
An epoch moves through `Phase { None, Open, Ordered, Finalized }`:
1. **Committee registration.** `registerKeypers(addrs, pubkeys, t)` is one-shot and coordinator-only. It stores each keyper's address and 256-byte G2 public key and sets `threshold = t`, `keyperCount = N`.
2. **Open.** `startEpoch(epochId, pk, h1, h1neg)` publishes the epoch encryption key `PK`, the epoch identity `H1`, and its negation, and sets the epoch `Open`.
3. **Submit.** While `Open`, anyone calls `submitEncrypted(epochId, ciphertext, commitment)`. The full ciphertext blob (`U(256-byte G2) || C(payload XOR mask)`) is emitted for off-chain availability; only the binding commitment `keccak256(abi.encode(plaintext, opening))` is stored, and duplicates revert. The sequencer sees only ciphertexts.
4. **Order.** `commitOrdering(epochId, orderingRoot)` is sequencer-only and moves the epoch to `Ordered`. It commits a Merkle root over execution positions and **must** happen before any decryption share exists.
5. **Shares.** `submitDecryptionShare(epochId, keyperIndex, share)` reverts while the epoch is still `Open` or `None` (this is the no-early-decrypt guard) and reverts unless the share passes its on-chain pairing check. Each keyper index can submit once.
6. **Finalize.** Once at least `t` valid shares exist, `finalizeEpoch(epochId, dk)` accepts the off-chain Lagrange reconstruction only if it passes the DK pairing check, and moves the epoch to `Finalized`.
7. **Reveal and execute.** `revealAndExecute(epochId, index, plaintext, opening, proof)` walks the committed ordering strictly in sequence (`index == nextExecIndex`), checks the commitment was actually submitted and not already executed, verifies Merkle membership of `(epochId, index, commitment)` against the committed ordering root, and confirms the plaintext opens the commitment. It then advances the cursor and optionally forwards the plaintext to the configured `executor` as a best-effort `execute(uint64,uint256,bytes)` call (a failing consumer does not roll back the reveal record).
### 4.3 Anti-MEV invariant (V2)
1. Users submit ciphertexts plus a hiding, binding commitment while the epoch is `Open`; the sequencer sees only ciphertexts.
2. The sequencer commits an ordering (a Merkle root over positions) **before** any decryption share can exist; `submitDecryptionShare` reverts until ordering is committed.
3. Keypers post `t` valid shares; anyone reconstructs `DK` off-chain; the reconstruction is pairing-verified on-chain at `finalizeEpoch`.
4. Reveal walks the committed ordering strictly in order; a plaintext that does not open its committed value reverts, and an out-of-order reveal reverts.
Because ordering is fixed while contents are still encrypted, the party that fixes ordering cannot base that ordering on plaintext. Reordering after the fact is impossible: the ordering root is already committed, and reveals must follow it.
### 4.4 Keyper accountability
`reportInconsistentShare(epochId, keyperIndex, allegedShare)` lets anyone flag a keyper whose off-chain share is inconsistent with its on-chain VSS commitment `P_i`. The function reverts if the alleged share actually passes its pairing check, so false accusations are impossible. This emits a `KeyperFlagged` event only; it is a detection primitive, not slashing. Economic slashing of misbehaving keypers is not implemented.
### 4.5 Honest scope and assumptions (V2, from source)
- The symmetric unmasking step (hashing the GT pairing element to a byte mask for XOR) is done **off-chain** by the executor, because the `0x0f` precompile returns only a pairing-equality boolean and cannot output a GT element to hash. On-chain, the plaintext-to-commitment binding is a keccak commitment; DK correctness itself is fully pairing-verified on-chain, so decryption is deterministic and cannot be produced before `t` keypers act.
- `PK`, `P_i`, `H1`, and `H1neg` are published by the committee dealer at registration and epoch start. A malformed published point only breaks liveness (an honest DK or share would fail its pairing); it cannot let a non-committee actor forge `DK`, which still requires the master secret `s` that only `t`-of-`N` shares reconstruct.
- The PoC uses a **trusted-dealer Shamir setup**. A production system would run a DKG so no single party ever holds `s`. This is the single most important cryptographic caveat.
### 4.6 V2 known weakness (fixed in V3)
`revealAndExecute` advances the cursor only on a successful reveal. A single committed position whose plaintext or opening is never revealed (griefed, lost, or malformed) stalls every later position in that epoch forever. There is no skip and no timeout. V3 fixes exactly this.
### 4.7 V2 on-chain demonstration (from deployment artifact)
The V2 deployment artifact records an end-to-end run on chain 2800: a 3-of-5 committee, epoch `8934372` finalized with `shareCount` 3, an ordering root over 2 committed envelopes, and two reveal/execute transactions (`0x552f6779...c7bb`, `0x30f2d141...dc0c`), with `nextExecIndex` reaching 2. The recorded `dkHash` is `0xbb4f9385bf208ea729a5f353434ed126212e377847db7211ac0cfe7e906f3892`. These values are transcribed verbatim from `shutter-mempool-v2.json`; they evidence that the pairing-verified threshold path executed, not a production traffic claim.
---
## 5. Subsystem A3: AereShutterMempool V3 (liveness fix, PoC deployed)
Source: `aerenew/contracts/contracts/mempool/AereShutterMempoolV3.sol`.
POC-ARTIFACT address (from `contracts/deployments/shutter-mempool-v3.json`, **not** in the canonical SDK registry): `0x0C0b0560fcaa534fc30D29ae1D248CC1675281df`, deploy tx `0x72f333214cc61836c334a4e82548175f658db39416f103f5124c9917b40c7fa1`, deploy block 8952902, reveal window 60 seconds, coordinator `0xbeB33D20dFBBD49eC7AC1F617667f1f02dfd6465`.
V3 is byte-for-byte the V2 threshold-BLS design (same 3-of-5 committee shape, same commitment binding, same out-of-order reject, same no-early-decrypt guarantee) plus one liveness fix for the reveal-timeout DoS described in 4.6.
### 5.1 The reveal-window escape hatch
- `finalizeEpoch` now stamps a per-epoch `revealDeadline = block.timestamp + REVEAL_WINDOW`, where `REVEAL_WINDOW` is an immutable constructor parameter (60 seconds in the PoC deployment).
- `skipUnrevealed(epochId, index)` advances `nextExecIndex` past the current head position, but only when all four conditions hold: the epoch is `Finalized`, `index == nextExecIndex` (skip strictly at the head), `index < envelopeCount` (there is a position to skip), and `block.timestamp > revealDeadline` (the window has closed). It records `skippedPosition[epochId][index]` and emits `PositionSkipped`.
Because skip only ever acts on the head and a successful reveal advances the head atomically, the head is by construction the still-unrevealed position: a position that was already revealed sits behind the cursor, so skipping it reverts `OutOfOrder`. Revealing is never gated by the deadline; `revealAndExecute` keeps working after the window closes, so a late-but-honest opener can still land its position. Skip is only an escape hatch for a position nobody reveals.
### 5.2 Permissionless-reveal invariant
The liveness argument rests on a protocol invariant that the ciphertext must embed its own opening: the encrypted blob decrypts under `DK` to `(plaintext || opening)` such that `keccak256(abi.encode(plaintext, opening))` equals the committed commitment. This makes reveal permissionless: once `DK` is reconstructed, any holder of the decryption key can open any committed position and call `revealAndExecute`. A well-formed position is therefore always revealable by anyone, so `skipUnrevealed` only ever fires on a position whose opening was genuinely withheld, lost, or malformed, never to censor a revealable transaction. The tradeoff is a little worst-case latency: a stuck position is only skippable after `REVEAL_WINDOW` elapses.
### 5.3 V3 on-chain demonstration (from deployment artifact)
The V3 deployment artifact records the liveness fix exercised on chain 2800: epoch `8952903` finalized (`dkHash` `0xc435edc7fa0eff0f4d2a43556c7d5321294befb67988a03b546d261ddc06081b`, `revealDeadline` 1783700920), position 0 deliberately left unrevealed to simulate a withheld opening, an early `skipUnrevealed` reverting `SkipTooEarly`, then a post-deadline skip (`0x3ee66f74...43ca`) followed by reveal of position 1 (`0x39f75472...fa29`), with `finalNextExecIndex` reaching 2. Transcribed verbatim from `shutter-mempool-v3.json`. This demonstrates that one stuck position no longer stalls the epoch, which was the V2 DoS.
### 5.4 V3 residual weaknesses
The same trusted-dealer, off-chain-unmasking, and single-coordinator caveats from 4.5 carry over unchanged. V3 fixes liveness, not decentralisation. Keyper slashing is still detection-only.
---
## 6. Subsystem B: AereSettlement, solver, and relayer (CoW-style, canonical)
Sources: `aerenew/contracts/contracts/dex/AereSettlement.sol`, `AereSolverRegistry.sol`, `AereVaultRelayer.sol`.
CANONICAL addresses (verbatim from `sdk-js/src/addresses.ts`):
- `AereSettlement` = `0x9C2957b1622567B4802E4AFd4c42FB2ec70dE875` (Tier-1.9 batch-auction DEX, deployed 2026-05-31)
- `AereSolverRegistry` = `0xDBD29332a9993d2816EF0bD240288E03a8103f3B`
- `AereVaultRelayer` = `0x9FCA122e87E36D7cba20DbEA7b9b7354A4Cece91`
The settlement-stack deployment artifact additionally records a bootstrap solver `0xE04587e0c6da6a30a24EFe7d59701A2BfFb706d9` and Foundation owner `0x0243A4f47D44b40b65D33f20329dE20D00c6f3C3`.
### 6.1 What it is
A simplified port of CoW Protocol's GPv2 stack. Users sign EIP-712 `Order` structs off-chain (gasless). An authorised solver collects open orders and submits the whole batch atomically via `settle(orders, signatures, executions)`. MEV protection comes from atomicity: because the whole batch executes in one transaction, no actor can insert a sandwich between two orders in the batch.
The `Order` struct carries `user`, `sellToken`, `buyToken`, `sellAmount` (exact, fill-or-kill in v1), `minBuyAmount` (the user's own slippage floor), `validTo` (unix deadline), and `salt` (per-user replay nonce). The `Execution` struct carries just `executedBuyAmount`, the solver's claim of what it will deliver.
`orderHash` builds the EIP-712 digest under the domain `EIP712("AereSettlement", "1")`. The contract inherits `Ownable`, `ReentrancyGuard`, and `EIP712` from OpenZeppelin.
### 6.2 The settle() flow
`settle` is `onlySolver` (allowlist-gated) and `nonReentrant`. It requires equal-length arrays and a non-empty batch. For each order it:
1. Checks `block.timestamp <= o.validTo` (not expired).
2. Requires `executions[i].executedBuyAmount >= o.minBuyAmount` (the user's floor is respected).
3. Recovers the EIP-712 signer with `ECDSA.recover` and requires `signer == o.user`.
4. Requires the order hash is not already `filled`, then marks it filled (replay protection).
5. Pulls `sellAmount` from user to solver through `AereVaultRelayer.transferFromUser` (users approved the relayer, not the settlement contract).
6. Pushes `executedBuyAmount` of `buyToken` from solver to user via `safeTransferFrom` (the solver must have approved the settlement contract for the buy token, or supply liquidity via internal coincidence-of-wants matching).
If any step in the loop reverts, the entire batch reverts, so a user can never be pulled from without receiving at least its `minBuyAmount`. `cancelOrder` lets a user mark its own order consumed on-chain; `isOrderFillable` is a view for open-order checks.
### 6.3 AereVaultRelayer and AereSolverRegistry
`AereVaultRelayer` follows the GPv2VaultRelayer pattern: users approve the relayer once per token (typically MaxUint256), and only the single authorised `settlement` address may call `transferFromUser`. Separating allowances from the settlement contract lets the settlement contract be upgraded without users re-approving. The owner can rotate the settlement address.
`AereSolverRegistry` follows GPv2AllowListAuthentication: an owner-curated `isSolver` allowlist with `addSolver`/`removeSolver`. Phase 1 (current) is an owner-curated allowlist; Phase 2 (not built) is intended to add permissionless bonded-solver entry with slashable AERE stake.
### 6.4 What settle() does and does not guarantee (critical honesty section)
On-chain guarantees that hold today:
- **Atomic batch execution.** No sandwich can be inserted between orders in the batch, because they share one transaction.
- **Per-order limit protection.** Every filled order delivers at least its signed `minBuyAmount`, or the whole batch reverts.
- **Signature and replay safety.** Each fill requires a valid EIP-712 signature from `o.user`, and each order hash can be filled at most once.
What the contract does **not** enforce, despite the docstring's description of finding a "uniform clearing price":
- **Uniform clearing price is not verified on-chain.** The contract checks each order against its own `minBuyAmount` only. It does not check that all orders in a pair cleared at one common price. Uniform-price fairness is a solver behaviour, not a contract invariant. A solver could fill different users of the same pair at different effective prices as long as each clears its own floor.
- **No surplus capture and no protocol fee.** Unlike CoW, this contract has no fee parameter and no surplus accounting. Any price improvement above `minBuyAmount` accrues wherever the solver directs it, not to users or to a protocol sink, on-chain. There is no wiring to `AereSink` (`0x69581B86A48161b067Ff4E01544780625B231676`) for MEV or surplus redistribution in this contract today.
- **No partial fills, no ERC-1271 signatures, no balance modes, no dynamic fees.** The source explicitly states these are omitted and that audit-grade hardening is deferred to Phase 2.
The practical security posture is therefore: **atomicity plus your own limit price protect you from the worst MEV, but you are trusting the permissioned solver for fair (uniform, surplus-returning) pricing above your floor.** That is a meaningful and honest distinction from a fully trust-minimised CoW deployment.
### 6.5 Off-chain solver and relayer service (roadmap, not deployed)
The settlement-stack deployment artifact lists the operational pieces that would make this a live product, all recorded as next steps rather than done:
- Rotate the bootstrap solver to a dedicated solver address.
- Deploy an `aere-cow-solver` Docker container running the default solver.
- Stand up an order-book API at `api.aere.network/cow` storing open orders in a separate explorer-db schema.
- Add an MEV-protected (intent) toggle to `/swap.html` alongside the direct AereSwap mode.
- Phase 2: bonded-solver registry for permissionless entry.
None of these off-chain services is confirmed deployed in the repository artifacts. The on-chain contracts are live; the surrounding solver and order-book infrastructure is in development.
---
## 7. How the two subsystems relate
The intended composition is: encrypted mempool blinds the ordering party to order flow, and batch settlement removes the intra-batch ordering surface. In a fully integrated design, decrypted orders from an epoch would be handed to a settlement executor that clears them as a batch.
**Honest current state:** the two are not integrated on-chain. AereShutterMempool V2/V3 can call an optional `executor.execute(uint64,uint256,bytes)` on reveal, but AereSettlement exposes `settle(...)`, not that interface, and no adapter connects them. A user today would use either the encrypted-mempool PoC or the batch-settlement stack, not a wired pipeline. Building a mempool-to-settlement executor adapter is a named roadmap item.
---
## 8. Consolidated honest limitations and security posture
1. **App-layer, opt-in, not base consensus.** Neither subsystem changes QBFT. Validators sign classical QBFT and can still censor or reorder the underlying commit and settle transactions at the base layer. The encrypted mempool protects transaction **contents** from being front-run; it does not stop a proposer from censoring or reordering ciphertext-commit transactions (though reordering opaque ciphertexts yields no MEV, since the proposer cannot see what is inside). This is not enshrined PBS and not a protocol-level encrypted mempool.
2. **Centralised operator and validator set.** Chain 2800 runs seven validators under one operator, one client (Besu). The encrypted-mempool coordinator and sequencer default to a single address (`0xbeB33D20dFBBD49eC7AC1F617667f1f02dfd6465` in the PoCs). A single sequencer commits ordering. This is a trust assumption, not a decentralised guarantee.
3. **Trusted-dealer key setup (V2/V3).** The threshold master secret is Shamir-shared by a dealer in the PoC. Until a DKG replaces the dealer, one party transiently holds `s`. A live product must not ship on a trusted dealer.
4. **Keyper accountability is detection-only.** Inconsistent shares can be flagged on-chain but are not economically slashed.
5. **Permissioned solvers, single bootstrap solver.** The settlement allowlist is owner-curated with one bootstrap solver today. Permissionless bonded, slashable solver entry is unbuilt (Phase 2).
6. **Uniform pricing and surplus are off-chain trust.** As detailed in 6.4, the settlement contract enforces atomicity and per-order floors only.
7. **PoC status of the encrypted mempool.** V2/V3 are demonstrated but not promoted to the canonical SDK registry, not audited externally, and carry no production traffic claim. V0 is code-only.
8. **No external audit.** The in-source audit-fix annotations reflect internal review. There is no third-party audit of these contracts.
Stating these plainly is deliberate. For a chain with thin usage and a small validator set, credibility comes from precise scoping, not from overclaiming that app-layer opt-in tooling is base-layer MEV resistance.
---
## 9. Roadmap (in development, not yet live)
- **DKG for the keyper committee** to remove the trusted dealer, so no party ever holds the master secret `s`.
- **On-chain (or precompile-assisted) symmetric unmasking.** The V0 source names a proposed decryption precompile at `0x102`; this is a proposed, not-live capability and would need a client fork on a scratch fork, not mainnet 2800. Today unmasking is off-chain and only DK correctness is verified on-chain.
- **Keyper slashing** built on the existing `reportInconsistentShare` detection primitive.
- **Mempool-to-settlement executor adapter** so a decrypted epoch feeds AereSettlement as a batch (section 7).
- **On-chain uniform-price verification and surplus capture** in the settlement path, with surplus routed to users or to `AereSink`.
- **Permissionless bonded-solver registry** with slashable AERE stake (Phase 2 of AereSolverRegistry).
- **Promotion of the encrypted-mempool contracts into the canonical SDK registry** once the trusted-dealer and audit gaps close.
- **Off-chain solver and order-book service deployment** (aere-cow-solver, `api.aere.network/cow`, `/swap.html` intent toggle).
- **External audit** of the full anti-MEV stack.
---
## 10. Address and artifact appendix (verbatim)
CANONICAL, from `aerenew/sdk-js/src/addresses.ts`:
| Contract | Address |
|---|---|
| AereSettlement | `0x9C2957b1622567B4802E4AFd4c42FB2ec70dE875` |
| AereSolverRegistry | `0xDBD29332a9993d2816EF0bD240288E03a8103f3B` |
| AereVaultRelayer | `0x9FCA122e87E36D7cba20DbEA7b9b7354A4Cece91` |
| AereSink (fee sink, referenced, not wired here) | `0x69581B86A48161b067Ff4E01544780625B231676` |
| Foundation (owner) | `0x0243A4f47D44b40b65D33f20329dE20D00c6f3C3` |
POC-ARTIFACT, from `aerenew/contracts/deployments/` (NOT in the canonical SDK registry; treat as proof-of-concept, in-development):
| Contract | Address | Source artifact |
|---|---|---|
| AereShutterMempoolV2 | `0x135100D523edA8D0AdC70e08af905dE5042A9310` | `shutter-mempool-v2.json` |
| AereShutterMempoolV3 | `0x0C0b0560fcaa534fc30D29ae1D248CC1675281df` | `shutter-mempool-v3.json` |
| PoC coordinator/deployer (both) | `0xbeB33D20dFBBD49eC7AC1F617667f1f02dfd6465` | both artifacts |
| Settlement bootstrap solver | `0xE04587e0c6da6a30a24EFe7d59701A2BfFb706d9` | `settlement-stack.json` |
AereShutterMempool V0 (`AereShutterMempool.sol`) is code and compiled artifact only, with no deployment record, and is superseded by V2/V3.
Precompile dependency: EIP-2537 BLS12-381 pairing check at address `0x0f`, live on chain 2800 under the Pectra ruleset (BLS12-381 precompiles at 0x0b through 0x11).
Contract sources of record:
- `aerenew/contracts/contracts/mempool/AereShutterMempool.sol` (V0)
- `aerenew/contracts/contracts/mempool/AereShutterMempoolV2.sol`
- `aerenew/contracts/contracts/mempool/AereShutterMempoolV3.sol`
- `aerenew/contracts/contracts/dex/AereSettlement.sol`
- `aerenew/contracts/contracts/dex/AereSolverRegistry.sol`
- `aerenew/contracts/contracts/dex/AereVaultRelayer.sol`