Expand description
WebSocket execution dispatch for the Hyperliquid execution client.
Implements the two-tier execution dispatch contract from
docs/developer_guide/adapters.md#tracked-and-external-execution-updates:
- The execution client registers an
OrderIdentityinWsDispatchStatewhen it submits an order, and refreshes the cached venue order id when a modify is sent so the WebSocket consumer can detect cancel-replace. - Incoming
OrderStatusReportandFillReportmessages are routed throughdispatch_order_eventanddispatch_order_fill. For tracked orders these build typedOrderEventAnyevents and emit them viaExecutionEventEmitter::send_order_event. For untracked / external orders the dispatch falls back to forwarding the raw report.
The dispatch state lives in an Arc<WsDispatchState> shared between the
main client task (which registers identities at submission time) and the
spawned WebSocket consumer task.
§GH-3827 cancel-replace handling
Hyperliquid implements modify as a cancel-and-replace: the venue emits an
ACCEPTED(new_voi) together with a CANCELED(old_voi) under the same
client_order_id. The dispatch detects the replacement leg by comparing
report.venue_order_id to the last cached value, promotes it to an
OrderUpdated event, and suppresses the stale cancel so strategies never
observe a spurious termination.
Each in-flight modify is tracked as an intent in a per-order chain (keyed
on client_order_id), pushed by modify_order before the HTTP call. An
intent lets dispatch skip an early CANCELED(old_voi) that arrives before
the replacement ACCEPTED(new_voi), regardless of whether the WS message
races ahead of the HTTP response. Rapid repeated modifies under one stable
CLOID queue as a chain so a later modify cannot overwrite an earlier
intent’s old-leg suppression, and a failed modify clears only its own
generation (leaving newer intents intact). The front intent is claimed on
promotion, advancing the next intent’s old leg to the promoted replacement;
a rejected front reparents the next intent to the same still-live leg.
A fill carrying the replacement venue_order_id during an in-flight modify
promotes the binding directly (the same OrderUpdated path as the
replacement ACCEPTED), so a dropped ACCEPTED does not strand the fill.
A fill is buffered into WsDispatchState::buffered_fills only when the
identity has no price to promote with; handle_accepted drains the buffer
on the replacement ACCEPTED. A delayed earlier-leg fill during a chained
modify is a known limitation. See GH-3972.
When neither the replacement ACCEPTED nor a fill arrives, a query that
resolves the replacement by cloid promotes the binding the same way via
promote_replacement_from_query, so a dropped ACCEPTED with no fill
cannot leave the order bound to the canceled leg.
Structs§
- Bounded
Dedup - Bounded FIFO deduplication set.
- Modify
Intent - A single in-flight Hyperliquid modify awaiting its replacement leg.
- Order
Identity - Identity metadata captured when an order is submitted through this client.
- WsDispatch
State - Per-client dispatch state shared between order submission and the WebSocket consumer task.
Enums§
- Dispatch
Outcome - Outcome of a single dispatch call.
Constants§
- DEDUP_
CAPACITY - MAX_
PENDING_ MODIFY_ INTENTS - Maximum in-flight modify intents tracked per order. Rapid repricing rarely queues more than one or two unacknowledged modifies at once; the cap bounds memory if replacement acks stall. On overflow the oldest intent is evicted.
Functions§
- dispatch_
order_ event - Dispatches an
OrderStatusReportusing the two-tier routing contract. - dispatch_
order_ fill - Dispatches a
FillReportusing the two-tier routing contract. - promote_
replacement_ from_ query - Promotes a cancel-replace replacement surfaced by a query during an in-flight modify.