Skip to content

fix(rtds): serialize crypto_prices filters as comma-separated string (closes #270)#341

Open
HxrmxStudio wants to merge 1 commit intoPolymarket:mainfrom
HxrmxStudio:fix/crypto-prices-filter-serialization
Open

fix(rtds): serialize crypto_prices filters as comma-separated string (closes #270)#341
HxrmxStudio wants to merge 1 commit intoPolymarket:mainfrom
HxrmxStudio:fix/crypto-prices-filter-serialization

Conversation

@HxrmxStudio
Copy link
Copy Markdown

@HxrmxStudio HxrmxStudio commented Apr 21, 2026

Summary

Subscription::crypto_prices serializes its filters field as a JSON array (["btcusdt","ethusdt"]), but the Polymarket RTDS server expects a comma-separated plain string ("btcusdt,ethusdt") per the published schema. The server silently rejects the JSON-array body with {\"message\":\"Invalid request body\"} while leaving the WebSocket connection open — consumers see a live stream that never forwards any ticks, with no visible error anywhere in the SDK path.

This matches the bug described in #270. The reporter closed that issue without a fix merged, so v0.4.4 still ships the broken serialization. This PR adopts the reporter's one-liner recommendation.

Repro (observed in production)

Downstream consumer of polymarket-client-sdk = \"0.4.4\", subscribed via:

```rust
client.subscribe_crypto_prices(Some(vec!["btcusdt".into(), "ethusdt".into(), "solusdt".into(), "xrpusdt".into(), "dogeusdt".into()]))
```

Outcome: binance_ticks_forwarded counter stayed at 0 across ≥6 hours while chainlink_ticks_forwarded (on the same Client) accumulated at a healthy ~4.95 tps. No RTDS stream error warnings, no stream ENDED log line — the stream simply never yielded any items.

Passing None instead (no server-side filter) resumes tick flow immediately, confirming the filter shape is the root cause.

Fix

Replace the JSON array encoding with a join(\",\"):

```rust

  • let filters =
  •    symbols.map(|s| serde_json::to_string(&s).unwrap_or_else(|_| \"[]\".to_owned()));
    
  • let filters = symbols.map(|s| s.join(","));
    ```

The existing custom Serialize impl already handles plain-string filters correctly via the fallback branch (the JSON re-parse fails for a comma-separated string and falls through to map.serialize_entry(\"filters\", filters)?, which emits the string value as-is). No changes needed there.

The two unit tests that encoded the old (broken) expected shape are updated to match the documented wire format:

  • serialize_subscription_request — asserts \"filters\":\"btcusdt,ethusdt\"
  • serialize_mixed_subscriptions — same assertion in the multi-topic case

Chainlink

crypto_prices_chainlink is unaffected. Its filter shape (\"{\\\"symbol\\\":\\\"btc/usd\\\"}\") is correct per #136 and remains serialized as an escaped JSON string by the existing Chainlink branch in the Serialize impl.

Test plan

  • `cargo test --lib --features rtds` → 36 passed / 0 failed
  • Updated assertions match the RTDS schema example in the official docs
  • crypto_prices_chainlink serialization unchanged (verified by serialize_chainlink_subscription passing)
  • Maintainer to verify against a live RTDS server

Closes #270.


Note

Low Risk
Low risk wire-format fix limited to RTDS Binance crypto_prices subscription serialization; main risk is behavior change for any consumers relying on the previous (incorrect) JSON-array encoding.

Overview
Fixes RTDS Binance crypto_prices subscriptions to serialize filters as a comma-separated string (e.g. "btcusdt,ethusdt") instead of a JSON array, matching the published RTDS schema and preventing silent server rejection/no-tick streams.

Updates unit tests to assert the new on-the-wire format (including mixed Chainlink+Binance requests) while keeping Chainlink filter serialization unchanged.

Reviewed by Cursor Bugbot for commit 223716d. Bugbot is set up for automated code reviews on this repo. Configure here.

The RTDS server expects `crypto_prices` subscription filters as a
comma-separated plain string per the published schema:

  https://docs.polymarket.com/market-data/websocket/rtds

    "filters": "solusdt,btcusdt,ethusdt"

`Subscription::crypto_prices` was serializing the filter list via
`serde_json::to_string`, which produced a JSON array on the wire:

    "filters": ["btcusdt","ethusdt"]

The server silently rejects that shape with
`{"message":"Invalid request body"}` while leaving the WebSocket
connection open and healthy at the transport layer. Consumers see
a stream that never forwards any ticks — no visible error
anywhere in the SDK path, but the `crypto_prices` topic is
effectively dead for the lifetime of the subscription.

Originally reported in Polymarket#270 with the exact wire-format diagnosis;
closed by the reporter without a fix merged. This patch adopts
the reporter's recommended one-liner (`symbols.join(",")`) and
updates the two unit tests that encoded the incorrect expected
shape (`serialize_subscription_request` and
`serialize_mixed_subscriptions`).

`crypto_prices_chainlink` is unaffected — its filter shape
(`"{\"symbol\":\"btc/usd\"}"`) is correct per Polymarket#136 and already
serialized as an escaped JSON string.

All 36 library tests pass.
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 21, 2026

Codecov Report

❌ Patch coverage is 66.66667% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 85.52%. Comparing base (77264a4) to head (223716d).

Files with missing lines Patch % Lines
src/rtds/types/request.rs 66.66% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #341      +/-   ##
==========================================
- Coverage   85.54%   85.52%   -0.02%     
==========================================
  Files          32       32              
  Lines        5167     5168       +1     
==========================================
  Hits         4420     4420              
- Misses        747      748       +1     
Flag Coverage Δ
rust 85.52% <66.66%> (-0.02%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

RTDS crypto_prices subscription sends filters as JSON array instead of comma-separated string

1 participant