Skip to content

refactor: share a single UI subscription per messenger event type - #42823

Merged
cryptodev-2s merged 29 commits into
mainfrom
cryptodev-2s/wpc-1005-shared-subscription-per-event-type
Jun 17, 2026
Merged

refactor: share a single UI subscription per messenger event type#42823
cryptodev-2s merged 29 commits into
mainfrom
cryptodev-2s/wpc-1005-shared-subscription-per-event-type

Conversation

@cryptodev-2s

@cryptodev-2s cryptodev-2s commented May 20, 2026

Copy link
Copy Markdown
Contributor

Description

Follows up on the discussion in PR #42573 (r3259805738). After that PR, every route messenger that subscribes to a background event called subscribeToMessengerEvent independently — so N route messengers subscribing to the same event produced N upstream messengerSubscribe IPCs and N background.onNotification listeners, each running a filter check on every incoming notification.

This PR deduplicates at the subscribeToMessengerEvent layer (the location the thread converged on — not UIMessenger, which still needs per-messenger bookkeeping for revoke):

  • A module-level Map<event, { callbacks, subscribePromise }> refcounts local subscribers.
  • A single, lazily-attached notificationRouter listens on background.onNotification and dispatches by event-name lookup — no more per-listener filter scan.
  • Multiple local subscribers share the in-flight subscribePromise; the upstream messengerUnsubscribe only fires when the last subscriber leaves.
  • setBackgroundConnection clears the in-memory state, so a replaced background connection starts with a clean slate.

Public signature of subscribeToMessengerEvent is unchanged; both existing callers (UIMessenger.delegate and ui/contexts/query-client.ts) benefit transparently.

How it works

Two subscribers to the same event share one upstream IPC and one notification listener. When a notification arrives, the router does a single map lookup and fans out locally.

sequenceDiagram
    autonumber
    participant A as UI caller A
    participant B as UI caller B
    participant Sub as subscribeToMessengerEvent
    participant Map as eventEntries (Map)
    participant Router as notificationRouter
    participant BG as background (IPC)

    A->>Sub: subscribe('EventX', cbA)
    Sub->>Map: get('EventX') -> undefined
    Sub->>BG: onNotification(router) [attach once]
    Sub->>BG: messengerSubscribe('EventX')
    Sub->>Map: set('EventX', { {cbA}, subscribePromise })
    Sub-->>A: unsubscribeA

    B->>Sub: subscribe('EventX', cbB)
    Sub->>Map: get('EventX') -> entry
    Sub->>Map: entry.callbacks.add(cbB)
    Note over Sub,BG: no new IPC, no new listener
    Sub-->>B: unsubscribeB

    BG-->>Router: notification('EventX', payload)
    Router->>Map: get('EventX') -> entry
    Router->>A: cbA(payload)
    Router->>B: cbB(payload)

    A->>Sub: unsubscribeA()
    Sub->>Map: entry.callbacks.delete(cbA) -> size=1
    Note over Sub,BG: no IPC - others still subscribed

    B->>Sub: unsubscribeB()
    Sub->>Map: entry.callbacks.delete(cbB) -> size=0
    Sub->>Map: delete('EventX')
    Sub->>BG: messengerUnsubscribe('EventX')
Loading

Out of scope (deliberate)

  • UIMessenger is not modified — per the thread's conclusion.
  • ui/contexts/query-client.ts is not modified. It has a latent unsubscribe-before-subscribe-resolves race, but that's a separate concern.
  • Reconnect handling (setBackgroundConnection called a second time mid-session) was already not handled by the previous code; this PR matches that — though it does now clear in-memory state on replace, which is hygiene the previous code lacked.

Changelog

CHANGELOG entry: null

Related issues

Manual testing steps

The behavioral change is invisible to end users — the cost being optimized is per-notification CPU and number of IPC round-trips. The 15 unit tests in ui/store/background-connection.test.ts cover the observable contract. For local smoke:

  1. Build the extension and exercise flows that subscribe to background events (account switch, network switch, transaction list updates).
  2. Verify nothing visibly regresses.

To observe the optimization itself, inspect background.onNotification listeners or the count of messengerSubscribe calls in DevTools — there should now be at most one per distinct event type for the lifetime of the UI session.

Screenshots/Recordings

N/A — no UI changes.

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.

@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.

@metamaskbot metamaskbot added the team-core-platform Core Platform team label May 20, 2026
@cryptodev-2s
cryptodev-2s force-pushed the cryptodev-2s/wpc-1005-shared-subscription-per-event-type branch from 1ac25f5 to 872d338 Compare May 20, 2026 13:28
@cryptodev-2s cryptodev-2s self-assigned this May 20, 2026
@github-actions github-actions Bot added size-M and removed size-XL labels May 20, 2026
@metamaskbotv2

metamaskbotv2 Bot commented May 20, 2026

Copy link
Copy Markdown
Contributor
Builds ready [872d338]
Deprecated Browserify fallback builds
⚡ Performance Benchmarks (Total: 🟢 13 pass · 🟡 11 warn · 🔴 0 fail)

Baseline (latest main): 51036da | Date: 5/2/2026 | Pipeline: 26165693613 | Baseline logs

Interaction Benchmarks · Samples: 5
Benchmarkchrome-webpackfirefox-webpack
loadNewAccount
[Sentry log · main/release]
🟡 [CI log]🟢 [CI log]
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: -71%
  • loadNewAccount/total: -71%
  • bridgeUserActions/bridge_load_page: -34%
  • bridgeUserActions/bridge_load_asset_picker: -64%
  • bridgeUserActions/bridge_search_token: -30%
  • bridgeUserActions/total: -35%
  • loadNewAccount/load_new_account: -43%
  • loadNewAccount/total: -43%
  • bridgeUserActions/bridge_load_page: +15%
  • bridgeUserActions/bridge_load_asset_picker: -49%
  • bridgeUserActions/bridge_search_token: -31%
  • bridgeUserActions/total: -28%

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

  • 🟡 loadNewAccount/FCP: p75 1.9s
  • 🟡 confirmTx/FCP: p75 1.9s
  • 🟡 bridgeUserActions/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/uiStartup: -29%
  • startupStandardHome/load: -25%
  • startupStandardHome/domContentLoaded: -25%
  • startupStandardHome/domInteractive: -13%
  • startupStandardHome/firstPaint: -36%
  • startupStandardHome/backgroundConnect: -51%
  • startupStandardHome/firstReactRender: -26%
  • startupStandardHome/loadScripts: -25%
  • startupStandardHome/setupStore: -14%
  • startupStandardHome/numNetworkReqs: -50%
  • startupStandardHome/load: +10%
  • startupStandardHome/domContentLoaded: +10%
  • startupStandardHome/domInteractive: -58%
  • startupStandardHome/backgroundConnect: +12%
  • startupStandardHome/firstReactRender: +17%
  • startupStandardHome/initialActions: +20%
  • startupStandardHome/setupStore: -51%
  • startupStandardHome/numNetworkReqs: -21%
  • startupPowerUserHome/uiStartup: -39%
  • startupPowerUserHome/domInteractive: -76%
  • startupPowerUserHome/backgroundConnect: -72%
  • startupPowerUserHome/setupStore: -85%
  • startupPowerUserHome/numNetworkReqs: -56%

🌐 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]🟢 [CI log]
onboardingNewWallet
[Sentry log · main/release]
🟢 [CI log]🟢 [CI log]
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: -82%
  • onboardingImportWallet/pwFormToMetricsScreen: +501%
  • onboardingImportWallet/metricsToWalletReadyScreen: -46%
  • onboardingImportWallet/doneButtonToHomeScreen: -83%
  • onboardingImportWallet/total: -50%
  • onboardingNewWallet/srpButtonToPwForm: -77%
  • onboardingNewWallet/createPwToRecoveryScreen: +1095%
  • onboardingNewWallet/skipBackupToMetricsScreen: -67%
  • onboardingNewWallet/doneButtonToAssetList: -47%
  • onboardingNewWallet/total: -40%
  • assetDetails/assetClickToPriceChart: -61%
  • assetDetails/total: -61%
  • solanaAssetDetails/assetClickToPriceChart: -77%
  • solanaAssetDetails/total: -77%
  • importSrpHome/loginToHomeScreen: -41%
  • importSrpHome/openAccountMenuAfterLogin: -78%
  • importSrpHome/homeAfterImportWithNewWallet: -68%
  • importSrpHome/total: -65%
  • swap/openSwapPageFromHome: -97%
  • swap/fetchAndDisplaySwapQuotes: +35%
  • swap/total: +12%

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

  • 🟡 assetDetails/INP: p75 240ms
  • 🟡 assetDetails/FCP: p75 1.9s
  • 🟡 solanaAssetDetails/FCP: p75 1.9s
  • 🟡 importSrpHome/FCP: p75 2.1s
  • 🟡 sendTransactions/FCP: p75 1.9s
  • 🟡 swap/FCP: p75 1.9s
  • 🟡 assetDetails/FCP: p75 1.8s
  • 🟡 sendTransactions/FCP: p75 1.8s
Dapp Page Load Benchmarks · Samples: 100
Benchmarkchrome-webpack
dappPageLoad
[Sentry log · main/release]
🟢 [CI log]

📈 Results compared to the previous 5 runs on main

  • dappPageLoad/pageLoadTime: +18%
Bundle size diffs
  • background: 31 Bytes (0%)
  • ui: 341 Bytes (0%)
  • common: 426 Bytes (0%)

@metamaskbotv2

metamaskbotv2 Bot commented May 20, 2026

Copy link
Copy Markdown
Contributor
Builds ready [31c928c] [reused from 872d338]
Deprecated Browserify fallback builds
⚡ Performance Benchmarks (Total: 🟢 13 pass · 🟡 11 warn · 🔴 0 fail)

Baseline (latest main): 51036da | Date: 5/2/2026 | Pipeline: 26167166178 | Baseline logs

Interaction Benchmarks · Samples: 5
Benchmarkchrome-webpackfirefox-webpack
loadNewAccount
[Sentry log · main/release]
🟡 [CI log]🟢 [CI log]
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: -71%
  • loadNewAccount/total: -71%
  • bridgeUserActions/bridge_load_page: -34%
  • bridgeUserActions/bridge_load_asset_picker: -64%
  • bridgeUserActions/bridge_search_token: -30%
  • bridgeUserActions/total: -35%
  • loadNewAccount/load_new_account: -43%
  • loadNewAccount/total: -43%
  • bridgeUserActions/bridge_load_page: +15%
  • bridgeUserActions/bridge_load_asset_picker: -49%
  • bridgeUserActions/bridge_search_token: -31%
  • bridgeUserActions/total: -28%

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

  • 🟡 loadNewAccount/FCP: p75 1.9s
  • 🟡 confirmTx/FCP: p75 1.9s
  • 🟡 bridgeUserActions/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/uiStartup: -29%
  • startupStandardHome/load: -25%
  • startupStandardHome/domContentLoaded: -25%
  • startupStandardHome/domInteractive: -13%
  • startupStandardHome/firstPaint: -36%
  • startupStandardHome/backgroundConnect: -51%
  • startupStandardHome/firstReactRender: -26%
  • startupStandardHome/loadScripts: -25%
  • startupStandardHome/setupStore: -14%
  • startupStandardHome/numNetworkReqs: -50%
  • startupStandardHome/load: +10%
  • startupStandardHome/domContentLoaded: +10%
  • startupStandardHome/domInteractive: -58%
  • startupStandardHome/backgroundConnect: +12%
  • startupStandardHome/firstReactRender: +17%
  • startupStandardHome/initialActions: +20%
  • startupStandardHome/setupStore: -51%
  • startupStandardHome/numNetworkReqs: -21%
  • startupPowerUserHome/uiStartup: -39%
  • startupPowerUserHome/domInteractive: -76%
  • startupPowerUserHome/backgroundConnect: -72%
  • startupPowerUserHome/setupStore: -85%
  • startupPowerUserHome/numNetworkReqs: -56%

🌐 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]🟢 [CI log]
onboardingNewWallet
[Sentry log · main/release]
🟢 [CI log]🟢 [CI log]
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: -82%
  • onboardingImportWallet/pwFormToMetricsScreen: +501%
  • onboardingImportWallet/metricsToWalletReadyScreen: -46%
  • onboardingImportWallet/doneButtonToHomeScreen: -83%
  • onboardingImportWallet/total: -50%
  • onboardingNewWallet/srpButtonToPwForm: -77%
  • onboardingNewWallet/createPwToRecoveryScreen: +1095%
  • onboardingNewWallet/skipBackupToMetricsScreen: -67%
  • onboardingNewWallet/doneButtonToAssetList: -47%
  • onboardingNewWallet/total: -40%
  • assetDetails/assetClickToPriceChart: -61%
  • assetDetails/total: -61%
  • solanaAssetDetails/assetClickToPriceChart: -77%
  • solanaAssetDetails/total: -77%
  • importSrpHome/loginToHomeScreen: -41%
  • importSrpHome/openAccountMenuAfterLogin: -78%
  • importSrpHome/homeAfterImportWithNewWallet: -68%
  • importSrpHome/total: -65%
  • swap/openSwapPageFromHome: -97%
  • swap/fetchAndDisplaySwapQuotes: +35%
  • swap/total: +12%

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

  • 🟡 assetDetails/INP: p75 240ms
  • 🟡 assetDetails/FCP: p75 1.9s
  • 🟡 solanaAssetDetails/FCP: p75 1.9s
  • 🟡 importSrpHome/FCP: p75 2.1s
  • 🟡 sendTransactions/FCP: p75 1.9s
  • 🟡 swap/FCP: p75 1.9s
  • 🟡 assetDetails/FCP: p75 1.8s
  • 🟡 sendTransactions/FCP: p75 1.8s
Dapp Page Load Benchmarks · Samples: 100
Benchmarkchrome-webpack
dappPageLoad
[Sentry log · main/release]
🟢 [CI log]

📈 Results compared to the previous 5 runs on main

  • dappPageLoad/pageLoadTime: +18%
Bundle size diffs
  • background: 31 Bytes (0%)
  • ui: 341 Bytes (0%)
  • common: 426 Bytes (0%)

Deduplicate background-event subscriptions in subscribeToMessengerEvent so
the UI registers at most one upstream messengerSubscribe IPC and one
background.onNotification listener per event type. Multiple local subscribers
share the in-flight subscribe promise; only the last unsubscribe sends the
upstream messengerUnsubscribe.

Per the conclusion of the PR #42573 discussion (r3259805738).

WPC-1005
…mock

Adds two short comments above the `eventName as NamespacedName` and
`callback as (data: Json) => void` casts to explain why each is safe.
Removes the now-unused `removeOnNotification` jest mock from the test
setup since the global notification router is never detached.

WPC-1005
Use object destructuring for notification.params and flip the entry-check
branch order to avoid a negated condition with else clause.

WPC-1005
Documents the Set semantics of the callbacks field so future maintainers
know that subscribing the same function twice collapses to a single slot.

WPC-1005
Verifies that replacing the background connection clears eventEntries
and resets the notification router, so a subsequent subscribe sends a
fresh upstream IPC against the new connection.

WPC-1005
@metamask-ci

metamask-ci Bot commented Jun 15, 2026

Copy link
Copy Markdown
Contributor
Builds ready [26fac36]
Deprecated Browserify fallback builds
⚡ Performance Benchmarks (Total: 🟢 18 pass · 🟡 7 warn · 🔴 0 fail)

Baseline (latest main): b6c5676 | Date: 6/15/2026 | Pipeline: 27546516887 | Baseline logs

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/load_new_account: +71%
  • loadNewAccount/total: +71%
  • loadNewAccount/inp: -10%
  • loadNewAccount/lcp: -17%
  • confirmTx/lcp: -61%
  • bridgeUserActions/bridge_load_asset_picker: +17%
  • bridgeUserActions/lcp: -12%
  • loadNewAccount/load_new_account: +722%
  • loadNewAccount/total: +722%
  • loadNewAccount/inp: +10%
  • loadNewAccount/lcp: +1041%
  • confirmTx/confirm_tx: +11%
  • confirmTx/longTaskCount: -100%
  • confirmTx/longTaskTotalDuration: -100%
  • confirmTx/longTaskMaxDuration: -100%
  • confirmTx/tbt: -100%
  • confirmTx/total: +11%
  • confirmTx/inp: +51%
  • confirmTx/lcp: +367%
  • bridgeUserActions/bridge_load_page: +85%
  • bridgeUserActions/bridge_load_asset_picker: +75%
  • bridgeUserActions/longTaskCount: -100%
  • bridgeUserActions/longTaskTotalDuration: -100%
  • bridgeUserActions/longTaskMaxDuration: -100%
  • bridgeUserActions/tbt: -100%
  • bridgeUserActions/total: +223%
  • bridgeUserActions/fcp: -50%
  • bridgeUserActions/lcp: +1194%
Startup Benchmarks · Samples: 100
Benchmarkchrome-webpackfirefox-webpack
startupStandardHome
[Sentry log · main/release]
🟢 [CI log]🟢 [CI log]
startupPowerUserHome
[Sentry log · main/release]
🟡 [CI log]🟡 [CI log]

📈 Results compared to the previous 5 runs on main

  • startupStandardHome/uiStartup: +12%
  • startupStandardHome/load: +11%
  • startupStandardHome/domContentLoaded: +12%
  • startupStandardHome/domInteractive: +17%
  • startupStandardHome/backgroundConnect: +12%
  • startupStandardHome/firstReactRender: +13%
  • startupStandardHome/loadScripts: +11%
  • startupStandardHome/setupStore: +10%
  • startupStandardHome/longTaskTotalDuration: +13%
  • startupStandardHome/longTaskMaxDuration: +13%
  • startupStandardHome/tbt: +16%
  • startupStandardHome/fcp: +12%
  • startupStandardHome/lcp: -19%
  • startupPowerUserHome/inp: +11%

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

  • 🔴 startupPowerUserHome/INP: p75 600ms
  • 🟡 startupPowerUserHome/LCP: p75 2.6s
User Journey Benchmarks · Samples: 5 · mock API
Benchmarkchrome-webpackfirefox-webpack
onboardingImportWallet
[Sentry log · main/release]
🟢 [CI log]🟢 [CI log]
onboardingNewWallet
[Sentry log · main/release]
🟢 [CI log]🟡 [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/doneButtonToHomeScreen: -89%
  • onboardingImportWallet/openAccountMenuToAccountListLoaded: -97%
  • onboardingImportWallet/longTaskCount: -79%
  • onboardingImportWallet/longTaskTotalDuration: -94%
  • onboardingImportWallet/longTaskMaxDuration: -92%
  • onboardingImportWallet/tbt: -99%
  • onboardingImportWallet/total: -88%
  • onboardingNewWallet/doneButtonToAssetList: -27%
  • onboardingNewWallet/longTaskCount: -50%
  • onboardingNewWallet/longTaskTotalDuration: -47%
  • onboardingNewWallet/longTaskMaxDuration: -13%
  • onboardingNewWallet/tbt: -41%
  • onboardingNewWallet/total: -24%
  • solanaAssetDetails/assetClickToPriceChart: -58%
  • solanaAssetDetails/longTaskCount: -100%
  • solanaAssetDetails/longTaskTotalDuration: -100%
  • solanaAssetDetails/longTaskMaxDuration: -100%
  • solanaAssetDetails/total: -58%
  • solanaAssetDetails/cls: -86%
  • importSrpHome/loginToHomeScreen: -11%
  • importSrpHome/openAccountMenuAfterLogin: +14%
  • importSrpHome/homeAfterImportWithNewWallet: -41%
  • importSrpHome/longTaskCount: -33%
  • importSrpHome/longTaskTotalDuration: -28%
  • importSrpHome/tbt: -26%
  • importSrpHome/total: -34%
  • importSrpHome/inp: -35%
  • importSrpHome/cls: -17%
  • sendTransactions/openSendPageFromHome: +17%
  • sendTransactions/selectTokenToSendFormLoaded: +30%
  • sendTransactions/longTaskCount: +11%
  • sendTransactions/tbt: +12%
  • sendTransactions/fcp: +16%
  • sendTransactions/lcp: +13%
  • sendTransactions/cls: -89%

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

  • 🟡 sendTransactions/INP: p75 240ms
  • 🟡 sendTransactions/FCP: p75 1.9s
  • 🟡 assetDetails/FCP: p75 1.9s
  • 🟡 solanaAssetDetails/FCP: p75 1.8s
  • 🟡 sendTransactions/FCP: p75 1.8s
Dapp Page Load Benchmarks · Samples: 100
Benchmarkchrome-webpack
dappPageLoad
[Sentry log · main/release]
🟢 [CI log]

📈 Results compared to the previous 5 runs on main

  • dappPageLoad/pageLoadTime: -15%
Bundle size diffs
  • background: 58 Bytes (0%)
  • ui: 5 Bytes (0%)
  • common: 395 Bytes (0%)

@cryptodev-2s
cryptodev-2s enabled auto-merge June 16, 2026 12:41
@cryptodev-2s
cryptodev-2s requested a review from GuillaumeRx June 16, 2026 13:44
Comment thread ui/store/background-connection.test.ts Outdated
Comment thread ui/store/background-connection.ts Outdated
The unsubscribe path just awaits the result, so a plain jest.fn works.
Only messengerSubscribe needs to return a promise because the code calls
.catch on it.

WPC-1005
@metamask-ci

metamask-ci Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor
Builds ready [2799aef]
Deprecated Browserify fallback builds
⚡ Performance Benchmarks (Total: 🟢 14 pass · 🟡 11 warn · 🔴 0 fail)

Baseline (latest main): 2f60258 | Date: 6/17/2026 | Pipeline: 27684756000 | Baseline logs

Interaction Benchmarks · Samples: 5
Benchmarkchrome-webpackfirefox-webpack
loadNewAccount
[Sentry log · main/release]
🟡 [CI log]🟡 [CI log]
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: -11%
  • confirmTx/inp: -17%
  • bridgeUserActions/tbt: +11%
  • bridgeUserActions/inp: -29%
  • loadNewAccount/load_new_account: +99%
  • loadNewAccount/total: +99%
  • loadNewAccount/inp: -20%
  • loadNewAccount/lcp: +1175%
  • confirmTx/longTaskCount: -100%
  • confirmTx/longTaskTotalDuration: -100%
  • confirmTx/longTaskMaxDuration: -100%
  • confirmTx/tbt: -100%
  • confirmTx/inp: -25%
  • confirmTx/lcp: +1184%
  • bridgeUserActions/bridge_load_page: +109%
  • bridgeUserActions/bridge_load_asset_picker: +76%
  • bridgeUserActions/longTaskCount: -100%
  • bridgeUserActions/longTaskTotalDuration: -100%
  • bridgeUserActions/longTaskMaxDuration: -100%
  • bridgeUserActions/tbt: -100%
  • bridgeUserActions/total: +20%
  • bridgeUserActions/inp: -36%
  • bridgeUserActions/fcp: -46%
  • bridgeUserActions/lcp: +1201%

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

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

📈 Results compared to the previous 5 runs on main

  • startupStandardHome/domInteractive: -18%
  • startupStandardHome/fcp: -18%
  • startupPowerUserHome/backgroundConnect: -29%
  • startupPowerUserHome/setupStore: +27%

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

  • 🟡 startupPowerUserHome/INP: p75 448ms
  • 🟡 startupPowerUserHome/LCP: p75 2.8s
User Journey Benchmarks · Samples: 5 · mock API
Benchmarkchrome-webpackfirefox-webpack
onboardingImportWallet
[Sentry log · main/release]
🟢 [CI log]🟢 [CI log]
onboardingNewWallet
[Sentry log · main/release]
🟢 [CI log]🟡 [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/metricsToWalletReadyScreen: -12%
  • onboardingImportWallet/doneButtonToHomeScreen: -90%
  • onboardingImportWallet/openAccountMenuToAccountListLoaded: -97%
  • onboardingImportWallet/longTaskCount: -74%
  • onboardingImportWallet/longTaskTotalDuration: -92%
  • onboardingImportWallet/longTaskMaxDuration: -90%
  • onboardingImportWallet/tbt: -98%
  • onboardingImportWallet/total: -89%
  • onboardingNewWallet/skipBackupToMetricsScreen: +21%
  • onboardingNewWallet/longTaskTotalDuration: -33%
  • onboardingNewWallet/longTaskMaxDuration: -31%
  • onboardingNewWallet/tbt: -64%
  • solanaAssetDetails/assetClickToPriceChart: -68%
  • solanaAssetDetails/longTaskCount: +67%
  • solanaAssetDetails/longTaskTotalDuration: +200%
  • solanaAssetDetails/longTaskMaxDuration: +200%
  • solanaAssetDetails/tbt: +326%
  • solanaAssetDetails/total: -68%
  • solanaAssetDetails/inp: +21%
  • solanaAssetDetails/cls: -88%
  • importSrpHome/loginToHomeScreen: -28%
  • importSrpHome/openAccountMenuAfterLogin: +19%
  • importSrpHome/homeAfterImportWithNewWallet: -37%
  • importSrpHome/longTaskCount: -31%
  • importSrpHome/longTaskTotalDuration: -29%
  • importSrpHome/longTaskMaxDuration: -10%
  • importSrpHome/tbt: -30%
  • importSrpHome/total: -33%
  • importSrpHome/inp: -38%
  • sendTransactions/selectTokenToSendFormLoaded: +14%
  • sendTransactions/longTaskCount: +11%
  • sendTransactions/longTaskTotalDuration: +16%
  • sendTransactions/tbt: +22%
  • sendTransactions/inp: -10%
  • sendTransactions/cls: -51%

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

  • 🟡 sendTransactions/INP: p75 224ms
  • 🟡 assetDetails/FCP: p75 1.9s
  • 🟡 solanaAssetDetails/FCP: p75 1.9s
  • 🟡 importSrpHome/FCP: p75 1.8s
  • 🟡 sendTransactions/FCP: p75 2.0s
Dapp Page Load Benchmarks · Samples: 100
Benchmarkchrome-webpack
dappPageLoad
[Sentry log · main/release]
🟢 [CI log]
Bundle size diffs [🚨 Warning! Bundle size has increased!]
  • background: 3.42 KiB (0.05%)
  • ui: 63.38 KiB (0.56%)
  • common: -23.76 KiB (-0.18%)

Key each subscription by a unique symbol instead of deduping callbacks in
a Set. Two subscribe calls that share the same function reference are now
independent registrations, so unsubscribing one no longer tears down the
other. This avoids a React footgun where two mounted components subscribing
with a stable handler would lose events when the first unmounts.

A shared callback now fires once per registration rather than once total,
which is the correct per subscription semantics.

WPC-1005
@metamask-ci

metamask-ci Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor
Builds ready [d9f75da]
Deprecated Browserify fallback builds
⚡ Performance Benchmarks (Total: 🟢 15 pass · 🟡 10 warn · 🔴 0 fail)

Baseline (latest main): 9724737 | Date: 6/17/2026 | Pipeline: 27687478363 | Baseline logs

Interaction Benchmarks · Samples: 5
Benchmarkchrome-webpackfirefox-webpack
loadNewAccount
[Sentry log · main/release]
🟢 [CI log]🟢 [CI log]
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: +25%
  • confirmTx/longTaskMaxDuration: -12%
  • bridgeUserActions/tbt: -14%
  • bridgeUserActions/inp: -21%
  • loadNewAccount/load_new_account: +101%
  • loadNewAccount/total: +101%
  • loadNewAccount/inp: +34%
  • loadNewAccount/fcp: -48%
  • loadNewAccount/lcp: +1157%
  • confirmTx/confirm_tx: +10%
  • confirmTx/longTaskCount: -100%
  • confirmTx/longTaskTotalDuration: -100%
  • confirmTx/longTaskMaxDuration: -100%
  • confirmTx/tbt: -100%
  • confirmTx/total: +10%
  • confirmTx/inp: -13%
  • confirmTx/fcp: -48%
  • confirmTx/lcp: +1171%
  • bridgeUserActions/bridge_load_page: +96%
  • bridgeUserActions/bridge_load_asset_picker: +37%
  • bridgeUserActions/longTaskCount: -100%
  • bridgeUserActions/longTaskTotalDuration: -100%
  • bridgeUserActions/longTaskMaxDuration: -100%
  • bridgeUserActions/tbt: -100%
  • bridgeUserActions/total: +14%
  • bridgeUserActions/inp: -36%
  • bridgeUserActions/lcp: +1157%

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

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

📈 Results compared to the previous 5 runs on main

  • startupStandardHome/uiStartup: -21%
  • startupStandardHome/load: -20%
  • startupStandardHome/domContentLoaded: -21%
  • startupStandardHome/domInteractive: -22%
  • startupStandardHome/backgroundConnect: -19%
  • startupStandardHome/firstReactRender: -23%
  • startupStandardHome/initialActions: -33%
  • startupStandardHome/loadScripts: -21%
  • startupStandardHome/setupStore: -24%
  • startupStandardHome/longTaskCount: -33%
  • startupStandardHome/longTaskTotalDuration: -27%
  • startupStandardHome/longTaskMaxDuration: -23%
  • startupStandardHome/tbt: -27%
  • startupStandardHome/inp: -18%
  • startupStandardHome/fcp: -20%
  • startupStandardHome/lcp: -12%
  • startupStandardHome/domInteractive: +17%
  • startupStandardHome/backgroundConnect: +10%
  • startupStandardHome/setupStore: +15%
  • startupStandardHome/fcp: +17%
  • startupPowerUserHome/backgroundConnect: +26%
  • startupPowerUserHome/firstReactRender: +12%
  • startupPowerUserHome/setupStore: +26%
  • startupPowerUserHome/inp: +11%

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

  • 🟡 startupPowerUserHome/INP: p75 432ms
  • 🟡 startupPowerUserHome/LCP: p75 2.8s
User Journey Benchmarks · Samples: 5 · mock API
Benchmarkchrome-webpackfirefox-webpack
onboardingImportWallet
[Sentry log · main/release]
🟢 [CI log]🟢 [CI log]
onboardingNewWallet
[Sentry log · main/release]
🟢 [CI log]🟡 [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/doneButtonToHomeScreen: -89%
  • onboardingImportWallet/openAccountMenuToAccountListLoaded: -64%
  • onboardingImportWallet/longTaskCount: -75%
  • onboardingImportWallet/longTaskTotalDuration: -91%
  • onboardingImportWallet/longTaskMaxDuration: -87%
  • onboardingImportWallet/tbt: -96%
  • onboardingImportWallet/total: -86%
  • onboardingNewWallet/createWalletToSocialScreen: +11%
  • onboardingNewWallet/srpButtonToPwForm: +31%
  • onboardingNewWallet/createPwToRecoveryScreen: +31%
  • onboardingNewWallet/skipBackupToMetricsScreen: +32%
  • onboardingNewWallet/agreeButtonToOnboardingSuccess: +38%
  • onboardingNewWallet/longTaskCount: +50%
  • onboardingNewWallet/longTaskTotalDuration: +62%
  • onboardingNewWallet/longTaskMaxDuration: +22%
  • onboardingNewWallet/tbt: +70%
  • solanaAssetDetails/assetClickToPriceChart: -54%
  • solanaAssetDetails/longTaskCount: -100%
  • solanaAssetDetails/longTaskTotalDuration: -100%
  • solanaAssetDetails/longTaskMaxDuration: -100%
  • solanaAssetDetails/tbt: -100%
  • solanaAssetDetails/total: -54%
  • solanaAssetDetails/inp: +15%
  • solanaAssetDetails/cls: -84%
  • importSrpHome/loginToHomeScreen: -16%
  • importSrpHome/openAccountMenuAfterLogin: +57%
  • importSrpHome/homeAfterImportWithNewWallet: -37%
  • importSrpHome/longTaskCount: -31%
  • importSrpHome/longTaskTotalDuration: -35%
  • importSrpHome/longTaskMaxDuration: -13%
  • importSrpHome/tbt: -38%
  • importSrpHome/total: -32%
  • importSrpHome/inp: -40%
  • importSrpHome/lcp: -64%
  • sendTransactions/longTaskCount: +11%
  • sendTransactions/inp: -31%
  • sendTransactions/cls: -75%

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

  • 🟡 importSrpHome/FCP: p75 1.8s
  • 🟡 assetDetails/FCP: p75 1.8s
  • 🟡 solanaAssetDetails/FCP: p75 1.9s
  • 🟡 importSrpHome/FCP: p75 1.8s
  • 🟡 sendTransactions/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: 3.42 KiB (0.05%)
  • ui: 63.38 KiB (0.56%)
  • common: -23.71 KiB (-0.17%)

Comment thread ui/store/background-connection.ts
@cryptodev-2s
cryptodev-2s added this pull request to the merge queue Jun 17, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Jun 17, 2026
@cryptodev-2s
cryptodev-2s added this pull request to the merge queue Jun 17, 2026
Merged via the queue into main with commit 8bd65b1 Jun 17, 2026
199 of 201 checks passed
@cryptodev-2s
cryptodev-2s deleted the cryptodev-2s/wpc-1005-shared-subscription-per-event-type branch June 17, 2026 17:12
@github-actions github-actions Bot locked and limited conversation to collaborators Jun 17, 2026
@metamaskbot metamaskbot added the release-13.37.0 Issue or pull request that will be included in release 13.37.0 label Jun 17, 2026
@MajorLift

MajorLift commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

🧪 Validation Run

Verdict: ⚠️ one new listener with no release in the file — Claim: sharing a single UI subscription per messenger event removes duplicate work without changing lifecycle. head 8bd65b126cc · 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.

Every retention primitive the diff adds, 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: every retention primitive this diff introduces has a paired release

$ sh -c git diff 8bd65b126ccaf4ce2a96cd8abb7d31ac52f3f4ac^ 8bd65b126ccaf4ce2a96cd8abb7d31ac52f3f4ac > d.patch && python3 .evidence-skills/domains/stability/skills/memory-leak/scripts/retention-scan.py ui/store/background-connection.ts:d.patch
RETENTION REVIEW — scoped to the supplied diff  (re-run: retention-scan.py <file>:<patch> [...])
==========================================================================

background-connection.ts
  [NEW      ] OPEN L154: background.onNotification(routeMessengerEventNotification)
                   -> no removeOnNotification/offNotification in file

VERDICT: 1 NEW un-paired primitive(s) → escalate to a heap snapshot (Phase 2)

Produced by capture.sh, not transcribed. head 8bd65b126ccaf4ce2a96cd8abb7d31ac52f3f4ac · 0 tracked changes · node v22.23.1 · Python 3.12.3 · yarn.lock 673ee49998dc0a8d. Run: https://github.com/MajorLift/metamask-skills/actions/runs/30765813337 — logs and artifacts attached there.

Follows from the scan above

  • background-connection.ts#L154 registers background.onNotification(routeMessengerEventNotification) and the file contains no removeOnNotification or offNotification.
  • The other primitives in the file are pre-existing and paired; this is the only one the diff introduces.
  • Sharing one subscription is what makes it durable by design: the single listener is meant to outlive individual consumers, so "no release" may be the intent rather than an omission.

Open for review: whether that listener's lifetime is bounded by something outside this file — a port teardown, a page unload, a controller disposal. A static pass sees a file, not a lifecycle, so it can say the pair is absent here and not that the listener leaks. If the connection is per-session and the page owns it, this is correct as written; if a connection can be replaced while the page lives, each replacement adds a listener.

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

Labels

release-13.37.0 Issue or pull request that will be included in release 13.37.0 risk:medium size-M team-core-platform Core Platform team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants