chore: add OHLCVService for real-time candlestick WebSocket streaming#8695
chore: add OHLCVService for real-time candlestick WebSocket streaming#8695sahar-fehri wants to merge 29 commits into
Conversation
2d69c0f to
bd0d4d2
Compare
|
@metamaskbot publish-preview |
|
Preview builds have been published. Learn how to use preview builds in other projects. Expand for full list of packages and versions. |
|
@metamaskbot publish-preview |
|
Preview builds have been published. Learn how to use preview builds in other projects. Expand for full list of packages and versions. |
|
@metamaskbot publish-preview |
|
Preview builds have been published. Learn how to use preview builds in other projects. Expand for full list of packages and versions. |
|
@metamaskbot publish-preview |
|
Preview builds have been published. Learn how to use preview builds in other projects. Expand for full list of packages and versions. |
|
@metamaskbot publish-preview |
|
Preview builds have been published. Learn how to use preview builds in other projects. Expand for full list of packages and versions. |
|
@metamaskbot publish-preview |
|
Preview builds have been published. Learn how to use preview builds in other projects. Expand for full list of packages and versions. |
| channel, | ||
| refCount: entry.refCount, | ||
| }); | ||
| return; |
There was a problem hiding this comment.
When trying to create a new subscription and that entry has a running gracePeriodTimer, it means refCount just hit 0 and the channel is waiting to be unsubscribed but the WebSocket subscription is still alive.
In that case; cancel the timer, clear it, bump refCount back to 1, and return early; no need to call connect or subscribe on the WebSocket since the subscription never actually got torn down
| if (entry && entry.refCount > 0) { | ||
| entry.refCount += 1; | ||
| return; | ||
| } |
There was a problem hiding this comment.
If the channel entry exists and refCount > 0, it means there's already an active subscription with at least one consumer. So it just bumps the count and returns=> no connect, no subscribe, no WebSocket traffic at all. Multiple UI components share the same underlying WebSocket subscription.
|
|
||
| export type OHLCVServiceActions = OHLCVServiceMethodActions; | ||
|
|
||
| export const OHLCV_SERVICE_ALLOWED_ACTIONS = [ |
There was a problem hiding this comment.
Oh interesting, so we rely on the BackendWebSocketService to add support for this? Nice.
There was a problem hiding this comment.
Yes BackendWebSocketService is the shared infra that is handling the WS connection lifecycle
Actions OHLCVService calls on BackendWebSocketService
BackendWebSocketService:connect — ensure WS is open
BackendWebSocketService:subscribe — create server-side subscription
BackendWebSocketService:channelHasSubscription — idempotency check
BackendWebSocketService:getSubscriptionsByChannel — for unsubscribing
BackendWebSocketService:forceReconnection — tear down + rebuild on errors
BackendWebSocketService:addChannelCallback — register system-notification listener
BackendWebSocketService:removeChannelCallback — cleanup on destroy
Kriys94
left a comment
There was a problem hiding this comment.
Overall, LGTM. I have a very small question about the reliability of ChannelEntry and its counter.
| // ============================================================================= | ||
|
|
||
| type ChannelEntry = { | ||
| refCount: number; |
|
@metamaskbot publish-preview |
|
Preview builds have been published. Learn how to use preview builds in other projects. Expand for full list of packages and versions. |
| */ | ||
| async subscribe(options: OHLCVSubscriptionOptions): Promise<void> { | ||
| const channel = this.#buildChannel(options); | ||
| return this.#withChannelLock(channel, () => this.#subscribeInner(channel)); |
There was a problem hiding this comment.
I think you can use import { Mutex } from 'async-mutex'; its already used in other Controllers
|
@metamaskbot publish-preview |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ 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 337abd9. Configure here.
|
Preview builds have been published. Learn how to use preview builds in other projects. Expand for full list of packages and versions. |
|
@metamaskbot publish-preview |
|
Preview builds have been published. Learn how to use preview builds in other projects. Expand for full list of packages and versions. |


Explanation
Architecture Overview
What
OHLCVServicefor real-time OHLCV (candlestick) data streaming via the backend WebSocket gatewayBackendWebSocketService,AccountActivityService) into a newsrc/ws/directory per code review feedbackWhy
ws/folder for better discoverabilityNew 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 reconnectsrc/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 typessrc/ws/ohlcv/types.ts—OHLCVBarandOHLCVSubscriptionOptionstypessrc/ws/ohlcv/index.ts— barrel exportsModified files
src/index.ts— added exports forOHLCVService, its types, and allowed actions/events; updated import paths to./ws/eslint-suppressions.json— updated paths for moved files, added suppressions for new test fileCHANGELOG.md— documented new service and exportsMoved files (no logic changes)
src/BackendWebSocketService.ts→src/ws/BackendWebSocketService.tssrc/BackendWebSocketService.test.ts→src/ws/BackendWebSocketService.test.tssrc/BackendWebSocketService-method-action-types.ts→src/ws/BackendWebSocketService-method-action-types.tssrc/AccountActivityService.ts→src/ws/AccountActivityService.tssrc/AccountActivityService.test.ts→src/ws/AccountActivityService.test.tssrc/AccountActivityService-method-action-types.ts→src/ws/AccountActivityService-method-action-types.ts./logger→../logger,./types→../types, test helper paths)Key design decisions
AccountActivityService(auto-subscribes on account change),OHLCVServiceexposessubscribe()/unsubscribe()called by the UI when the chart mounts/unmountschannelHasSubscriptionbefore subscribing; duplicate calls are no-ops (React Strict Mode safe)system-notifications.v1.market-data.v1(auto-subscribed by server) and publishesOHLCVService:chainStatusChangedchainStatusChanged { status: 'down' }for all tracked chains, triggering UI polling fallbacksessionIdneeded for OHLCV; UI polling fallback covers the gap)init()method — system notification callback registered ininit()(not constructor) to comply with messenger-in-constructor lint ruleEvents published
OHLCVService:barUpdated—{ channel, bar: OHLCVBar }— new candle data from WebSocketOHLCVService:chainStatusChanged—{ chainIds, status, timestamp? }— chain up/down (server notification or WS disconnect)OHLCVService:subscriptionError—{ channel, error, operation }— subscribe or unsubscribe failureReferences
Checklist
Note
Medium Risk
Adds a new WebSocket-based OHLCV streaming service with ref-counting, timers, and reconnection logic, which could affect subscription lifecycle and reconnect behavior if misused. Also moves existing WebSocket services into
src/ws/, so any missed import/export updates could break consumers.Overview
Adds real-time OHLCV candlestick streaming via WebSocket. Introduces
OHLCVService(plus types, messenger action types, and exports) that wrapsBackendWebSocketServiceto providesubscribe/unsubscribesemantics formarket-data.v1channels, including reference counting, a 3s grace-period unsubscribe, idempotent subscribe checks, reconnect resubscription, chain up/down forwarding from system notifications, and error reporting viaOHLCVService:subscriptionError.Reorganizes WebSocket code and public exports. Moves
BackendWebSocketServiceandAccountActivityServiceintosrc/ws/and updatessrc/index.tsexports accordingly; addsasync-mutexas a new dependency for concurrency control; and updates the changelog and ESLint suppressions for the new paths/tests.Reviewed by Cursor Bugbot for commit 150787a. Bugbot is set up for automated code reviews on this repo. Configure here.