Order lifecycle
The AsseteraECS order state machine (place, fill, 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 are selling up front, and takers fill against it directly. Orders are 1-indexed
(getOrder(id) reads one; totalOrders() is the high-water mark).
There is no on-chain matching engine: a taker names the order id it wants to execute against. The
contract never scans the book for a cross, which is why the transitions below are all explicit calls.
The full behavioural spec is
FUNCTIONAL_SPEC.md
in the public contracts repo.
State machine
The on-chain OrderStatus enum is: None, Open, Filled, Settled, Cancelled, Refunded,
ForceCancelled, Expired. Settled and Refunded retain their numeric positions for storage and ABI
compatibility, but no current function writes those states. A newly placed order is Open; every reachable
terminal transition emits one lifecycle event.
Placing an order
placeOrder(sellToken, sellAmount, buyToken, buyAmount, expireTs, att, feeAtt) escrows the maker's
sell leg and any required maker fee. It requires a valid KYC attestation for the Place action and a
separate fee attestation. The fee terms and settlement currency are snapshotted onto the order and remain
immutable. 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.
Both fees are denominated in the market's settlement currency. The currency payer supplies the notional plus their fee, the currency receiver receives the notional less their fee, and the asset leg moves gross. The buy-side amount is ceiling-divided so the maker does not lose to rounding.
Operator settle and refund functions are not part of the current contract ABI. Orders execute through
fillOrder; self-cancel, expiry sweep, and the admin force-cancel path handle remaining escrow.
Cancelling
A maker cancels their own open order with cancelOrder(id) and reclaims the escrowed remaining quantity,
emitting OrderCancelled. The call takes no attestation: a user must always be able to cancel their own
open order and reclaim escrow, so cancellation is never gated on a compliance signature.
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.
Sweeping is a caller-triggered contract action. Integrators should run or use an operational sweeper if they want expired escrow returned promptly; the contract does not execute calls on its own.
Escape hatches
The admin-gated cancelOrderForUser(id, recipient) escape hatch force-cancels an open order and routes its
remaining escrow to the maker or another compliance-directed recipient. It emits OrderForceCancelled.