Skip to content

feat(perps): wire dedicated aggregated order-book socket per UI connection - #45035

Merged
michalconsensys merged 2 commits into
mainfrom
feat/metamask-extension-perps-order-book-socket
Jul 30, 2026
Merged

feat(perps): wire dedicated aggregated order-book socket per UI connection#45035
michalconsensys merged 2 commits into
mainfrom
feat/metamask-extension-perps-order-book-socket

Conversation

@michalconsensys

@michalconsensys michalconsensys commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Description

Context

The Perps order-book ladder needs a server-aggregated l2Book subscription (nSigFigs / mantissa / fast). PerpsController already keeps a shared Hyperliquid WebSocket for the raw l2Book used by spread / mid / slippage. The Hyperliquid SDK dispatches l2Book events by coin only, so running raw and aggregated subscriptions for the same coin on one socket cross-contaminates them.

Problem

Without a dedicated connection per UI stream, aggregated and raw order-book data for the same market can clobber each other on the shared controller socket.

Solution

In the per-UI-connection setup path of metamask-controller.js:

  • Construct one AggregatedOrderBookConnection per UI outStream (not a controller singleton), with live isTestnet reads from perpsController.state.
  • Inject subscribeAggregatedOrderBook into PerpsStreamBridge.
  • Close the dedicated socket in outstreamEndHandler after perpsStream?.destroy().

Also stub AggregatedOrderBookConnection in the Jest mock for @metamask/perps-controller so unit tests do not open a real socket.

Changelog

CHANGELOG entry: null

Related issues

Relates to #44254

Manual testing steps

Feature: Dedicated aggregated order-book socket per UI connection

  Scenario: Order-book panel uses the dedicated aggregated connection
    Given Perps is enabled and the extension UI is connected
    And a market detail / order-book panel is available
    When I open the order-book panel for a market
    Then the aggregated `l2Book` stream is activated via the injected AggregatedOrderBookConnection
    And the raw order book / spread path still uses the controller shared socket

  Scenario: UI disconnect tears down the dedicated socket
    Given an active UI connection with an AggregatedOrderBookConnection
    When the UI outStream ends (popup close / stream teardown)
    Then PerpsStreamBridge is destroyed first
    And aggregatedOrderBookConnection.close() is called afterward

Screenshots/Recordings

N/A — background socket wiring only; no UI visual change in this PR.

Pre-merge author checklist

Pre-merge reviewer checklist

  • I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed).
  • I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots.

Note

Medium Risk
Changes real-time perps streaming and WebSocket lifecycle per UI connection; incorrect teardown or subscription identity could leak sockets or show wrong ladder data, though behavior is heavily covered by new bridge tests.

Overview
Wires server-aggregated order book (nSigFigs / mantissa) through a dedicated Hyperliquid WebSocket per UI outStream, separate from the controller’s shared socket used for the raw l2Book. That avoids SDK l2Book dispatch-by-coin cross-talk between the grouped ladder and spread/slippage paths.

PerpsStreamBridge gains perpsActivateOrderBookAggregatedStream / perpsDeactivateOrderBookAggregatedStream, injecting subscribeAggregatedOrderBook and emitting orderBookAggregated plus orderBookAggregatedStatus with an optional subscriptionId so the UI can ignore stale packets after grouping changes.

Price, raw order book, and aggregated streams now share #activateDynamicWhenReady / #deactivateDynamicChannel generation guards so a deactivate (or destroy) during pending perpsInit does not resurrect a hidden subscription after teardown.

metamask-controller.js constructs AggregatedOrderBookConnection when the stream opens and **close()**s it after perpsStream.destroy(); Jest mocks stub the connection for unit tests.

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

…ction

Isolate the order-book panel's nSigFigs l2Book subscription on AggregatedOrderBookConnection so it cannot cross-contaminate the controller's shared raw book socket.
@github-actions

Copy link
Copy Markdown
Contributor

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.

@metamask-ci

metamask-ci Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

✨ Files requiring CODEOWNER review ✨

🫰 @MetaMask/core-platform (1 files, +13 -0)
  • 📁 app/
    • 📁 scripts/
      • 📄 metamask-controller.js +13 -0

👨‍🔧 @MetaMask/extension-platform (1 files, +13 -0)
  • 📁 app/
    • 📁 scripts/
      • 📄 metamask-controller.js +13 -0

👨‍🔧 @MetaMask/perps (2 files, +466 -24)
  • 📁 app/
    • 📁 scripts/
      • 📁 controllers/
        • 📁 perps/
          • 📄 perps-stream-bridge.test.ts +290 -7
          • 📄 perps-stream-bridge.ts +176 -17

@metamask-ci metamask-ci Bot added the team-perps Perps team label Jul 30, 2026
@michalconsensys
michalconsensys marked this pull request as ready for review July 30, 2026 12:07
@michalconsensys
michalconsensys requested review from a team as code owners July 30, 2026 12:07

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ 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 ea3c074. Configure here.

Comment thread app/scripts/metamask-controller.js
@metamask-ci

metamask-ci Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor
Builds ready [ea3c074]
⚡ Performance Benchmarks (Total: 🟢 14 pass · 🟡 10 warn · 🔴 0 fail)

Baseline (latest main): 171ed20 | Date: 7/28/2026 | Pipeline: 30541047410 | Baseline logs

Interaction Benchmarks · Samples: 5
Benchmarkchrome-webpackfirefox-webpack
loadNewAccount
[Sentry log · main/release]
🟡 [CI log]
🟡 load_new_account
🟡 [CI log]
🟡 load_new_account
confirmTx
[Sentry log · main/release]
🟢 [CI log]🟢 [CI log]
bridgeUserActions
[Sentry log · main/release]
🟢 [CI log]🟢 [CI log]

📈 Results compared to the previous 5 runs on main

  • loadNewAccount/load_new_account: +18%
  • loadNewAccount/total: +18%
  • bridgeUserActions/bridge_load_asset_picker: +29%
  • bridgeUserActions/longTaskCount: +11%
  • bridgeUserActions/longTaskTotalDuration: +15%
  • bridgeUserActions/tbt: +25%
  • loadNewAccount/load_new_account: +22%
  • loadNewAccount/total: +22%
  • loadNewAccount/inp: -17%
  • loadNewAccount/lcp: +1151%
  • confirmTx/confirm_tx: +11%
  • confirmTx/longTaskCount: -100%
  • confirmTx/longTaskTotalDuration: -100%
  • confirmTx/longTaskMaxDuration: -100%
  • confirmTx/tbt: -100%
  • confirmTx/total: +11%
  • confirmTx/inp: -17%
  • confirmTx/fcp: -42%
  • confirmTx/lcp: +1175%
  • bridgeUserActions/bridge_load_page: +393%
  • bridgeUserActions/bridge_load_asset_picker: +152%
  • bridgeUserActions/longTaskCount: -100%
  • bridgeUserActions/longTaskTotalDuration: -100%
  • bridgeUserActions/longTaskMaxDuration: -100%
  • bridgeUserActions/tbt: -100%
  • bridgeUserActions/total: +59%
  • bridgeUserActions/fcp: -44%
  • bridgeUserActions/lcp: +1194%

🌐 Core Web Vitals — 🟢 good · 🟡 needs improvement · 🔴 poor (web.dev thresholds)

  • 🟡 loadNewAccount/FCP: p75 1.9s
Startup Benchmarks · Samples: 100
Benchmarkchrome-webpackfirefox-webpack
startupStandardHome
[Sentry log · main/release]
🟢 [CI log]🟢 [CI log]
startupPowerUserHome
[Sentry log · main/release]
🟡 [CI log]

📈 Results compared to the previous 5 runs on main

  • startupStandardHome/numNetworkReqs: -17%
  • startupStandardHome/domInteractive: -19%
  • startupStandardHome/numNetworkReqs: -17%
  • startupStandardHome/fcp: -26%
  • startupPowerUserHome/load: +17%
  • startupPowerUserHome/domContentLoaded: +17%
  • startupPowerUserHome/domInteractive: +33%
  • startupPowerUserHome/backgroundConnect: +35%
  • startupPowerUserHome/firstReactRender: +16%
  • startupPowerUserHome/initialActions: +11%
  • startupPowerUserHome/loadScripts: +15%
  • startupPowerUserHome/setupStore: +286%
  • startupPowerUserHome/fcp: +27%
  • startupPowerUserHome/lcp: +11%

🌐 Core Web Vitals — 🟢 good · 🟡 needs improvement · 🔴 poor (web.dev thresholds)

  • 🟡 startupPowerUserHome/LCP: p75 3.2s
User Journey Benchmarks · Samples: 5 · mock API
Benchmarkchrome-webpackfirefox-webpack
onboardingImportWallet
[Sentry log · main/release]
🟡 [CI log]
🟡 total
🟢 [CI log]
onboardingNewWallet
[Sentry log · main/release]
🟢 [CI log]
🔴 total
🟡 [CI log]
🟡 total
assetDetails
[Sentry log · main/release]
🟢 [CI log]🟡 [CI log]
solanaAssetDetails
[Sentry log · main/release]
🟢 [CI log]🟡 [CI log]
importSrpHome
[Sentry log · main/release]
🟡 [CI log]🟡 [CI log]
sendTransactions
[Sentry log · main/release]
🟢 [CI log]🟡 [CI log]
swap
[Sentry log · main/release]
🟢 [CI log]🟢 [CI log]

📈 Results compared to the previous 5 runs on main

  • onboardingImportWallet/srpButtonToSrpForm: +21%
  • onboardingImportWallet/doneButtonToHomeScreen: -92%
  • onboardingImportWallet/openAccountMenuToAccountListLoaded: -53%
  • onboardingImportWallet/longTaskCount: -86%
  • onboardingImportWallet/longTaskTotalDuration: -96%
  • onboardingImportWallet/longTaskMaxDuration: -93%
  • onboardingImportWallet/tbt: -100%
  • onboardingImportWallet/total: -82%
  • onboardingNewWallet/srpButtonToPwForm: -13%
  • onboardingNewWallet/agreeButtonToOnboardingSuccess: -27%
  • onboardingNewWallet/doneButtonToAssetList: -12%
  • onboardingNewWallet/longTaskCount: -100%
  • onboardingNewWallet/longTaskTotalDuration: -100%
  • onboardingNewWallet/longTaskMaxDuration: -100%
  • onboardingNewWallet/tbt: -100%
  • onboardingNewWallet/total: -11%
  • solanaAssetDetails/assetClickToPriceChart: -66%
  • solanaAssetDetails/longTaskCount: -100%
  • solanaAssetDetails/longTaskTotalDuration: -100%
  • solanaAssetDetails/longTaskMaxDuration: -100%
  • solanaAssetDetails/tbt: -100%
  • solanaAssetDetails/total: -66%
  • solanaAssetDetails/fcp: +12%
  • importSrpHome/loginToHomeScreen: -39%
  • importSrpHome/homeAfterImportWithNewWallet: -22%
  • importSrpHome/longTaskCount: -65%
  • importSrpHome/longTaskTotalDuration: -63%
  • importSrpHome/longTaskMaxDuration: -32%
  • importSrpHome/tbt: -63%
  • importSrpHome/total: -22%
  • importSrpHome/inp: -33%
  • importSrpHome/lcp: +23%
  • importSrpHome/cls: +449%
  • sendTransactions/openSendPageFromHome: +22%
  • sendTransactions/reviewTransactionToConfirmationPage: -98%
  • sendTransactions/longTaskCount: -100%
  • sendTransactions/longTaskTotalDuration: -100%
  • sendTransactions/longTaskMaxDuration: -100%
  • sendTransactions/tbt: -100%
  • sendTransactions/total: -96%
  • sendTransactions/inp: -33%
  • sendTransactions/lcp: -67%
  • sendTransactions/cls: +171%
  • swap/openSwapPageFromHome: -17%
  • swap/fetchAndDisplaySwapQuotes: +78%
  • swap/longTaskCount: -100%
  • swap/longTaskTotalDuration: -100%
  • swap/longTaskMaxDuration: -100%
  • swap/tbt: -100%
  • swap/total: +77%
  • swap/fcp: -11%
  • swap/lcp: -76%
  • swap/cls: -92%

🌐 Core Web Vitals — 🟢 good · 🟡 needs improvement · 🔴 poor (web.dev thresholds)

  • 🟡 importSrpHome/INP: p75 208ms
  • 🟡 assetDetails/FCP: p75 1.9s
  • 🟡 solanaAssetDetails/FCP: p75 1.9s
  • 🟡 importSrpHome/FCP: p75 2.0s
  • 🔴 sendTransactions/INP: p75 768ms
  • 🟡 sendTransactions/FCP: p75 2.1s
Dapp Page Load Benchmarks · Samples: 100
Benchmarkchrome-webpack
dappPageLoad
[Sentry log · main/release]
🟢 [CI log]
Bundle size diffs [🚨 Warning! Bundle size has increased!]
  • background: 4.73 KiB (0.03%)
  • ui: 3.48 KiB (0.02%)
  • common: 0 Bytes (0%)
  • other: 0 Bytes (0%)
  • contentScripts: 80 Bytes (0%)
  • zip: 2.21 KiB (0.01%)

Route the aggregated order-book channel through the injected dedicated
connection so raw and grouped l2Book streams no longer share one socket.
@sonarqubecloud

Copy link
Copy Markdown

@metamask-ci

metamask-ci Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor
Builds ready [ccd9fe8]
⚡ Performance Benchmarks (Total: 🟢 14 pass · 🟡 9 warn · 🔴 1 fail)

Baseline (latest main): 171ed20 | Date: 7/28/2026 | Pipeline: 30549801973 | Baseline logs

Metricschrome-webpackfirefox-webpack
onboardingNewWallet
[Sentry log · main/release]
🟢 [CI log]🔴 [CI log]

Regressions (🔴 1 failure)

Interaction Benchmarks · Samples: 5
Benchmarkchrome-webpackfirefox-webpack
loadNewAccount
[Sentry log · main/release]
🟢 [CI log]🟡 [CI log]
🟡 load_new_account
confirmTx
[Sentry log · main/release]
🟢 [CI log]🟢 [CI log]
bridgeUserActions
[Sentry log · main/release]
🟢 [CI log]🟢 [CI log]

📈 Results compared to the previous 5 runs on main

  • loadNewAccount/inp: -17%
  • loadNewAccount/fcp: -17%
  • confirmTx/longTaskTotalDuration: -20%
  • confirmTx/longTaskMaxDuration: -21%
  • confirmTx/tbt: -35%
  • confirmTx/inp: -32%
  • bridgeUserActions/bridge_load_page: -15%
  • bridgeUserActions/bridge_load_asset_picker: -32%
  • bridgeUserActions/longTaskCount: -44%
  • bridgeUserActions/longTaskTotalDuration: -43%
  • bridgeUserActions/longTaskMaxDuration: -12%
  • bridgeUserActions/tbt: -39%
  • bridgeUserActions/total: -11%
  • bridgeUserActions/fcp: -15%
  • bridgeUserActions/lcp: +694%
  • loadNewAccount/load_new_account: +24%
  • loadNewAccount/total: +24%
  • loadNewAccount/inp: +165%
  • loadNewAccount/fcp: -62%
  • loadNewAccount/lcp: +1095%
  • confirmTx/longTaskCount: -100%
  • confirmTx/longTaskTotalDuration: -100%
  • confirmTx/longTaskMaxDuration: -100%
  • confirmTx/tbt: -100%
  • confirmTx/inp: -24%
  • confirmTx/fcp: -47%
  • confirmTx/lcp: +1123%
  • bridgeUserActions/bridge_load_page: +553%
  • bridgeUserActions/bridge_load_asset_picker: +192%
  • bridgeUserActions/longTaskCount: -100%
  • bridgeUserActions/longTaskTotalDuration: -100%
  • bridgeUserActions/longTaskMaxDuration: -100%
  • bridgeUserActions/tbt: -100%
  • bridgeUserActions/total: +91%
  • bridgeUserActions/inp: -15%
  • bridgeUserActions/fcp: -51%
  • bridgeUserActions/lcp: +1114%

🌐 Core Web Vitals — 🟢 good · 🟡 needs improvement · 🔴 poor (web.dev thresholds)

  • 🟡 loadNewAccount/INP: p75 280ms
Startup Benchmarks · Samples: 100
Benchmarkchrome-webpackfirefox-webpack
startupStandardHome
[Sentry log · main/release]
🟢 [CI log]🟡 [CI log]
🟡 loadScripts
startupPowerUserHome
[Sentry log · main/release]
🟢 [CI log]

📈 Results compared to the previous 5 runs on main

  • startupStandardHome/numNetworkReqs: -14%
  • startupStandardHome/numNetworkReqs: -13%
  • startupPowerUserHome/uiStartup: -17%
  • startupPowerUserHome/load: -12%
  • startupPowerUserHome/domContentLoaded: -12%
  • startupPowerUserHome/backgroundConnect: -16%
  • startupPowerUserHome/firstReactRender: -14%
  • startupPowerUserHome/initialActions: +11%
  • startupPowerUserHome/loadScripts: -11%
  • startupPowerUserHome/setupStore: -75%
  • startupPowerUserHome/numNetworkReqs: +31%
  • startupPowerUserHome/inp: -19%
  • startupPowerUserHome/lcp: -16%
User Journey Benchmarks · Samples: 5 · mock API 🔴 1
Benchmarkchrome-webpackfirefox-webpack
onboardingImportWallet
[Sentry log · main/release]
🟡 [CI log]
🟡 total
🟢 [CI log]
onboardingNewWallet
[Sentry log · main/release]
🟢 [CI log]
🔴 total
🔴 [CI log]
🔴 total
assetDetails
[Sentry log · main/release]
🟢 [CI log]🟡 [CI log]
solanaAssetDetails
[Sentry log · main/release]
🟢 [CI log]🟡 [CI log]
importSrpHome
[Sentry log · main/release]
🟡 [CI log]🟡 [CI log]
sendTransactions
[Sentry log · main/release]
🟢 [CI log]🟡 [CI log]
swap
[Sentry log · main/release]
🟢 [CI log]🟡 [CI log]

📈 Results compared to the previous 5 runs on main

  • onboardingImportWallet/srpButtonToSrpForm: +10%
  • onboardingImportWallet/metricsToWalletReadyScreen: -14%
  • onboardingImportWallet/doneButtonToHomeScreen: -92%
  • onboardingImportWallet/openAccountMenuToAccountListLoaded: -53%
  • onboardingImportWallet/longTaskCount: -86%
  • onboardingImportWallet/longTaskTotalDuration: -96%
  • onboardingImportWallet/longTaskMaxDuration: -93%
  • onboardingImportWallet/tbt: -100%
  • onboardingImportWallet/total: -82%
  • onboardingNewWallet/agreeButtonToOnboardingSuccess: -11%
  • onboardingNewWallet/longTaskCount: -100%
  • onboardingNewWallet/longTaskTotalDuration: -100%
  • onboardingNewWallet/longTaskMaxDuration: -100%
  • onboardingNewWallet/tbt: -100%
  • solanaAssetDetails/assetClickToPriceChart: -70%
  • solanaAssetDetails/longTaskCount: -100%
  • solanaAssetDetails/longTaskTotalDuration: -100%
  • solanaAssetDetails/longTaskMaxDuration: -100%
  • solanaAssetDetails/tbt: -100%
  • solanaAssetDetails/total: -70%
  • solanaAssetDetails/inp: -21%
  • solanaAssetDetails/fcp: -14%
  • solanaAssetDetails/lcp: -16%
  • importSrpHome/homeAfterImportWithNewWallet: -13%
  • importSrpHome/longTaskCount: -37%
  • importSrpHome/longTaskTotalDuration: -36%
  • importSrpHome/tbt: -39%
  • importSrpHome/total: -12%
  • importSrpHome/inp: -25%
  • importSrpHome/cls: +494%
  • sendTransactions/openSendPageFromHome: -35%
  • sendTransactions/reviewTransactionToConfirmationPage: -98%
  • sendTransactions/longTaskCount: -100%
  • sendTransactions/longTaskTotalDuration: -100%
  • sendTransactions/longTaskMaxDuration: -100%
  • sendTransactions/tbt: -100%
  • sendTransactions/total: -95%
  • sendTransactions/inp: -26%
  • sendTransactions/lcp: -65%
  • sendTransactions/cls: +171%
  • swap/openSwapPageFromHome: -20%
  • swap/fetchAndDisplaySwapQuotes: +102%
  • swap/longTaskCount: -100%
  • swap/longTaskTotalDuration: -100%
  • swap/longTaskMaxDuration: -100%
  • swap/tbt: -100%
  • swap/total: +100%
  • swap/lcp: -74%
  • swap/cls: -92%

🌐 Core Web Vitals — 🟢 good · 🟡 needs improvement · 🔴 poor (web.dev thresholds)

  • 🟡 importSrpHome/INP: p75 232ms
  • 🟡 importSrpHome/FCP: p75 1.8s
  • 🟡 assetDetails/FCP: p75 1.9s
  • 🟡 solanaAssetDetails/FCP: p75 1.9s
  • 🟡 importSrpHome/FCP: p75 1.9s
  • 🟡 sendTransactions/FCP: p75 1.9s
  • 🟡 swap/FCP: p75 1.9s
Dapp Page Load Benchmarks · Samples: 100
Benchmarkchrome-webpack
dappPageLoad
[Sentry log · main/release]
🟢 [CI log]
Bundle size diffs [🚨 Warning! Bundle size has increased!]
  • background: 9.28 KiB (0.07%)
  • ui: 5.93 KiB (0.03%)
  • common: 0 Bytes (0%)
  • other: 0 Bytes (0%)
  • contentScripts: 548 Bytes (0.03%)
  • zip: 4.09 KiB (0.02%)

@gauthierpetetin gauthierpetetin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm for @MetaMask/extension-platform CO file:

  • app/scripts/metamask-controller.js

perpsToggleTestnet: this.messengerClientApi.perpsToggleTestnet,
isConnectionAlive: () => !outStream.mmFinished,
subscribeAggregatedOrderBook: (params) =>
aggregatedOrderBookConnection.subscribe(params),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

non-blocking nit: The aggregatedOrderBookConnection / perpsStream pair are both gated on the same perpsController ternary, so the .subscribe() call can never hit a null reference in practice. However, since aggregatedOrderBookConnection is typed as AggregatedOrderBookConnection | null, this will become a compile error the day metamask-controller.js is migrated to TypeScript. Worth adding either a non-null assertion (aggregatedOrderBookConnection!.subscribe(params)) or optional chaining with a no-op fallback now to avoid the issue later.

@michalconsensys
michalconsensys added this pull request to the merge queue Jul 30, 2026
Merged via the queue into main with commit 0610a60 Jul 30, 2026
138 checks passed
@michalconsensys
michalconsensys deleted the feat/metamask-extension-perps-order-book-socket branch July 30, 2026 16:16
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 30, 2026
@metamaskbot metamaskbot added the release-13.43.0 Issue or pull request that will be included in release 13.43.0 label Jul 30, 2026
@MajorLift

MajorLift commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

🧪 Validation Run

Verdict: ✅ nothing introduced — every new retention primitive is torn down — Claim: the dedicated aggregated order-book socket releases what it acquires. head 0610a607c74 · 2026-08-02 · retention-pairing check

Note

Trial run of the MetaMask evidence skills,
using the memory-leak skill
feedback welcome, on the finding or on whether this format is useful to a reviewer.
Not a review verdict; nothing here blocks the PR.

Wiring a new socket is where subscriptions and pending-teardown maps accumulate, so every retention primitive in the changed file was paired against a release in the same file, run in CI at the merge commit:

Ran to completion (exit 0) — read the output, no verdict asserted

Claim under test: the dedicated order-book socket releases everything it acquires

$ sh -c git diff 0610a607c7480022b7d4c201875f294b38d8b45f^ 0610a607c7480022b7d4c201875f294b38d8b45f > d.patch && python3 .evidence-skills/domains/stability/skills/memory-leak/scripts/retention-scan.py app/scripts/controllers/perps/perps-stream-bridge.ts:d.patch
RETENTION REVIEW — scoped to the supplied diff  (re-run: retention-scan.py <file>:<patch> [...])
==========================================================================

perps-stream-bridge.ts
  [pre-exist] ok   L140: #pendingCandleTeardowns  (.set L389)
                   -> .delete L330
  [pre-exist] ok   L154: #pendingCandleActivations  (.set L363)
                   -> .delete L368

VERDICT: no retention path INTRODUCED — every NEW primitive is torn down; no heap snapshot warranted

Produced by capture.sh, not transcribed. head 0610a607c7480022b7d4c201875f294b38d8b45f · 0 tracked changes · node v22.23.1 · Python 3.12.3 · yarn.lock 4fdf764f5380953b. Run: https://github.com/MajorLift/metamask-skills/actions/runs/30765815723 — logs and artifacts attached there.

Follows from the scan above

  • Both maps — #pendingCandleTeardowns and #pendingCandleActivations — have a .delete reachable from their .set.
  • Both are marked pre-existing: the diff wires a new socket through structures that already had their teardown, rather than adding retention of its own.
  • This is the outcome the lane is least likely to produce. A check that only ever reports leaks cannot be read as evidence when it reports none.

Open for review: static pairing shows a release exists in the file, not that it runs on every path — an early return or a thrown error between .set and .delete would satisfy this check and still retain. The scan also covers the bridge only; metamask-controller.js and the mock changed in the same diff and were not scanned.

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

Labels

release-13.43.0 Issue or pull request that will be included in release 13.43.0 risk:medium size-L team-perps Perps team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants