Skip to content

feat: move perps streaming ownership to background (Phase 2 POC)#40600

Merged
gambinish merged 8 commits intoperps/poc-controller-integration-tempfrom
perps/phase2-background-streaming
Mar 4, 2026
Merged

feat: move perps streaming ownership to background (Phase 2 POC)#40600
gambinish merged 8 commits intoperps/poc-controller-integration-tempfrom
perps/phase2-background-streaming

Conversation

@abretonc7s
Copy link

@abretonc7s abretonc7s commented Mar 4, 2026

Description

This PR implements Path 2 from the streaming architecture discussion: move WebSocket ownership from the UI to the background process, eliminating the dual-controller problem flagged in #40078.

The Problem (from #40078 review)

The current architecture creates two PerpsController instances — one in the background and one in the UI — resulting in:

  • Two independent WebSocket connections to HyperLiquid
  • Split controller authority / two state machines
  • Risk of race conditions and data inconsistency

Path 2: Custom JSON-RPC Notification Channel

Background subscribes to HyperLiquid WebSocket via PerpsController, then emits perpsStreamUpdate notifications over the existing outStream connection. The UI handles them alongside sendUpdate in ui/index.js.

Background PerpsController (WebSocket owner)
  → subscribeToPositions/Orders/Account/Prices/OrderBook/Candles
  → emitPerpsUpdate(channel, data) → outStream.write({ method: 'perpsStreamUpdate' })
    ↓ (only when perpsSubscriberCount > 0)
UI ui/index.js onNotification
  → getPerpsStreamManager().handleBackgroundUpdate({ channel, data })
    ↓
PerpsDataChannel.pushData() → React subscribers (direct, no Redux, no debounce)

What Changed

Area Change
metamask-controller.js Subscribe background controller to all 6 channels on connection setup; emit perpsStreamUpdate notifications; track per-connection subscriber count via perpsSubscriberChange
ui/index.js Route perpsStreamUpdatePerpsStreamManager.handleBackgroundUpdate()
getPerpsController.ts Remove UI PerpsController instantiation entirely — no new PerpsController(), no messenger, no streaming controller
createPerpsControllerFacade.ts Remove all streaming methods; all operations delegate to background
PerpsStreamManager.ts Add prices/orderBook channels; handleBackgroundUpdate() for all 6 channels; initial REST connectFn for positions/orders/account; eager cache clear on account switch
CandleStreamChannel.ts Add pushFromBackground()
usePerpsLivePrices / usePerpsLiveOrderBook Migrate from direct controller subscribe → usePerpsChannel pattern
usePerpsStreamManager Clear caches synchronously on account switch to prevent stale-data flash

Key Properties

  • No Redux overhead for high-frequency data (prices, orderBook, candles)
  • No debounce — data reaches React components directly via PerpsDataChannel.pushData()
  • Component-scoped: only UI connections with perpsSubscriberCount > 0 receive stream data
  • Single WebSocket: one connection per HyperLiquid endpoint, owned by background
  • Cache preserved across navigation; cleared on account switch

Tests

193 tests passing across 12 suites. All perps provider and hook tests updated to match new architecture.

Changelog

CHANGELOG entry: null

Related


Note

High Risk
High risk because it rewires the Perps streaming lifecycle and introduces new background→UI notification plumbing in metamask-controller.js, so regressions could break live prices/order book/candles or leave subscriptions active unexpectedly.

Overview
Perps streaming is moved to the background process. A new per-connection PerpsStreamBridge subscribes the background PerpsController to positions/orders/account and dynamically switches prices/orderBook/candles streams, emitting perpsStreamUpdate notifications only when the UI reports active subscribers.

The UI now routes perpsStreamUpdate in ui/index.js into PerpsStreamManager.handleBackgroundUpdate(). PerpsStreamManager, CandleStreamChannel, and stream hooks/pages are refactored to stop using a UI-side streaming controller; they instead (1) fetch initial snapshots via background RPC (perpsGetPositions, perpsGetAccountState, etc.), (2) request background streaming via perpsActivateStreaming, and (3) consume pushed updates from the manager.

Perps routing/provider wiring is adjusted to lazy-load a new PerpsLayout that wraps all Perps routes with PerpsControllerProvider, removing PerpsRouteWrapper. Misc cleanup includes removing mock-based Perps exports/usages in UI components/tests and updating affected tests/baselines.

Written by Cursor Bugbot for commit 11b8756. This will update automatically on new commits. Configure here.

Implements Path 2 streaming architecture: background PerpsController
owns WebSocket connections and emits perpsStreamUpdate notifications
to the UI. The UI PerpsController instance is removed entirely,
eliminating dual WebSocket connections and split controller authority.

Changes:
- metamask-controller.js: subscribe background PerpsController to all
  6 channels (positions, orders, account, prices, orderBook, candles);
  emit perpsStreamUpdate notifications per-connection; track subscriber
  count via perpsSubscriberChange to gate emission
- ui/index.js: route perpsStreamUpdate notifications to PerpsStreamManager
- PerpsStreamManager: add prices/orderBook channels; add
  handleBackgroundUpdate() for all 6 channels; add initial REST fetch
  connectFn for positions/orders/account; clear caches on account switch
- getPerpsController: remove UI PerpsController instantiation; simplify
  to facade-only; add markPerpsControllerInitialized/isInitialized helpers
- createPerpsControllerFacade: remove streaming methods; all operations
  delegate to background via submitRequestToBackground
- usePerpsLivePrices/usePerpsLiveOrderBook: migrate from direct controller
  subscribe to usePerpsChannel pattern
- CandleStreamChannel: add pushFromBackground() method
- usePerpsStreamManager: clear caches eagerly on account switch to
  prevent stale data flash
- All related tests updated (193 passing)
@github-actions
Copy link
Contributor

github-actions bot commented Mar 4, 2026

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.

@metamaskbot metamaskbot added team-perps Perps team INVALID-PR-TEMPLATE PR's body doesn't match template labels Mar 4, 2026
@metamaskbotv2
Copy link
Contributor

metamaskbotv2 bot commented Mar 4, 2026

✨ Files requiring CODEOWNER review ✨

👨‍🔧 @MetaMask/perps (26 files, +1169 -1958)
  • 📁 app/
    • 📁 scripts/
      • 📁 controllers/
        • 📁 perps/
          • 📄 perps-stream-bridge.ts +120 -0
  • 📁 ui/
    • 📁 components/
      • 📁 app/
        • 📁 perps/
          • 📁 order-entry/
            • 📁 components/
              • 📁 amount-input/
                • 📄 amount-input.tsx +1 -2
              • 📄 index.ts +0 -12
          • 📁 perps-recent-activity/
            • 📄 perps-recent-activity.test.tsx +112 -99
            • 📄 perps-recent-activity.tsx +3 -3
            • 📄 perps-tab-view.tsx +1 -1
    • 📁 hooks/
      • 📁 perps/
        • 📁 stream/
          • 📄 usePerpsLiveOrderBook.ts +18 -39
          • 📄 usePerpsLivePrices.ts +31 -54
          • 📄 usePerpsStreamManager.ts +17 -17
          • 📄 usePerpsTopOfBook.ts +49 -52
          • 📄 usePerpsOrderForm.ts +1 -1
    • 📁 pages/
      • 📁 perps/
        • 📄 perps-layout.tsx +18 -0
        • 📄 perps-market-detail-page.tsx +22 -35
        • 📄 perps-order-entry-page.tsx +47 -64
    • 📁 providers/
      • 📁 perps/
        • 📄 CandleStreamChannel.test.ts +191 -251
        • 📄 CandleStreamChannel.ts +63 -53
        • 📄 createPerpsControllerFacade.test.ts +29 -65
        • 📄 createPerpsControllerFacade.ts +16 -42
        • 📄 getPerpsController.test.ts +48 -172
        • 📄 getPerpsController.ts +49 -278
        • 📄 PerpsControllerProvider.test.tsx +3 -8
        • 📄 PerpsControllerProvider.tsx +11 -23
        • 📄 PerpsRouteWrapper.test.tsx +0 -273
        • 📄 PerpsRouteWrapper.tsx +0 -134
        • 📄 PerpsStreamManager.test.ts +168 -195
        • 📄 PerpsStreamManager.ts +151 -85

@abretonc7s
Copy link
Author

I have read the CLA Document and I hereby sign the CLA

@metamaskbotv2
Copy link
Contributor

metamaskbotv2 bot commented Mar 4, 2026

Builds ready [fedc086]
⚡ Performance Benchmarks
👆 Interaction Benchmarks
BenchmarkMetricMean (ms)Min (ms)Max (ms)Std Dev (ms)P75 (ms)P95 (ms)
Load New Accountload_new_account28226133631267336
total28226133631267336
Confirm Txconfirm_tx6048600460983960926098
total6048600460983960926098
Bridge User Actionsbridge_load_page2142122172215217
bridge_load_asset_picker2001992000200200
bridge_search_token7057027093708709
total111711161120211201120
🔌 Startup Benchmarks
BenchmarkMetricMean (ms)Min (ms)Max (ms)Std Dev (ms)P75 (ms)P95 (ms)
Standard HomeuiStartup14361200184011014761644
load1206101315039912451395
domContentLoaded119798514539512351383
domInteractive2917114202579
firstPaint1567245987213345
backgroundConnect21419440223214246
firstReactRender20125862130
initialActions208224
loadScripts100178812479410391188
setupStore1363351721
numNetworkReqs312288192582
Power User HomeuiStartup216414409494108420904285
load11981030164914012261533
domContentLoaded11791014158513112091471
domInteractive3519149203382
firstPaint211861578201261339
backgroundConnect50326038815893811669
firstReactRender25156692746
initialActions107113
loadScripts96582013361249961249
setupStore1553861828
numNetworkReqs71401462181113
🧭 User Journey Benchmarks
BenchmarkMetricMean (ms)Min (ms)Max (ms)Std Dev (ms)P75 (ms)P95 (ms)
Onboarding Import WalletimportWalletToSocialScreen2202182232222223
srpButtonToSrpForm95949719497
confirmSrpToPwForm22212212222
pwFormToMetricsScreen16151601616
metricsToWalletReadyScreen17161701717
doneButtonToHomeScreen63960172550617725
openAccountMenuToAccountListLoaded2929290629461529372946
total3959388840687140184068
Onboarding New WalletcreateWalletToSocialScreen2182172191219219
srpButtonToPwForm1061041092105109
createPwToRecoveryScreen888088
skipBackupToMetricsScreen35343613636
agreeButtonToOnboardingSuccess16161601616
doneButtonToAssetList54747768287618682
total94086111181039981118
Asset DetailsassetClickToPriceChart554571126971
total554571126971
Solana Asset DetailsassetClickToPriceChart78718668286
total78718668286
Import Srp HomeloginToHomeScreen21781969252521123162525
openAccountMenuAfterLogin43375054450
homeAfterImportWithNewWallet30202712369336931403693
total49854852515313350785153
Send TransactionsopenSendPageFromHome24173152931
selectTokenToSendFormLoaded19182012020
reviewTransactionToConfirmationPage8488468512850851
total8918838987898898
SwapopenSwapPageFromHome12910815217140152
fetchAndDisplaySwapQuotes289428932895128952895
total3023300130531830283053
🌐 Dapp Page Load Benchmarks

Current Commit: fedc086 | Date: 3/4/2026

📄 Localhost MetaMask Test Dapp

Samples: 100

Summary

  • pageLoadTime-> current mean value: 985ms (±73ms) 🟢 | historical mean value: 1.05s ⬇️ (historical data)
  • domContentLoaded-> current mean value: 698ms (±81ms) 🟢 | historical mean value: 740ms ⬇️ (historical data)
  • firstContentfulPaint-> current mean value: 88ms (±118ms) 🟢 | historical mean value: 81ms ⬆️ (historical data)

📈 Detailed Results

Metric Mean Std Dev Min Max P95 P99
pageLoadTime 985ms 73ms 940ms 1.30s 1.21s 1.30s
domContentLoaded 698ms 81ms 656ms 1.20s 903ms 1.20s
firstPaint 88ms 118ms 60ms 1.26s 88ms 1.26s
firstContentfulPaint 88ms 118ms 60ms 1.26s 88ms 1.26s
largestContentfulPaint 0ms 0ms 0ms 0ms 0ms 0ms
Bundle size diffs [🚨 Warning! Bundle size has increased!]
  • background: 7.69 MiB (100%)
  • ui: 8.18 MiB (100%)
  • common: 10.9 MiB (100%)

@gambinish gambinish marked this pull request as ready for review March 4, 2026 19:53
@gambinish gambinish requested a review from a team as a code owner March 4, 2026 19:53
@gambinish gambinish merged commit 30bd5e1 into perps/poc-controller-integration-temp Mar 4, 2026
5 of 8 checks passed
@gambinish gambinish deleted the perps/phase2-background-streaming branch March 4, 2026 19:53
@github-actions github-actions bot locked and limited conversation to collaborators Mar 4, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

INVALID-PR-TEMPLATE PR's body doesn't match template size-XL team-perps Perps team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants