Order lifecycle
The AsseteraExchange order state machine (place, fill, settle, cancel and expire), and what each step escrows and attests.
An order is a maker's standing intent to trade one token for another. The maker escrows the
token they're selling up front; takers fill against it, and the trade finalizes either by a
counterparty fill or by an operator settling two complementary orders. Orders are 1-indexed
(getOrder(id) reads one; totalOrders() is the high-water mark).
State machine
The on-chain OrderStatus enum is: None, Open, Filled, Settled, Cancelled, Refunded,
ForceCancelled, Expired. A newly placed order is Open; every terminal transition emits exactly
one lifecycle event.
Placing an order
placeOrder(sellToken, sellAmount, buyToken, buyAmount, expireTs, att) escrows sellAmount of
sellToken and records the desired buyToken / buyAmount. It requires a valid KYC attestation for
the Place action and snapshots the fee terms (makerFeeBps, takerFeeBps, feeCollector) onto
the order, immutable thereafter. expireTs of 0 means the order never expires. It emits
OrderPlaced and returns the new order id.
placeOrderWithPermit(...) is the same call with an ERC-2612 permit attempted first (best-effort,
swallowed on failure) so the maker can approve and place in one transaction.
Filling an order
fillOrder(id, fillSellAmount, att) lets a taker trade against an open order, requiring a KYC
attestation for the Fill action. Fills can be partial or full:
- Partial (order still has remaining quantity afterwards) emits
OrderPartiallyFilledand the order staysOpen. - Full (remaining quantity reaches zero) emits
OrderFilledand the order becomesFilled.
Fees are deducted from the gross amounts before each party receives funds: the maker fee comes out of
the maker's buy-side receipt, the taker fee out of the taker's sell-side receipt, both routed to the
allowlisted feeCollector. The buy-side amount is ceiling-divided so the maker never loses to
rounding.
Settling two orders
settle(buyId, sellId, attBuy, attSell) is an operator action (OPERATOR_ROLE) that matches two
complementary orders and moves both to Settled, zeroing both remaining quantities. It requires
a fresh KYC attestation from both makers for the Settle action: the operator cannot settle on
a maker's behalf without their signed consent. It emits OrderSettled with the settled amounts and
both fee legs.
Roadmap: on-chain operator settlement is being parked; standard operations will not settle on
chain. settle, settleOffer and refund are moving out of the production standard flow (kept
for possible re-enable via upgrade). Treat operator settlement as present-in-testnet but not part
of the production baseline.
Cancelling
The maker can cancel their own open order and reclaim the escrowed remaining quantity, emitting
OrderCancelled. On the current testnet there are two paths: cancelOrder(id, att) (KYC-gated) and
cancelOrderSelf(id) (no attestation). They emit the same event and can only be distinguished by
whether a KycConsumed event appears in the same transaction.
Roadmap: these two cancel paths are being consolidated into a single always-unattested
cancelOrder(id): a user must always be able to cancel their own open order and reclaim escrow
without needing an attestation.
Expiry and sweeping
An order past its expireTs is dead but its escrow is still locked until swept.
sweepExpired(ids[]) is permissionless and batched (≤100 ids). It refunds each expired order's
remaining quantity to the rightful maker (not the caller), silently skipping ineligible ids, and
emits one OrderExpired per swept id. Anyone can sweep a user's expired orders on their behalf.
Roadmap: automatic sweeping (so escrow is auto-returned without a manual call) is planned.
Escape hatches
Two admin-gated exits move escrow when a maker can't act for themselves: refund(id, reason)
returns remaining escrow to the maker (OrderRefunded), and cancelOrderForUser(id, recipient)
force-cancels an open order to a compliance-directed recipient (OrderForceCancelled). Together with
the sweeps, these are the only ways escrow leaves the contract without a matching fill or settlement.