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.
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
- LOCK on origin. The user calls
AereSpokePool.open()(or the gaslessopenFor/openForPQC), locking input and emittingOpenwith the declared output leg. - 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 emitsFilled. - 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.
- FINALITY prover.
contracts/interop/AereZkQbftLightClient.sol(live on consuming chains at0xCaDA54FA...6488) proves, in the SP1 zkVM (guestqbft-lightclient-guest), that one Aere header is QBFT-final: it decodes the Besu QBFTextraData, recomputes both Besu hash pre-images, ecrecovers each committed seal, and asserts the distinct-validator count reaches the QBFT quorumceil(2N/3)(5-of-7 for Aere's live N=7 set), binding to an IMMUTABLE validator-set anchor. The settler declares this interface asIAereQbftFinalityLightClientfor 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. - INCLUSION plus delivery.
contracts/interop/AereOutboundVerifierV2.solis 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 committedvalidatorSetRootto 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 andFILLED_EVENT_SIGas the event, re-derivesfillCommitmentfrom 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 plusfillCommitment. - 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-guestELF, 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.