chore: add ohlcv websocket streaming#29739
Conversation
|
CLA Signature Action: All authors have signed the CLA. You may need to manually re-run the blocking PR check if it doesn't pass in a few minutes. |
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #29739 +/- ##
==========================================
+ Coverage 81.54% 81.68% +0.13%
==========================================
Files 5343 5389 +46
Lines 142128 143476 +1348
Branches 32411 32756 +345
==========================================
+ Hits 115899 117192 +1293
+ Misses 18299 18266 -33
- Partials 7930 8018 +88 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
| "@metamask/connectivity-controller": "^0.1.0", | ||
| "@metamask/controller-utils": "^11.18.0", | ||
| "@metamask/core-backend": "^6.2.0", | ||
| "@metamask/core-backend": "npm:@metamask-previews/core-backend@6.2.2-preview-094d56fb3", |
There was a problem hiding this comment.
TODO: to be removed after release
| }); | ||
|
|
||
| const wsInterval = WS_INTERVAL_BY_TIME_RANGE[timeRange]; | ||
| // TODO: Check if we want to add a feature flag to gate the WS OHLCV feature |
There was a problem hiding this comment.
Could be a followup
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
There are 2 total unresolved issues (including 1 from previous review).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit a24239b. Configure here.
🔍 Smart E2E Test Selection
click to see 🤖 AI reasoning detailsE2E Test Selection: Key findings:
The primary risk is that the Engine initialization with the new OHLCVService (using a preview package) could affect the WebSocket service infrastructure. SmokeWalletPlatform is the appropriate tag as it contains the WebSocket connection tests and covers the wallet platform where asset overview is displayed. Performance Test Selection: |
|
…MetaMask#8695) ## Explanation ### Architecture Overview ``` ┌─────────────────┐ messenger ┌──────────────────────────┐ │ OHLCVService │ ─── calls actions ────► │ BackendWebSocketService │ │ (domain logic) │ │ (raw WS connection) │ │ │ ◄── listens to events ── │ │ └────────┬────────┘ └──────────┬───────────────┘ │ │ publishes events actual WebSocket to UI consumers (connect, auth, reconnect, heartbeat, JSON framing) │ ▼ ┌──────────────────┐ │ Mobile UI │ │ (React hooks) │ │ useOHLCVRealtime │ └──────────────────┘ ``` ### What - Add `OHLCVService` for real-time OHLCV (candlestick) data streaming via the backend WebSocket gateway - Move all WebSocket-related files (`BackendWebSocketService`, `AccountActivityService`) into a new `src/ws/` directory per code review feedback ### Why - Enable real-time chart updates on the Token Details screen without polling - Reduce API load by replacing periodic HTTP calls with persistent WebSocket subscriptions - Organize WebSocket code into a dedicated `ws/` folder for better discoverability ### New files - `src/ws/ohlcv/OHLCVService.ts` — main service with subscribe/unsubscribe semantics, reference counting, grace-period unsubscribe, idempotency checks, chain-status forwarding, and automatic resubscription on reconnect - `src/ws/ohlcv/OHLCVService.test.ts` — 22 unit tests covering all paths (100% branch coverage) - `src/ws/ohlcv/OHLCVService-method-action-types.ts` — auto-generated messenger action types - `src/ws/ohlcv/types.ts` — `OHLCVBar` and `OHLCVSubscriptionOptions` types - `src/ws/ohlcv/index.ts` — barrel exports ### Modified files - `src/index.ts` — added exports for `OHLCVService`, its types, and allowed actions/events; updated import paths to `./ws/` - `eslint-suppressions.json` — updated paths for moved files, added suppressions for new test file - `CHANGELOG.md` — documented new service and exports ### Moved files (no logic changes) - `src/BackendWebSocketService.ts` → `src/ws/BackendWebSocketService.ts` - `src/BackendWebSocketService.test.ts` → `src/ws/BackendWebSocketService.test.ts` - `src/BackendWebSocketService-method-action-types.ts` → `src/ws/BackendWebSocketService-method-action-types.ts` - `src/AccountActivityService.ts` → `src/ws/AccountActivityService.ts` - `src/AccountActivityService.test.ts` → `src/ws/AccountActivityService.test.ts` - `src/AccountActivityService-method-action-types.ts` → `src/ws/AccountActivityService-method-action-types.ts` - Only import path updates (`./logger` → `../logger`, `./types` → `../types`, test helper paths) ### Key design decisions - **UI-driven lifecycle** — unlike `AccountActivityService` (auto-subscribes on account change), `OHLCVService` exposes `subscribe()`/`unsubscribe()` called by the UI when the chart mounts/unmounts - **Reference counting** — multiple UI consumers subscribing to the same assetId/interval/currency share one WebSocket subscription - **Grace period (3s)** — when all consumers unsubscribe, actual WS unsubscribe is delayed 3 seconds to absorb rapid navigation (Token A → Token B → Token A) - **Idempotency** — uses `channelHasSubscription` before subscribing; duplicate calls are no-ops (React Strict Mode safe) - **Chain status** — listens to `system-notifications.v1.market-data.v1` (auto-subscribed by server) and publishes `OHLCVService:chainStatusChanged` - **Disconnect handling** — on WebSocket disconnect, publishes `chainStatusChanged { status: 'down' }` for all tracked chains, triggering UI polling fallback - **Reconnect** — resubscribes all active channels when WebSocket reconnects (no `sessionId` needed for OHLCV; UI polling fallback covers the gap) - **`init()` method** — system notification callback registered in `init()` (not constructor) to comply with messenger-in-constructor lint rule ### Events published - `OHLCVService:barUpdated` — `{ channel, bar: OHLCVBar }` — new candle data from WebSocket - `OHLCVService:chainStatusChanged` — `{ chainIds, status, timestamp? }` — chain up/down (server notification or WS disconnect) - `OHLCVService:subscriptionError` — `{ channel, error, operation }` — subscribe or unsubscribe failure ## References * Related to https://www.notion.so/metamask-consensys/OHLCV-WebSocket-Integration-UI-Implementation-Guide-346f86d67d6880b6a70fc3be0f0c34b9 * Related to MetaMask/metamask-mobile#29739 * Fixes https://consensyssoftware.atlassian.net/browse/ASSETS-3195 ## Checklist - [ ] I've updated the test suite for new or updated code as appropriate - [ ] I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate - [ ] I've communicated my changes to consumers by [updating changelogs for packages I've changed](https://github.com/MetaMask/core/tree/main/docs/processes/updating-changelogs.md) - [ ] I've introduced [breaking changes](https://github.com/MetaMask/core/tree/main/docs/processes/breaking-changes.md) in this PR and have prepared draft pull requests for clients and consumer packages to resolve them <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Adds a new WebSocket-driven market-data service with reference counting, timers, and reconnect resubscription logic, which can affect subscription lifecycles and event delivery. Also moves existing WebSocket services into `src/ws/`, so consumers relying on internal paths (vs package exports) could break if any remain. > > **Overview** > Adds a new `OHLCVService` to stream real-time OHLCV bars over WebSocket, exposing `subscribe`/`unsubscribe` via messenger actions, publishing `barUpdated`/`chainStatusChanged`/`subscriptionError` events, and handling reconnect resubscription with ref-counting plus a grace-period unsubscribe (mutex-protected). > > Refactors `core-backend` by moving `BackendWebSocketService` and `AccountActivityService` (and their tests/action-type files) into `src/ws/`, updating imports/exports (`src/index.ts`), and updating lint suppressions; also adds `async-mutex` plus comprehensive unit tests for the new service and documents the addition in the changelog. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 730af62. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->




Description
Related to: https://www.notion.so/metamask-consensys/OHLCV-WebSocket-Integration-UI-Implementation-Guide-346f86d67d6880b6a70fc3be0f0c34b9
Wires
OHLCVServicefrom@metamask/core-backendinto the Engine and creates auseOHLCVRealtimehook that streams live candlestick updates to the advanced chart via the existingrealtimeBarprop onAdvancedChart.Why: The advanced chart currently only renders historical data fetched via the REST OHLCV API. Users see stale candles until they navigate away and back. Real-time streaming via WebSocket keeps the chart live with 5-second heartbeat updates.
How: Follows the exact same Engine wiring pattern as
AccountActivityService— messenger, init function, Engine registration. The newuseOHLCVRealtimehook subscribes toOHLCVService:barUpdatedevents, filters by channel, and converts the WS bar format (timestamp in Unix seconds) to the chart's expected format (time in milliseconds).Manual Test Plan
Prerequisites
backendWebSocketConnectionfeature flag enabledAdding console.log statements to the mobile hook
1. Inside
handleBarUpdated— after the channel guard:2. Inside
handleSubscriptionError— first line of the callback:3. Inside
handleChainStatusChanged— after thechainIds.includesguard:4. Inside
pollLatest— first line of the function:5. Inside the staleness
setInterval— whenisStale || chainDown:6. Inside the debounce
setTimeout— first line:7. In the cleanup
returnfunction — first line:Enabling core logs in the debugger
By default, core
OHLCVServicelogs useprojectLogger(thedebugpackage) and won't appear in the React Native debugger. To make them visible, open:Find this line (near the top, around line 30):
Replace with:
Now all core logs will appear in the debugger with the
[OHLCV-WS]prefix, alongside the mobile hook logs. Revert withyarn installwhen done.Group A — No Code Changes (Just Tap and Observe)
Scenario 1: Basic WebSocket Subscription
Steps:
Expected logs:
Verify: Bars continue arriving every ~5s with updating
closeprices.Scenario 2: Navigate Away (Unsubscribe + Grace Period)
Steps:
Expected logs:
Verify: No more bar updates after grace period expires.
Scenario 3: Rapid Navigation (Grace Period Cancel)
Steps:
Expected logs:
Verify:
Cancelled grace-period unsubscribe, bumped refCountappears — subscription was reused without a server roundtrip.Scenario 4: Switch Between Tokens
Steps:
Expected logs:
Verify: Token A fully unsubscribes (grace period expires). Token B gets its own subscription and bars flow.
Scenario 5: Rapid Time Range Switching
Steps:
Expected logs (showing one switch cycle: 15m → 1h):
This pattern repeats for each switch (1h → 1d → 1h → 15m → 1m). Each time, the old channel is flushed immediately before the new subscribe — no accumulation, no server rejections.
Verify: Every subscribe succeeds (
Subscribe succeeded).Flushing grace-period channelappears before each new subscribe. Bars flow on the final time range.Scenario 6: App Background / Foreground
Steps:
Expected logs:
Verify:
Resubscribing active channels after reconnect {count: 1}appears after foregrounding. Bars resume automatically without user interaction.Scenario 7: Unsupported Token (No OHLCV Data)
Steps:
Expected: No WS subscription, falls back to legacy line chart.
Group B — Requires Changing DEV Constants in
useOHLCVRealtime.tsScenario 8: WebSocket Disconnect → REST Polling Fallback
What this tests: The WebSocket connection drops and stays disconnected. After the staleness threshold (30s) is exceeded, the hook falls back to polling REST.
Code to add
In
useOHLCVRealtime.ts, set the DEV constant:The simulation code in the hook must call
disconnect(clean shutdown, notforceReconnection):How it works
After 10s, calls
BackendWebSocketService:disconnect(clean shutdown, no auto-reconnect). The WS stays dead. After 30s with no bars, staleness triggers REST polling every 15s.Steps to test
Expected logs:
Verify: After the simulated disconnect, no more
Bar receivedlogs. REST polling kicks in every 15s once staleness threshold (30s) is exceeded.Group C — Requires Editing
.cjsin node_modulesScenario 10: Subscribe Failure / Error Recovery
What this tests:
OHLCVService.subscribe()fails. The service catches the error, publishesOHLCVService:subscriptionError, forces reconnection, and REST fallback keeps the chart alive.Code to add
1. Disable dev simulation constant in
useOHLCVRealtime.ts:2. Simulate subscribe failure — open
node_modules/@metamask/core-backend/dist/ws/ohlcv/OHLCVService.cjs.Find the subscribe call (look for
BackendWebSocketService:subscribe) and comment it out, then add a throw:Steps to test
Expected — look for these key logs:
Verify: Error is caught, reconnection attempted (
Forcing WebSocket reconnection), and REST fallback keeps chart alive after staleness is detected.Log Reference
All logs use the
OHLCV-WSprefix. Filter byOHLCV-WSin Flipper / debugger.Changelog
CHANGELOG entry: Adds websocket streaming integration for ohlcv data
Related issues
Fixes: https://consensyssoftware.atlassian.net/browse/ASSETS-3194?atlOrigin=eyJpIjoiYmQ4N2E3MTlmZTFlNGYyNGFiODUxNzA2YThmM2FkYTkiLCJwIjoiaiJ9
Related: MetaMask/core#8695
Manual testing steps
Screenshots/Recordings
Before
After
Pre-merge author checklist
Performance checks (if applicable)
trace()for usage andaddTokenfor an exampleFor performance guidelines and tooling, see the Performance Guide.
Pre-merge reviewer checklist
Note
Medium Risk
Introduces a new Engine-wired
OHLCVServiceand a real-time data hook that subscribes/polls network resources; failures could impact chart updates and add background network load despite feature-flag gating.Overview
Adds a new Engine-integrated
OHLCVService(messenger + init wiring) and upgrades@metamask/core-backendto a preview build to support OHLCV WebSocket streaming.Updates
Price.advancedto optionally stream live candles intoAdvancedChartvia the existingrealtimeBarprop, gated by a new version-gated remote flag (tokenDetailsOhlcvWsIntegration) and chart readiness/threshold checks; header price/diff now prefers the latest streamed close when available.Introduces
useOHLCVRealtime(with debounce, channel filtering, cleanup/unsubscribe, and staleness/chain-down REST/latestpolling fallback) plus new unit tests and adjusted chart tests/mocks, and registers the new feature flag in CI constant resolution and the feature-flag registry.Reviewed by Cursor Bugbot for commit 1d8190f. Bugbot is set up for automated code reviews on this repo. Configure here.