MetaMask / MetaMask/metamask-mobile
Audit parameterized selectors with single-entry caches across `app/selectors` (51 hits, 14 files)
- Dominant language
- TypeScript
- Stars
- 3k
- Forks
- 1.7k
- Avg merge
- 1d 14h
- Merged PRs (30d)
- 669
Description
> **Performance audit finding** · Severity: **Medium (batch)** · Effort: Large · Fix risk: Simple per instance · Test safety net: Partial
> Owner: per-file CODEOWNERS
> Scope: `app/selectors/` (51 hits, 14 files)
### What is this about?
Parameterized selectors (`(_state, arg) => arg` input pattern) built with `createSelector` have a **single-entry cache**: calls with alternating arguments from different components bust the cache on every call, so the selector recomputes per call site per render despite being "memoized". Sweep results on `main` (`b3e7eb88`), hits per file:
- [ ] `app/selectors/earnController/earn/index.ts` (6)
- [ ] `app/selectors/tokensController.ts` (5) — worst instance filed separately: #31490
- [ ] `app/selectors/tokenRatesController.ts` (4)
- [ ] `app/selectors/networkController.ts` (4)
- [ ] `app/selectors/currencyRateController.ts` (4)
- [ ] `app/selectors/smartTransactionsController.ts` (3)
- [ ] `app/selectors/multisrp/index.ts` (3)
- [ ] `app/selectors/transactionController.ts` (2)
- [ ] `app/selectors/tokenSearchDiscoveryDataController.ts` (2)
- [ ] `app/selectors/multichainAccounts/accounts.ts` (2)
- [ ] `app/selectors/assets/assets-list.ts` (2)
- [ ] `app/selectors/accountsController.ts` (2) — compound instance filed separately: #31491
- [ ] `app/selectors/transactionPayController.ts` (1)
- [ ] `app/selectors/snaps/permissionController.ts` (1)
Detection recipe: `grep -rn "(_state\|(_," app/selectors --include="*.ts"`
### Technical Details
Per instance, triage in this order: (1) **lookup-map selector** — memoize one keyed index, consumers key into it (preferred; removes per-arg caching entirely); (2) **factory selector** instantiated per call site with `useMemo`; (3) `lruMemoize` with `maxSize` (last resort). Instances called from a single call site with a stable argument are fine — close them as no-op with a note.
### Acceptance Criteria
- Each file's hits triaged: fixed (with reference-stability test) or explicitly waived (single stable call site).
- No parameterized selector consumed per-row in a list retains a single-entry cache.
### References
- Source: `mms-performance` sweep recipes added in MetaMask/skills#49 (`mm-state-normalization`); pattern catalogued in the extension audit as MetaMask-planning#6484
- Status: **UNVALIDATED** per instance (static sweep; needs per-instance review)
Contributor guide
Research direction
Start with the provided grep recipe, then inspect the 14 listed files and each selector's call sites. Triage each hit as a lookup-map selector, factory selector, lruMemoize case, or stable single-call-site no-op; add reference-stability tests for fixes and document waived instances. Done means all 51 hits are fixed or explicitly waived, with no per-row single-entry cache remaining.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- mobile, performance
- Issue type
- Refactor
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100