MetaMask / MetaMask/metamask-mobile

Memoize useERC20Tokens assetIds input and returned array

Open Beginner friendly
#31,377 1 comment 0 reactions 0 assignees View on GitHub
area-performance Sev3 size-S ta-triaged team-confirmations 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: Easy · Fix risk: Simple · Test safety net: Covered (app/components/hooks/DisplayName/useERC20Tokens.test.ts)
> Owner: `@MetaMask/confirmations (suggested)`
> File: `app/components/hooks/DisplayName/useERC20Tokens.ts:8`

### What is this about?

`useERC20Tokens` builds a fresh `assetIds` array (`.filter().map()`) on every render and passes it to `useTokensData`, then returns a fresh `requests.map(...)` array — neither is memoized.
```ts
const assetIds = requests
.filter(({ type, value }) => type === NameType.EthereumAddress && value)
.map(({ value, variation }) =>
buildEvmCaip19AssetId(value as string, variation as Hex),
); // fresh array every render

const tokensByAssetId = useTokensData(assetIds); // re-derives from a new array each render

return requests.map(({ preferContractSymbol, type, value, variation }) => {
...
return { name, image: token?.iconUrl }; // fresh objects + fresh array every render
});
```

**Why it matters**

This sub-hook is aggregated by `useDisplayNames`, which runs per rendered name/address (confirmations, simulation rows, transaction lists, per virtualized row). The returned array — and the inner `{ name, image }` objects — are new references on every render, defeating downstream memoization the wave-1 `useDisplayNames` finding aims to restore. `useTokensData` internally keys its effect on the joined string (`assetIds.join(',')`) so the network side is safe, but the recreated `assetIds` array and the fresh return array/objects still churn references on every render and re-run `buildEvmCaip19AssetId` for every request twice per render (once for `assetIds`, once in the return map).

### Scenario

N/A — see Technical Details.

### Design

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

### Technical Details

**Evidence**

`app/components/hooks/DisplayName/useERC20Tokens.ts:8-12` (fresh `assetIds`), `:16-27` (fresh return array of fresh objects). No `useMemo` anywhere in the hook.

**Fix**

- Memoize `assetIds` with `useMemo` keyed on `[requests]`.
- Memoize the final `requests.map(...)` with `useMemo` keyed on `[requests, tokensByAssetId]` so the returned array and its objects are stable when inputs are unchanged.

### 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/hooks/DisplayName/useERC20Tokens.test.ts`.
- Add a test rendering twice with identical `requests` and the same `useTokensData` result; assert the returned array reference is `===` across renders.

### References

- File: `app/components/hooks/DisplayName/useERC20Tokens.ts:8`
- Source: MetaMask Mobile performance audit — finding `unstablehook-useerc20tokens-fresh-assetids-and-array`
- Owner (CODEOWNERS / best-effort): @MetaMask/confirmations (suggested)
- Status: **UNVALIDATED**

Contributor guide

Open the contributing guide

Research direction

Start with app/components/hooks/DisplayName/useERC20Tokens.ts and inspect the existing coverage in app/components/hooks/DisplayName/useERC20Tokens.test.ts. Memoize the assetIds calculation and returned mapping as described, then run yarn jest app/components/hooks/DisplayName/useERC20Tokens.test.ts and verify that identical requests and useTokensData results preserve the returned array reference across renders.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
performance, testing-qa
Issue type
Refactor
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
65/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.