Intent 107: Execution hooks, paying for cross-chain code
callRecipient, callData, callValue. The three optional fields that turn a swap into any DeFi action, and how the Executor keeps them safe.
Every order carries two deadlines and the Hub adds a third clock. Here is what each one protects, and the attack the time buffer was built to stop.
iLayer is an on-chain order book. An order sits in a pending state until it is filled or expires, and time is what decides which. Three parameters govern it: the order’s primaryFillerDeadline, the order’s deadline, and the Hub’s timeBuffer. A fourth, maxOrderDeadline, caps how far ahead you may set the second.
When you accept a quote, the solver that gave it becomes the filler. Until primaryFillerDeadline only that address can call fillOrder(); anyone else gets RestrictedToPrimaryFiller().
After the deadline the order opens to the whole network, preserving the original input and output amounts. So a solver has every reason to fill fast: the quote it won can be taken by a competitor the moment its window closes. Choose this parameter carefully when integrating: too short and honest solvers may miss it, too long and a lazy one can sit on your order.
deadline is the time to live. Past it the Spoke refuses fills with OrderExpired(). The Hub checks at creation that the primary window ends before the deadline (OrderDeadlinesMismatch) and that the deadline does not exceed maxOrderDeadline (InvalidDeadline).
The request that wraps the order has its own short deadline too: an OrderRequest expires if not submitted in time, which stops a stale signed order from being created weeks later.
Imagine an order filled exactly at its expiry. The solver pays the outputs on the destination chain and the Spoke sends the settlement message. Cross-chain messages take time. If the user could withdraw the inputs the instant the deadline passed, the withdraw could land before the settlement message, and the user would walk away with both sides: the outputs already received and the inputs back. The solver would be rugged.
The Hub’s timeBuffer closes that window. Withdrawal is allowed only when now > deadline + timeBuffer. The buffer is roughly one block time, small enough to be invisible in normal use and large enough to let a legitimate fill settle.
function withdrawOrder(Order memory order, uint64 orderNonce) external nonReentrant
Pass the order struct and the nonce you stored at creation, and the Hub returns your inputs, provided the order is still ACTIVE, the caller is the user, and the buffer has elapsed. Anything else reverts with OrderCannotBeWithdrawn. That is why the integration guide insists on saving orderId and orderNonce: they are your receipt.
Three clocks, no trust required at any tick.
Last chapter of the series: the three optional fields that let an order pay for code on another chain.
callRecipient, callData, callValue. The three optional fields that turn a swap into any DeFi action, and how the Executor keeps them safe.