Intent 105: RFQ, how quotes reach you
Before the order there is a request. How iLayer broadcasts your RFQ to every solver over Waku, and how each answer comes back to you alone.
Two contracts on every chain, one message each way. Follow an order from createOrder() on the Hub to fillOrder() on the Spoke and back.
iLayer’s smart contracts are the backbone of the network. They let a user and a solver exchange value without counterparty risk, through a locking mechanism that behaves like an atomic swap, mediated by the Router cross-chain messaging adapter.
The system is split into two contracts, both deployed on every supported chain:
On EVM chains both are deployed with Create3, so the Hub and the Spoke sit at the same deterministic address on every chain. Let’s follow one order through them.
The user builds an Order: input tokens (ERC-20, ERC-721 or ERC-1155), the desired outputs on the destination chain, deadlines, destination chain id, the optional sponsored flag and the optional execution hook. Then the user signs it off-chain.
createOrder() on the OrderHub does four things in sequence:
ACTIVE and emits OrderCreated with the orderId and nonce.PENDING.Nothing has crossed a bridge except a message. Your tokens are exactly where you left them, held by a contract you can withdraw from if the order expires.
Hub and Spoke never talk to LayerZero or Axelar directly. They hand a Message struct to the Router: which bridge, target chain id, receiver, payload, extra bridge data and sender. The Router dispatches it, or reverts with UnsupportedBridgingRoute() if that bridge is not implemented on this chain.
Only whitelisted contracts may call the Router. That keeps attackers away from the privileged messaging path and keeps the event log clean. On the receiving side the Router invokes onMessageReceived on the target contract, which is how the Spoke learns about a new pending order and how the Hub learns about a fill.
A solver calls fillOrder() on the OrderSpoke of the destination chain. Before anything moves, the Spoke checks that the order was registered, is valid, has not expired, has not already been filled, and that the caller is allowed: either it is the designated primary filler, or the primary deadline has passed and the order is open to anyone.
The solver then provides the output tokens. The Spoke verifies that balances match the amounts in the Order; any positive slippage stays in the Spoke. If the Order carries a hook, the Spoke executes it through the Executor, isolated against reentrancy. Finally the Spoke emits OrderFilled and sends a message back to the origin chain.
When the fill message arrives, the Hub marks the order FILLED, emits OrderSettled, and releases the input tokens it was holding to the solver’s funding wallet.
That closes the loop: the user has the outputs on the destination chain, the solver has the inputs on the source chain, and at no point did either side depend on the other’s honesty. The user could not take the inputs back after the fill, and the solver could not take them before it.
The Hub and Spoke expose a list of named errors, and reading them is the fastest way to understand the guarantees:
RequestNonceReused, InvalidOrderSignature: no replay, no forged orders.OrderDeadlinesMismatch, InvalidDeadline: the primary window must end before the deadline, and the deadline must respect the maximum span.RestrictedToPrimaryFiller, OrderAlreadyFilled, OrderExpired: exactly one fill, by the right party, in time.OrderCannotBeWithdrawn: the time buffer after expiry protects the solver from a withdraw racing a late fill.ExternalCallFailed: a broken hook reverts the fill instead of leaving a half-executed order.Every one of these is a rule the code enforces, not a promise in a document. Next we look at how quotes reach you before any of this starts.
Before an order exists there is a request for quote. Next: the RFQ, and why it runs on Waku instead of a server.
Before the order there is a request. How iLayer broadcasts your RFQ to every solver over Waku, and how each answer comes back to you alone.