Skip to content

The lifecycle of an intent spans its initial public broadcast, potential settlement, and any necessary cancellations.

An order moves from the owner's signature through an optional announcement to a window in which it is filled, cancelled, or expires. the owner's signed Order announce(order, auth) anyone — the owner or a relayer startTime is not enforced here optional · repeatable emits IntentAnnounced; dedupe on id open — fillable through endTime invalidateOwnerNonces the owner, on their own proxy clears nonce bits, not settler state fill(order, auth, …) anyone cancel(order) cancelSigned(order, …) endTime passes settledfilled[id] · nonce spent cancelledcancelled[id] on the settler expiredno fill is accepted
Announcement is a discovery step, not a state: the same order can be announced any number of times. Only the three transitions at the bottom are terminal. fill, cancel and cancelSigned write settler state; the backstop does not.
Step Function Who calls it?
Publish announce(order, auth) Anyone — the owner self-submits, or a relayer broadcasts.
Settle fill(order, auth, solver, routerData) Anyone.
Cancel (own gas) cancel(order) The owner directly.
Cancel (gasless) cancelSigned(order, deadline, ownerSig) Anyone broadcasts the owner’s explicit cancellation signature.
Cancel (backstop) UserProxyV6.invalidateOwnerNonces(...) The owner directly on their own proxy.

The announce function is the discovery feed. It is entirely optional and builds no on-chain index.

When called, it asks the owner’s proxy to verify the authorization. This serves as a free, pre-gas admission gate for relayers. However, a successful announcement does not guarantee the order is trustworthy or fillable:

  • The owner may not have an active proxy allowance.
  • An ERC-1271 smart contract wallet might change its configuration after signing.

Because of this, fillers must always dry-run the fill function to confirm the intent is executable.

announce also checks less than fill does. It rejects an order that is cancelled, expired or carries invalid authorization — but it does not enforce startTime. Announcing an order before its window opens is therefore valid and normal.

It is also not idempotent and does not consume the nonce: the same signed order can be announced any number of times, by anyone, emitting repeated IntentAnnounced logs with the same id. Indexers must deduplicate on id.

At or before startTime, only SystemFillerGate may fill at the curve floor. An outsider may still fill in that window by paying floor + 25 bps to the recipient. After startTime, everyone fills at the floor. The filler pays a 3 bps protocol fee on top of the applicable floor in all cases; requiredOut is this gross amount and the fee is sent to FeeVault.

The owner’s proxy consumes its nonce during a successful pull, and IntentSettlerV3 records filled[id]. Together these make a second fill of the same order impossible.

IntentSettlerV3 keeps cancelled[id] and filled[id] as persistent order state. Its EIP-1153 reentrancy flag is transient and stores nothing after the transaction.

An owner-proxy nonce can be spent by a fill or invalidated directly. Distinguishing between those cases requires the filled[id] state or a matching IntentFilled event log.

Users have three paths to cancel an order:

  1. On-chain cancellation (cancel): The owner pays their own gas to call cancel, recording the order hash in the cancelled mapping.
  2. Gasless cancellation (cancelSigned): The owner signs a cancellation message against the settler’s EIP-712 domain. A relayer broadcasts this signature to record the cancellation on-chain.
  3. The durable backstop: The user calls invalidateOwnerNonces directly on their own UserProxyV6. This does not depend on the settler. It clears nonce bits on that proxy, so it invalidates every owner order sharing those nonces; cancel and cancelSigned are the per-order settler paths.

The proxy allowance is a funding gate, not the order’s replay marker. The durable cancellation path is nonce invalidation on the owner’s proxy.

Cancellation transactions do not automatically front-run a fill that is already in the mempool. Whichever valid transaction lands first wins the race.