Skip to content

fix(store): filter unsupported chains out of read paths for addedSafes - #8187

Closed
TenderDeve (tenderdeve) wants to merge 1 commit into
safe-global:devfrom
tenderdeve:fix/2585-sanitise-chain-store
Closed

fix(store): filter unsupported chains out of read paths for addedSafes#8187
TenderDeve (tenderdeve) wants to merge 1 commit into
safe-global:devfrom
tenderdeve:fix/2585-sanitise-chain-store

Conversation

@tenderdeve

@tenderdeve TenderDeve (tenderdeve) commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

What it solves

Resolves: #2585

Persisted chainId-keyed slices (addedSafes, address book, undeployed Safes, pinned Safe apps, …) hold entries for chains the gateway no longer lists. Consumers that iterate the raw maps surface stale state — this was the original trigger behind the linked incident in #2584.

Maintainer guidance on the issue was to filter at READ time rather than mutate localStorage. There was already a private _sanitizeNotifiableSafes doing exactly this for push-notification registration. This PR lifts that into a shared util and starts wiring it through the rest of the read paths.

How this PR fixes it

  • apps/web/src/utils/chainEntries.ts — new generic pickSupportedChainEntries(byChainId, chains) util (kept out of utils/chains.ts to avoid pulling the store-init graph back into the slice file).
  • addedSafesSlice — new memoised selectAllAddedSafesOnSupportedChains(state, chains) selector.
  • LocalSafesAlert (Spaces sign-in entry point) routed through the sanitised selector so the on-this-browser count stops including Safes on chains the gateway dropped.
  • _sanitizeNotifiableSafes collapsed to delegate to the shared util — no behavioural change, just removes the duplicate.

First slice; the same pickSupportedChainEntries + per-slice memoised selector pattern can be extended to addressBook, undeployedSafes, pinnedSafeApps, visitedSafes, etc. in follow-ups. Doing them all in one PR would touch ~150 call sites and make review hard.

How to test it

  1. Add a Safe on a chain the gateway no longer returns (or temporarily filter the chain out of the useChains mock).
  2. Open the Spaces sign-in screen.
  3. Before: LocalSafesAlert counts the stale-chain Safe. After: it's excluded; the count matches the actual usable list.

Unit tests cover the util's filter / no-mutation / empty-chains semantics, the new sanitised selector, and the updated alert behaviour.

Affected flows

  • Spaces sign-in → LocalSafesAlert count

Blast radius

  • New util file + new selector; existing public selectors untouched
  • _sanitizeNotifiableSafes keeps its signature, only its body now reuses the util
  • LocalSafesAlert is the only consumer migrated to the sanitised selector
  • No analytics / route / persisted state / RTK Query changes
  • Mobile unaffected (web-only files)

Risks / not checked

  • Other consumers (useAllSafes, AddAccounts, useOnboardingSafes, DataWidget, …) still read raw selectAllAddedSafes — incremental follow-ups
  • Sanitised selector is parametrised by the supported chains array; consumers must pass useChains().configs (or equivalent) for the filter to engage
  • Did not test on mobile (Spaces is web-only)

Visual summary

flowchart LR
  A["addedSafes slice (persisted, chainId-keyed)"] --> B["selectAllAddedSafes"]
  B --> C["direct consumers — stale-chain entries leak through"]
  A --> D["selectAllAddedSafesOnSupportedChains(state, chains)"]
  D --> E["pickSupportedChainEntries util"]
  E --> F["LocalSafesAlert count — stale-chain Safes dropped"]
Loading

Checklist

  • I've tested the branch on mobile 📱 — n/a, web-only files
  • I've documented how it affects the analytics (if at all) 📊 — none
  • I've written a unit/e2e test for it (if applicable) 🧑‍💻 — util, sanitised selector, alert
  • I've listed affected flows and blast radius, and named what I did not verify 🎯

CLA signature

I have read and hereby sign the Contributor License Agreement.

Persisted chainId-keyed slices (addedSafes, address book, undeployed
Safes, …) hold entries for chains the gateway no longer lists, so
consumers that iterate the raw maps surface stale state — see safe-global#2584.

Add a generic pickSupportedChainEntries util and a memoised
selectAllAddedSafesOnSupportedChains selector, then route the local
Safes alert through it so the on-this-browser count stops including
stale-chain Safes. Refactor _sanitizeNotifiableSafes to delegate to the
shared util.

This is the first slice — follow-ups can extend the same pattern to
the address book, pinned Safe apps, undeployed Safes, etc., without
mutating localStorage.

Resolves safe-global#2585
@Zalens

Copy link
Copy Markdown
Member

Thanks TenderDev, I am closing this one. LocalSafesAlert was deleted in #8271 (20 Jul), and that was the only part here with a visible effect; the rest has no consumer.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Sanitise chain-specific store entries according to supported chains

2 participants