Performance audit finding · Severity: Medium · Effort: Small-Medium · Fix risk: Simple · Test safety net: Partial
Owner: Earn / Bridge CODEOWNERS
Files: app/selectors/earnController/earn/index.ts:122, app/selectors/bridgeStatusController/index.ts:14
What is this about?
A per-selector triage of the ten highest-subscription components (51 selector definitions read; ruled-out verdicts in this issue's edit history and #31493) left two selectors standing — both allocate heavily on hot, always-visible paths:
selectEarnTokens (app/selectors/earnController/earn/index.ts:122) — a createDeepEqualSelector that rebuilds a large nested structure (earn tokens × markets × chains via .reduce) on every recompute, and deep-compares its sizable inputs on every check. Consumed by both WalletActions and TradeWalletActions, which sit on the primary action surfaces.
selectBridgeHistoryForAccount (app/selectors/bridgeStatusController/index.ts:14) — a plain createSelector whose result function allocates a fresh object via .reduce per recompute, handing consumers a new reference whenever the upstream recomputes even if the history is unchanged.
Why it matters
Both costs scale with power-user data (token count, bridge history length) and run at the batched-flush cadence for as long as their screens are mounted. These are the confirmed offenders out of 80+ candidate reads — the rest were verified stable/cheap and ruled out.
Scenario
N/A — see Technical Details.
Design
N/A — internal performance change; no UI/design impact.
Technical Details
For selectEarnTokens: narrow the inputs to the slices that actually change, split the mega-aggregation into composed memoized layers so a price tick doesn't rebuild the world, and verify whether the deep-equal input compare is still needed once inputs are narrow (see the reference-stability contract test in the mms-performance skill's mm-selector-cascade). For selectBridgeHistoryForAccount: stabilize the output with a resultEqualityCheck or narrow inputs so recomputes track actual history changes.
Threat Modeling Framework
N/A — performance-only change; behavior is preserved, no new data flow / trust boundary / attack surface.
Acceptance Criteria
- Both selectors return the same reference across unrelated flushes with unchanged data (assert
toBe across two equal-content states).
- Profiler on a power-user profile:
WalletActions/TradeWalletActions and bridge history consumers stop re-rendering on unrelated dispatches.
References
What is this about?
A per-selector triage of the ten highest-subscription components (51 selector definitions read; ruled-out verdicts in this issue's edit history and #31493) left two selectors standing — both allocate heavily on hot, always-visible paths:
selectEarnTokens(app/selectors/earnController/earn/index.ts:122) — acreateDeepEqualSelectorthat rebuilds a large nested structure (earn tokens × markets × chains via.reduce) on every recompute, and deep-compares its sizable inputs on every check. Consumed by bothWalletActionsandTradeWalletActions, which sit on the primary action surfaces.selectBridgeHistoryForAccount(app/selectors/bridgeStatusController/index.ts:14) — a plaincreateSelectorwhose result function allocates a fresh object via.reduceper recompute, handing consumers a new reference whenever the upstream recomputes even if the history is unchanged.Why it matters
Both costs scale with power-user data (token count, bridge history length) and run at the batched-flush cadence for as long as their screens are mounted. These are the confirmed offenders out of 80+ candidate reads — the rest were verified stable/cheap and ruled out.
Scenario
N/A — see Technical Details.
Design
N/A — internal performance change; no UI/design impact.
Technical Details
For
selectEarnTokens: narrow the inputs to the slices that actually change, split the mega-aggregation into composed memoized layers so a price tick doesn't rebuild the world, and verify whether the deep-equal input compare is still needed once inputs are narrow (see the reference-stability contract test in themms-performanceskill'smm-selector-cascade). ForselectBridgeHistoryForAccount: stabilize the output with aresultEqualityCheckor narrow inputs so recomputes track actual history changes.Threat Modeling Framework
N/A — performance-only change; behavior is preserved, no new data flow / trust boundary / attack surface.
Acceptance Criteria
toBeacross two equal-content states).WalletActions/TradeWalletActionsand bridge history consumers stop re-rendering on unrelated dispatches.References
app/selectors/earnController/earn/index.ts:122,app/selectors/bridgeStatusController/index.ts:14selectLocalTransactions#31499 (selectLocalTransactions, split out as dedicated), Fix 3 per-row parameterized-selector cache misses inTokenListItem(fresh object-literal args) #31492 (per-row parameterized misses), Audit the 19useSelectorreads on Wallet home for expensive and unstable selectors #31493 (Wallet home ruled out)mms-performanceper-selector triage (perf: Augmentmms-performancewith frontend learnings from the Extension performance audit skills#49)