MetaMask / MetaMask/metamask-mobile

Replace inline permitted-accounts selector + isEqual in DaimoPayModal

Open
#31,331 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

area-performance Sev3 size-S ta-triaged team-card team-mobile-platform
Dominant language
TypeScript
Stars
3k
Forks
1.7k
Avg merge
1d 14h
Merged PRs (30d)
669

Description

Performance audit finding · Severity: Medium · Effort: Hard · Fix risk: Risky · Test safety net: Partial (app/components/UI/Card/components/DaimoPayModal/DaimoPayModal.test.tsx exists; unclear it asserts selector stability)
Owner: @MetaMask/card
File: app/components/UI/Card/components/DaimoPayModal/DaimoPayModal.tsx:92

What is this about?

DaimoPayModal reads permitted accounts via an inline useSelector callback that calls getPermittedEvmAddressesByHostname(...) and is stabilized with isEqual. The inline selector derives a new array every invocation (it computes the permitted-address list on the fly), so without a memoized selector each Redux dispatch recomputes the list and the isEqual comparator deep-compares the result to suppress re-renders.

Why it matters

This pattern runs the derivation plus a deep isEqual on every store dispatch while the modal is open. getPermittedEvmAddressesByHostname is not memoized per-hostname here, so the work is repeated and isEqual is a band-aid covering the missing parametrized reselect selector. It also recomputes daimoOrigin (a new URL(...)) inline each render, feeding an unstable arg into the selector.

Scenario

N/A — see Technical Details.

Design

N/A — internal performance change; no UI/design impact.

Technical Details

Evidence

app/components/UI/Card/components/DaimoPayModal/DaimoPayModal.tsx:92

const permittedAccountsList = useSelector((state: RootState) => {
  const permissionsControllerState = selectPermissionControllerState(state);
  return getPermittedEvmAddressesByHostname(
    permissionsControllerState,
    daimoOrigin,
  );
}, isEqual);

daimoOrigin is computed inline just above (new URL(webViewUrl).origin).

Fix

Create a memoized parametrized selector, e.g. selectPermittedEvmAddressesByHostname(state, hostname) built with createSelector (using weakMapMemoize/argsMemoize so different hostnames cache independently). Memoize daimoOrigin with useMemo([payId]), then useSelector(state => selectPermittedEvmAddressesByHostname(state, daimoOrigin)) without isEqual. The selector returns a stable reference for unchanged inputs, eliminating the deep compare.

Threat Modeling Framework

N/A — performance-only change; behavior is preserved, no new data flow / trust boundary / attack surface.

Acceptance Criteria
    • Run yarn jest app/components/UI/Card/components/DaimoPayModal/DaimoPayModal.test.tsx.
  • Add a selector test asserting a stable reference for the same hostname across unrelated state changes.
References
  • File: app/components/UI/Card/components/DaimoPayModal/DaimoPayModal.tsx:92
  • Source: MetaMask Mobile performance audit — finding redux-daimopay-isequal-inline-selector
  • Owner (CODEOWNERS / best-effort): @MetaMask/card
  • Status: UNVALIDATED

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with app/components/UI/Card/components/DaimoPayModal/DaimoPayModal.tsx around line 92 and review the existing DaimoPayModal.test.tsx coverage. Run yarn jest app/components/UI/Card/components/DaimoPayModal/DaimoPayModal.test.tsx before changing the selector. Done means the parametrized selector and memoized daimoOrigin avoid isEqual, and a selector test confirms stable references across unrelated state changes.

Written by the indexing model from the issue text.

Assessment

Tech stack
react, typescript
Domain
performance, testing-qa
Issue type
Refactor
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.