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

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

9.8 KiB

Aere Network ERC-7683 Destination Settler

The receiving side of cross-chain intents on Aere Network. This document explains how the origin and destination halves compose, the solver flow, the repayment proof path, and the honest status of what is on-chain today versus what needs the off-chain solver and finality prover.

Contract: aerenew/contracts/contracts/intents/AereDestinationSettler.sol Interface: ERC-7683 IDestinationSettler (contracts/intents/IERC7683.sol) Test: aerenew/contracts/test/destination-settler.test.js (4 passing)

Why a destination settler at all

Aere Network already ships the ORIGIN side of ERC-7683: AereSpokePool (contracts/intents/AereSpokePool.sol) is an IOriginSettler. A user locks input tokens on the origin chain and emits an Open event that declares the OUTPUT leg they want on the destination chain (token, amount, recipient). What was missing is the DESTINATION half: the contract a solver calls to actually deliver that output and to record the delivery so the solver can be repaid. AereDestinationSettler is that half.

An intent design like this removes the need to bootstrap bridge liquidity. A classic lock-and-mint bridge must pre-fund destination-side liquidity before it can serve a single transfer. An intent bridge does not: the SOLVER fronts their own liquidity to fill the order, and is repaid the locked input on the origin chain after the fill is proven final. Aere Network therefore never has to seed or custody bridge liquidity; a competitive market of solvers supplies it per intent.

How origin and destination compose

  ORIGIN CHAIN                         AERE NETWORK (destination, chain 2800)
  ------------                         --------------------------------------
  User -> AereSpokePool.open()
     locks input, emits Open
     (declares output leg)  ........>  Solver reads Open, calls
                                       AereDestinationSettler.fill(orderId,
                                         originData, fillerData)
                                         - delivers output to recipient
                                         - records the fill
                                         - emits Filled
                                       (fill is included in an Aere block)
                                              |
                                       Aere QBFT finalizes the block
                                              |
  Origin-side verifier  <........  SP1 proof: "this Filled log was included
     (AereOutbound-                 in a QBFT-final Aere block", validator-set
      VerifierV2 pattern)           anchored (off-chain prover + on-chain verify)
     repays the solver the
     locked input

originData is the origin settler's declared output leg. AereSpokePool builds it (see _buildResolved) as:

abi.encode(bytes32 orderId, address outputToken, uint256 outputAmount, bytes32 recipient)

fillerData is the solver's actual delivery plus their origin-chain repayment address:

abi.encode(address deliveredToken, uint256 deliveredAmount, address deliveredRecipient, bytes32 repaymentAddress)

The solver flow

  1. LOCK on origin. The user calls AereSpokePool.open() (or the gasless openFor / openForPQC), locking input and emitting Open with the declared output leg.
  2. FILL on destination. A solver calls AereDestinationSettler.fill(orderId, originData, fillerData) on Aere Network. The settler pulls the output token from the solver and delivers it straight to the recipient (it never custodies the output), records the fill, and emits Filled.
  3. PROVE and REPAY on origin. Once the fill's block is QBFT-final on Aere, an SP1 proof of finality plus log inclusion is generated off-chain and submitted to an origin-side verifier, which repays the solver the locked input. No trusted relayer is required; the proof is validator-set anchored.

On-chain fail-closed guarantees

fill(orderId, originData, fillerData) reverts, and records nothing, when:

Condition Error
orderId argument does not equal the id inside originData OrderIdMismatch
Declared recipient is zero ZeroRecipient
Declared output token is zero, or declared amount is zero ZeroAddress / ZeroAmount
Order already filled once AlreadyFilled
Delivered token or recipient does not match the declared output, or delivered amount is short OutputMismatch

A fill that delivers the wrong token, to the wrong recipient, or short can never be recorded, and an order can be filled at most once, so a solver can never be repaid twice for a single intent. Delivery is atomic: the output is pulled from the solver and sent to the recipient with OpenZeppelin SafeERC20 in the same call, after the record is written (checks, effects, interactions; nonReentrant).

After a successful fill the settler exposes:

  • fills(orderId) / getFill(orderId): the full fill record (filler, output token, amount, recipient, timestamp, block, repayment address).
  • isFilled(orderId): whether the order has been filled.
  • fillCommitment(orderId): the canonical repayment commitment (see below).

Repayment proof path (wiring to Aere's QBFT finality)

The origin chain must learn "this exact fill was included in a block that is FINAL on Aere Network" without trusting a relayer. Aere Network already has the machinery, and the settler is built to plug into it.

  1. FINALITY prover. contracts/interop/AereZkQbftLightClient.sol (live on consuming chains at 0xCaDA54FA...6488) proves, in the SP1 zkVM (guest qbft-lightclient-guest), that one Aere header is QBFT-final: it decodes the Besu QBFT extraData, recomputes both Besu hash pre-images, ecrecovers each committed seal, and asserts the distinct-validator count reaches the QBFT quorum ceil(2N/3) (5-of-7 for Aere's live N=7 set), binding to an IMMUTABLE validator-set anchor. The settler declares this interface as IAereQbftFinalityLightClient for reference. This prover runs on the consuming/origin chain, not on Aere Network (Aere is native there), so the settler does not call it on-chain.
  2. INCLUSION plus delivery. contracts/interop/AereOutboundVerifierV2.sol is the origin-side pattern that CONSUMES such a proof. It accepts an SP1 proof that a specific Aere log was included in a QBFT-final Aere block (finality plus receipt inclusion, one guest), binds the proof's committed validatorSetRoot to its own immutable anchor, enforces exactly-once replay, and only then acts. An origin-side verifier built on this pattern trusts THIS settler as the source address and FILLED_EVENT_SIG as the event, re-derives fillCommitment from the proven log, and repays the solver.

The on-chain surface the settler must provide for that path is the Filled event, the persisted FillRecord, and the fillCommitment binder. It provides all three.

fillCommitment(orderId) binds:

keccak256(abi.encode(
    AERE_CHAIN_ID,          // 2800, matching AereOutboundVerifierV2.AERE_CHAIN_ID
    address(this),          // this settler
    orderId,
    filler,                 // solver, repaid on the origin chain
    outputToken,
    outputAmount,
    recipient,
    repaymentAddress        // solver's origin-chain repayment address
))

Proof shape

The origin-side proof is exactly the shape AereOutboundVerifierV2 already documents: an SP1 Groth16 proof over Aere QBFT finality plus log inclusion, with the guest committing validatorSetRoot into its public values and the on-chain verifier binding that root to an immutable anchor. Public values are the 288-byte (9 word) abi.encode of (uint64 chainId, uint64 blockNumber, bytes32 blockHash, bytes32 root, uint8 claimKind, bytes32 commitment, bytes32 validatorSetRoot, bool finalized, bool falconVerified). The pure finality proof consumed by AereZkQbftLightClient is the narrower 192-byte (6 word) form. The exact receipt-trie inclusion encoding for this settler's Filled log is [VERIFY: confirm against the final AereOutboundVerifierV2 guest].

Honest status

ON-CHAIN and tested today (test/destination-settler.test.js, 4 passing on Hardhat):

  • A valid fill delivers the output to the recipient, records the fill, emits Filled, and exposes the record plus fillCommitment.
  • A duplicate fill reverts (AlreadyFilled).
  • A mismatched-output fill reverts (OutputMismatch), tested for short amount, wrong recipient, and wrong token.
  • A zero-recipient fill reverts (ZeroRecipient).

NOT on one chain, marked [MEASURE]:

  • The end-to-end cross-chain flow (off-chain SP1 prover run over the qbft-lightclient-guest ELF, plus the origin-chain verifier deployment that repays the solver) is NOT exercised by this contract or its unit test. It needs the separate off-chain prover and a second chain. This is a settlement / interop composition, not a claim of a live cross-chain transfer.
  • The exact receipt-trie inclusion encoding is [VERIFY: confirm against the final AereOutboundVerifierV2 guest].

SCOPE. This is a settlement / interop-layer contract. It does not touch, gate, or change Aere Network consensus, which remains classical secp256k1 ECDSA QBFT. The finality proof above is a proof ABOUT that QBFT finality, verified classically today. That is the honest and intended posture, not a post-quantum consensus claim. The falconVerified slot in the proof public values is recorded and required to be false on mainnet.

Repayment security reduces to the honesty of at least 2f+1 of Aere Network's QBFT validators (5-of-7 today, all Foundation-operated: an operator assumption, not an economic one) plus a sound SP1 / Groth16 verifier and the correct validator-set anchor. Recording a fill here is permissionless and does not, by itself, move any origin funds; repayment happens only on the origin chain and only behind the proof.