T09 · Insecure Skill Coding Practices
Error
- Location
- references/hyperliquid_patch.py:116
- Finding
- Incomplete exchange response handling can desynchronize local and live trading state<![CDATA[ ## Vulnerability Details **File Location**: `references/hyperliquid_patch.py`, lines 116-145 **Vulnerability Type**: Incomplete order and fill state reconciliation **Risk Level**: High ### Vulnerable Code ```python result = exchange.order( name=symbol, is_buy=is_buy, sz=size, limit_px=float(limit_price), order_type=order_type, reduce_only=order.is_reduce_only, ) if result.get("status") == "ok": statuses = result.get("response", {}).get("data", {}).get("statuses", []) if statuses: s = statuses[0] if "error" in s: raise ValueError(s["error"]) venue_id = str(s.get("resting", s.get("filled", {})).get("oid", order.client_order_id)) else: venue_id = str(order.client_order_id) self.generate_order_accepted( strategy_id=order.strategy_id, instrument_id=order.instrument_id, client_order_id=order.client_order_id, venue_order_id=VenueOrderId(venue_id), ts_event=self._clock.timestamp_ns(), ) else: raise ValueError(str(result)) ``` ### Technical Analysis The patch bypasses the normal NautilusTrader Hyperliquid order-submission implementation and directly calls the Hyperliquid SDK. However, it treats both resting and immediately filled orders as merely accepted. When the exchange returns a `filled` response, the code extracts only the order identifier and calls `generate_order_accepted`. It does not generate an order-filled event or record the executed quantity, execution price, fees, remaining quantity, or resulting position. Consequently, NautilusTrader's cache, execution engine, risk engine, and strategy can retain state that differs from the actual exchange account. The project documentation also states that the patch has no position synchronization on reconnect. This increases the likelihood that state divergence will persist after an interruption. ### Attack Path 1. A live strategy submits a market or marketable limit or ...[truncated 1321 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Implement complete translation of every Hyperliquid SDK response into the appropriate NautilusTrader lifecycle events: - Order accepted. - Order rejected. - Order filled or partially filled. - Order canceled or expired. 2. For fills, propagate the actual venue order ID, trade ID, executed quantity, execution price, commission, liquidity side, and event timestamp. 3. Do not treat an empty or unrecognized status list as successful acceptance. Reject the local command or mark it unresolved until reconciliation completes. 4. Query the exchange after ambiguous responses and reconcile the order using its client or venue order ID. 5. Add startup and reconnect reconciliation for open orders, fills, balances, and positions before permitting new submissions. 6. Block new orders when local and exchange state cannot be reconciled. 7. Add unit and integration tests covering resting, filled, partially filled, rejected, malformed, timed-out, and duplicate-response cases. 8. Avoid replacing a private adapter method unless the replacement preserves the adapter's complete execution-state contract. ]]>
