Rewrite ws_client.py against the documented WebSocket API#153
Open
devin-ai-integration[bot] wants to merge 3 commits into
Open
Rewrite ws_client.py against the documented WebSocket API#153devin-ai-integration[bot] wants to merge 3 commits into
devin-ai-integration[bot] wants to merge 3 commits into
Conversation
Replaces the previous order-book/account-only client with a full implementation of the channels documented at https://apidocs.lighter.xyz/docs/websocket-reference. The new WsClient supports every documented channel (order book, ticker, market_stats, spot_market_stats, trade, candle, all account_* streams, user_stats, notification, pool_data, pool_info, height) plus jsonapi/sendtx and jsonapi/sendtxbatch transaction submission. It keeps the existing public surface used in examples/ws.py and examples/ws_async.py (order_book_ids, account_ids, on_order_book_update, on_account_update, run(), run_async()) and adds add_* helpers, async subscribe/unsubscribe, send_tx/send_tx_batch, readonly mode, ping/pong handling, configurable WebSocket keepalive, and optional auto-reconnect.
Contributor
Author
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
Replaces the 20+ on_X_update constructor params and add_X helper methods with one uniform subscribe(channel, on_update=..., auth=...) entry point. Dispatch looks up the registered subscription by canonical channel name (channels are normalized so callers can pass either ':' or '/' separators). Order book snapshot+diff reconstruction is still performed automatically for any 'order_book/*' subscription and exposed via client.order_book_states[market_id]. Other changes: - subscribe/unsubscribe are sync registration; if called while connected they also send the subscribe/unsubscribe frame via the running loop. - order_book_ids / account_ids constructor shortcuts and the per-channel on_*_update callbacks are removed. examples/ws.py and examples/ws_async.py are rewritten against the new API. - on_message is now the fallback for the 'connected' welcome and any unmatched channels. on_tx_response fires for every 'jsonapi/*' reply.
Introduces lighter/ws_messages.py with one pydantic envelope per documented server message type (WSOrderBookUpdate, WSTradeUpdate, WSCandleUpdate, WSAccountAllUpdate, etc., plus WSTxResponse for jsonapi/* replies). Every envelope uses ConfigDict(extra='allow') and treats channel-specific fields as Optional, so new server fields land in model_extra and missing fields surface as None - schema drift never causes parsing to raise. WsClient.subscribe gains a parse=False flag. When True, the dispatcher runs the message through ws_messages.parse_ws_message before invoking on_update, so callbacks receive a typed envelope instead of a dict. on_message and on_tx_response still receive the raw dict. ws_messages also exposes envelope_for(message_type) and a standalone parse_ws_message(message) helper for users who want to validate messages outside the run loop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Rewrites
lighter/ws_client.pyagainst the channels documented at https://apidocs.lighter.xyz/docs/websocket-reference. The previous client only handledorder_bookandaccount_all; the new one covers every documented channel, adds first-class transaction submission over the socket, exposes a single uniform subscription API, and ships opt-in typed payload envelopes.Unified
subscribe()API (breaking)The 20+ per-channel constructor params and
on_*_updatecallbacks are gone. All channels go through one method:dict) and may be sync or async.account_market/,account_tx/,account_all_orders/,account_orders/,account_all_assets/,account_spot_avg_entry_prices/,notification/,pool_data/,pool_info/). Per-subscription tokens override the client default.unsubscribe(channel)works at runtime;subscribe()called afterrun()dispatches the subscribe frame on the live connection.order_book/*snapshots and diffs are still merged intoclient.order_book_states[market_id]for callers who want a maintained book.Opt-in typed envelopes (
lighter/ws_messages.py)A new module exposes one pydantic envelope per documented server message type (
WSOrderBookUpdate,WSTradeUpdate,WSCandleUpdate,WSAccountAllUpdate,WSTxResponse, …). Every envelope usesConfigDict(extra="allow")and treats channel-specific fields asOptional, so:model_extraand do not raise.Nonerather than breaking parsing.parse_ws_message(raw_dict)andenvelope_for(message_type)are also exported for use outside the client (replay tooling, tests). Unknown message types fall through and return the raw dict, so adding new server channels never makes typed mode fatal.Other changes
send_tx/send_tx_batchwrapjsonapi/sendtx/jsonapi/sendtxbatch. The batch encoding matches the existingexamples/utils.py:ws_send_batch_txwire format (tx_infosas a JSON-encoded list of JSON-encoded strings).?readonly=truequery option, configurable WebSocket-levelping_interval/ping_timeout(default 30s/60s to stay below the server's 2-minute idle timeout), application-levelping→pongreply.auto_reconnectwithreconnect_delay; registered subscriptions are re-sent on each reconnect.close()for graceful shutdown.:(server) and/(subscribe) separators and normalizes to/internally.Breaking API changes (vs. previous
ws_client.py)order_book_ids,account_ids,on_order_book_update,on_account_updateconstructor params.on_*_updatestyle callbacks; everything ison_update(message)on eachsubscribe.client.subscriptionsis now adict[channel_str, Optional[auth_token]](was{"order_books": [...], "accounts": [...]}).examples/ws.pyandexamples/ws_async.pyhave been rewritten against the new API.Review & Testing Checklist for Human
examples/ws.pyandexamples/ws_async.py: confirm both order book and account streams emit.account_tx/<account_id>ornotification/<account_id>) with a real token and confirm the server returnssubscribed/...rather than an auth error. The auth-required prefix list inws_client.pywas assembled from the docs and has not been validated against server behavior.send_txandsend_tx_batchwith real signed payloads — the batch form preserves the double-JSON-encoded shape fromexamples/utils.py, which the docs describe ambiguously.parse=Truefor a couple of channels you care about — the envelopes were hand-mirrored from the docs, so any field-name drift between docs and live server payloads will surface asNone(silent) rather than as a validation error. If you'd rather have loud failures, swap a subclass toConfigDict(extra="forbid")and removeOptional.order_book_ids/account_idsconstructor params, the oldon_*_updatesignatures, or the previousclient.subscriptionsshape.Notes
on_messageis the fallthrough for any server message that doesn't match a registered subscription (including theconnectedwelcome).on_tx_responsefires for everyjsonapi/*reply (success or error). Both still receive raw dicts; onlysubscribe(..., parse=True)triggers envelope parsing.try/except ImportErrorforwebsockets.asyncio.clientvs.websockets.clientmatches the same pattern inlighter/paper_client/live.py.Link to Devin session: https://app.devin.ai/sessions/5169092926f142a5bfec4b97315b935c
Requested by: @lavrric