# 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: ```solidity abi.encode(bytes32 orderId, address outputToken, uint256 outputAmount, bytes32 recipient) ``` `fillerData` is the solver's actual delivery plus their origin-chain repayment address: ```solidity 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: ```solidity 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.