Fill an order
A solver fills an intent by calling IntentSettlerV1.fill with the signed
order, its Permit2 witness signature, a solver address, and opaque routerData.
The solver address must implement IIntentFiller.
The code below is illustrative. It shows the protocol boundary and call order; it is not a working routing implementation.
1. Screen with IntentLens
Section titled “1. Screen with IntentLens”Call IntentLens.preview(order) or batch orders with previewMany. The result
is an observation stamped with observedAt and observedBlock:
| Field | Meaning |
|---|---|
id |
keccak256(abi.encode(order)) |
cancelled |
cancelled[id] on the settler |
nonceSpent |
Permit2’s unordered nonce bit |
inWindow |
startTime <= now <= endTime |
decayComplete |
now >= decayEndTime |
floorNow |
required output at the observation time; 0 outside the window |
ownerBalance |
owner’s tokenIn balance |
ownerPermit2Allowance |
owner’s tokenIn allowance to Permit2 |
observedAt / observedBlock |
timestamp and block for the observation |
IntentLens deliberately has no fillable boolean. Balance, allowance,
nonce state, time, solver inventory, and venue liquidity can change before a
transaction is included. Treat the lens as screening data, not a verdict.
In particular, nonceSpent does not mean filled: Permit2 sets the same bit
when the owner invalidates the nonce. A matching IntentFilled event is needed
to distinguish those cases.
2. Implement the filler callback
Section titled “2. Implement the filler callback”The settler transfers order.amountIn of order.tokenIn to solver, then
calls this callback. The callback must return at least requiredOut of
order.tokenOut to msg.sender, which is the settler.
// Illustrative interface boundary from IIntentFiller.sol.interface IIntentFiller { function fillIntentCallback( IntentOrder calldata order, uint256 requiredOut, bytes calldata data ) external;}An implementation uses data as its routing input and must return the output
token to the callback caller before returning:
// Illustrative pseudocode. The venue route is solver-specific.function fillIntentCallback( IntentOrder calldata order, uint256 requiredOut, bytes calldata data) external { // Use order.amountIn of order.tokenIn and data to execute a route. // Transfer at least requiredOut of order.tokenOut to msg.sender.}Returning exactly requiredOut is the expected behavior. The solver keeps its
margin by not sending it to the settler. Any amount above the floor that does
arrive is delivered to order.recipient; it is not refunded to the solver.
3. Dry-run the exact fill
Section titled “3. Dry-run the exact fill”A valid announcement is only signature admission. Before spending gas, execute
the exact fill(order, permit2Sig, solver, routerData) as an eth_call at the
current chain state:
// Illustrative RPC shape; use your ABI encoder and provider.const result = await provider.request({ method: 'eth_call', params: [{ to: intentSettler, data: encodeFillCall(order, permit2Sig, solver, routerData), }, 'latest'],});The dry-run should cover the current time window, Permit2 signature and nonce,
owner balance and allowance, callback behavior, route liquidity, and exact
recipient delivery. A successful observation from IntentLens cannot replace
this call.
4. Submit the fill
Section titled “4. Submit the fill”If the dry-run succeeds, submit the same calldata as a transaction. fill is
permissionless; the transaction caller need not be the order owner. The settler
then validates the order, computes the current required output, pulls the input
through Permit2, invokes the callback, measures receivedOut, and transfers
everything received to the signed recipient.
The transaction reverts if the order is cancelled, outside its time window, or has a zero solver; if Permit2 cannot pull the exact input; if the callback returns less than the required floor; if recipient delivery is inexact; or if the settler’s token balances are not conserved. See settlement execution and the ABI reference.