Conversation
Add comprehensive WebSocket functionality to the Hyperliquid mock server: WebSocket Features: - Real-time order updates subscription (orderUpdates) - Best bid/offer market data subscription (l2Book/bbo) - Automatic broadcasting on order state changes - Multiple subscriptions per connection - Thread-safe concurrent operations Implementation: - New WebSocketManager for connection and subscription handling - Integration with existing State for automatic order update broadcasts - WebSocket endpoint at /ws - Support for subscribe/unsubscribe operations Test Utilities: - TestServer.WebSocketURL() for easy test connections - TestServer.SetBBO() for manual market data updates - TestServer.TriggerBBOUpdate() for forcing BBO broadcasts - Example tests demonstrating WebSocket usage Documentation: - Comprehensive WEBSOCKET.md guide - Updated README.md with WebSocket information - Example code for order updates and BBO subscriptions Mock Data: - BTC: bid=86956.5, ask=87043.5 (centered on 87000 USDT) - ETH: bid=2999.5, ask=3000.5 - SOL: bid=99.9, ask=100.1 Compliance: - Follows official Hyperliquid WebSocket API specifications - Compatible with upstream API docs
Go example functions must be niladic (no parameters), but these needed *testing.T for proper test infrastructure. Renamed from Example* to Test* to fix build errors: - ExampleWebSocketOrderUpdates -> TestWebSocketOrderUpdates - ExampleWebSocketBBO -> TestWebSocketBBO - ExampleManualBBOUpdate -> TestManualBBOUpdate Fixes build error: 'Example functions should be niladic'
The request capture middleware was interfering with WebSocket handshakes by trying to read the request body. WebSocket upgrades use HTTP headers for the handshake, not the request body, so we now skip body capture when the 'Upgrade: websocket' header is present. Fixes: websocket: bad handshake error in tests
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| logger.Info("Mock Hyperliquid API server listening", "addr", addr) | ||
| logger.Info("Endpoints", "exchange", addr+"/exchange", "info", addr+"/info", "health", addr+"/health") | ||
| logger.Info("Endpoints", | ||
| "exchange", addr+"/exchange", | ||
| "info", addr+"/info", | ||
| "health", addr+"/health", | ||
| "websocket", "ws://"+addr[7:]+"/ws") |
There was a problem hiding this comment.
Avoid panic when logging WebSocket endpoint
Run assumes the supplied server address is prefixed with http:// and slices addr[7:] when logging the WebSocket URL. The main entry point passes ":8080", so the slice indexes past the end of the string and the server panics before listening. Format the WebSocket URL without assuming a protocol so the process can start.
Useful? React with 👍 / 👎.
| s.orders[cloid] = order | ||
|
|
||
| // Broadcast order update via WebSocket | ||
| if s.wsm != nil { | ||
| // Note: In a real system, we'd track which user owns which order | ||
| // For the mock, we use a placeholder user address | ||
| s.wsm.BroadcastOrderUpdate("", order) | ||
| } |
There was a problem hiding this comment.
Order update broadcasts never reach subscribers
Every order state mutation enqueues BroadcastOrderUpdate("", order), but the broadcaster forwards updates only when a connection’s subscribed user matches update.User. Because handleSubscribe requires a concrete user string, any real subscription will never match the empty user used here, so orderUpdates messages are silently dropped. The handler must propagate the actual user address (or broadcast without filtering) for the new WebSocket feature to work.
Useful? React with 👍 / 👎.
The Run function receives addresses like ':8080' without a protocol prefix, but the WebSocket URL logging was assuming 'http://' and slicing addr[7:], causing an index out of bounds panic. Now properly handles addresses starting with ':' by prepending 'localhost' when formatting the WebSocket URL for logging. Fixes panic on server startup when address is ':8080'
Critical bug fix: Order updates were being broadcast with an empty user string but subscribers require a specific user address. The user filtering check 'if state.orderUpdatesUser == update.User' always failed, causing all orderUpdates messages to be silently dropped. Changes: - Removed user parameter from BroadcastOrderUpdate() - Changed broadcast logic to send to all orderUpdates subscribers - Simplified OrderUpdate struct (removed User field) - Updated all call sites in state.go and testserver.go For a mock server, broadcasting to all subscribers is appropriate since we don't track actual user ownership of orders. Subscribers still need to provide a user address when subscribing (per Hyperliquid protocol), but the mock broadcasts all order changes to everyone subscribed to orderUpdates. This makes the WebSocket orderUpdates feature actually work.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| msg := map[string]interface{}{ | ||
| "channel": "l2Book", | ||
| "data": map[string]interface{}{ | ||
| "coin": coin, | ||
| "time": time.Now().UnixMilli(), |
There was a problem hiding this comment.
Send BBO subscriptions in correct channel format
Clients subscribing with {"type": "bbo"} receive messages built by sendInitialBBO/broadcastL2Book that are tagged as channel: "l2Book" and contain a levels array. According to upstream-api-docs/subscriptions.md, the BBO feed should use channel: "bbo" and a payload shaped like WsBbo { coin, time, bbo: [bid, ask] }. As implemented, any consumer expecting the documented BBO schema will fail to parse the messages. Consider emitting a distinct bbo channel with a bbo field or otherwise matching the documented type.
Useful? React with 👍 / 👎.
BBO and l2Book subscriptions now send different message formats: l2Book subscriptions receive: - channel: "l2Book" - data.levels: [[bids...], [asks...]] bbo subscriptions receive: - channel: "bbo" - data.bbo: [bid, ask] Changes: - Track l2Book and bbo subscriptions separately in ConnectionState - Split sendInitialBBO into sendInitialL2Book and sendInitialBBO - Updated broadcastL2Book to send different formats based on subscription - Consumers subscribing to bbo now receive the correct WsBbo format This matches the upstream API docs (upstream-api-docs/subscriptions.md)
|
@codex review |
|
Codex Review: Didn't find any major issues. Swish! ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Add comprehensive WebSocket functionality to the Hyperliquid mock server:
WebSocket Features:
Implementation:
Test Utilities:
Documentation:
Mock Data:
Compliance: