Skip to main content

Module dispatch

Module dispatch 

Source
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:

  1. The execution client registers an OrderIdentity in WsDispatchState when it submits an order, and refreshes the cached venue order id when a modify is sent so the WebSocket consumer can detect cancel-replace.
  2. Incoming OrderStatusReport and FillReport messages are routed through dispatch_order_event and dispatch_order_fill. For tracked orders these build typed OrderEventAny events and emit them via ExecutionEventEmitter::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§

BoundedDedup
Bounded FIFO deduplication set.
ModifyIntent
A single in-flight Hyperliquid modify awaiting its replacement leg.
OrderIdentity
Identity metadata captured when an order is submitted through this client.
WsDispatchState
Per-client dispatch state shared between order submission and the WebSocket consumer task.

Enums§

DispatchOutcome
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 OrderStatusReport using the two-tier routing contract.
dispatch_order_fill
Dispatches a FillReport using the two-tier routing contract.
promote_replacement_from_query
Promotes a cancel-replace replacement surfaced by a query during an in-flight modify.