Skip to content

fix(ens): warm-cache resolved names to stop the address→name flash on mount - #2573

Merged
jjramirezn merged 3 commits into
mainfrom
fix/ens-name-warm-cache
Jul 29, 2026
Merged

fix(ens): warm-cache resolved names to stop the address→name flash on mount#2573
jjramirezn merged 3 commits into
mainfrom
fix/ens-name-warm-cache

Conversation

@kushagrasarathe

@kushagrasarathe kushagrasarathe commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Summary

Follow-up to #2565 (reported by Kush): backgrounding the app and returning reloads the page on mobile, so history briefly shows raw addresses before ENS names pop in. The in-memory react-query cache dies with the page, and every cold mount pays the /ens/reverse 404 round-trip before the client fallback resolves.

Fix: persist resolved names in localStorage (ens-primary-name-cache, 24h TTL). On mount the cached name paints immediately while the fresh lookup revalidates in the background.

Staleness safety (from the multi-agent review pass):

  • Authoritative "no name" from either path masks and evicts the cache entry: server null/'', or — critical while the backend route is undeployed — the client lookup settling '' (JustaName's not-found). A released/transferred ENS name cannot keep painting from cache once a lookup settles.
  • AddressLink now resets its href on the name→address downgrade (pre-existing one-way effect, previously unreachable; the warm cache made it a live path). Prevents navigating to a name's new owner from a row showing the raw address.
  • Malformed cache roots ("null", arrays) are treated as empty (CodeRabbit).

Design notes / accepted trade-offs

  • During the pending window a cached name is shown (and linkable) before revalidation settles — that's the point of the warm cache; worst case is bounded by the 24h TTL and by eviction-on-settle.
  • No cross-tab storage listener: another tab's eviction isn't observed until next mount. Accepted — display-only label, listener plumbing isn't earning its complexity.
  • Cache blob is unbounded (grows with distinct counterparties, entries expire logically at 24h). Practically dozens of entries; add pruning if it ever matters.
  • Best-effort writes: quota/blocked storage errors are swallowed.

Risks / breaking changes

QA

  • npm run typecheck ✅ · npm test ✅ (2235 passed; hook suite 9 tests: warm-paint, server-null eviction, client-'' eviction, both-fail, fallbacks) · prettier ✅
  • Manual: open history → background the app → return: name shows instantly (after one prior successful resolution).

Screenshots: N/A (timing behavior, not layout)

Summary by CodeRabbit

  • New Features

    • Added faster display of previously resolved address names while lookups are in progress.
    • Cached address names now expire after one day and update automatically when fresh results arrive.
  • Bug Fixes

    • Removed outdated cached names when an address has no confirmed name.
    • Ensured address links fall back to the correct raw address when name resolution fails.

…lash

a cold mount (mobile reopening the pwa reloads the page) showed the raw
address for a beat while the lookup ran — pay the 404, engage the client
fallback, resolve, flip. persist resolved names for 24h and paint them
immediately on mount while the lookup revalidates. an authoritative
server "no name" evicts the entry so stale names can't mask reality.
@vercel

vercel Bot commented Jul 29, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
peanut-wallet Ready Ready Preview, Comment Jul 29, 2026 3:01pm

Request Review

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

usePrimaryNameServer now uses a TTL-based localStorage cache for immediate primary-name display, updates or evicts entries after resolution, distinguishes server null from failures, and keeps address links aligned with valid ENS names.

Changes

Primary-name cache

Layer / File(s) Summary
Cache storage utilities
src/hooks/usePrimaryNameServer.ts
Adds guarded localStorage access, normalized cache keys, timestamped entries, TTL expiration, and cache updates.
Resolution flow and cache validation
src/hooks/usePrimaryNameServer.ts, src/hooks/__tests__/usePrimaryNameServer.test.tsx
Uses server or client results authoritatively, suppresses cached names after settled no-name results, and tests warm-cache rendering, eviction, and storage isolation.
Address-link target synchronization
src/components/Global/AddressLink/index.tsx
Resets the link target to the raw address when ENS normalization is unavailable.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Hook
  participant Cache
  participant Server
  participant Client
  Hook->>Cache: Read cached name
  Hook->>Server: Request primary name
  Server-->>Hook: Return name or error
  Hook->>Client: Resolve fallback
  Client-->>Hook: Return name or empty result
  Hook->>Cache: Write or evict cache entry
Loading

Possibly related PRs

Suggested reviewers: innolope-dev

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly captures the main ENS warm-cache fix and the mount-time address-to-name flash it addresses.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/ens-name-warm-cache

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Code-analysis diff

Painscore total: 6306.8 → 6307.65 (+0.85)
Findings: 0 net (+3 new, -3 resolved)

🆕 New findings (3)

  • high complexity — src/hooks/usePrimaryNameServer.ts — CC 33, MI 58.08, SLOC 86
  • medium complexity — src/components/Global/AddressLink/index.tsx — CC 9, MI 62.01, SLOC 28
  • low high-mdd — src/hooks/usePrimaryNameServer.ts:69 — usePrimaryNameServer: MDD 17.4 (uses across many lines from declarations)

✅ Resolved (3)

  • src/components/Global/AddressLink/index.tsx — CC 9, MI 62.47, SLOC 27
  • src/hooks/usePrimaryNameServer.ts — CC 8, MI 60.13, SLOC 32
  • src/hooks/usePrimaryNameServer.ts:1 — 'use client' but no hooks/handlers/browser API

📈 Painscore deltas (top movers)

File Before After Δ
src/hooks/usePrimaryNameServer.ts 7.4 8.1 +0.7

@github-actions

github-actions Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

🧪 UI test report — ✅ all green

Suites

  • unit: 2238 ran, 0 failed, 0 skipped, 39.4s

📊 Coverage (unit)

metric %
statements 61.7%
branches 45.3%
functions 51.1%
lines 62.2%
⏱ 10 slowest test cases
time test
3.7s src/components/Card/share-asset/__tests__/shareAssetLayout.test.ts › never places two stickers in heavy overlap (broad seed sweep)
1.0s src/utils/__tests__/demo-api.test.ts › isDemoMode() is false when not running under Capacitor
0.4s src/components/Card/share-asset/__tests__/shareAssetLayout.test.ts › every sticker stays within canvas at any count
0.3s src/app/actions/__tests__/api-headers.test.ts › should include Content-Type in validateInviteCode
0.3s src/app/(mobile-ui)/withdraw/__tests__/withdraw-states.test.tsx › Bank withdrawal keeps the $1 minimum for sub-$1 amounts
0.3s src/app/actions/__tests__/api-headers-extended.test.ts › should not include apiKey in validateInviteCode body
0.3s src/utils/__tests__/demo-balance.test.ts › debits and floors at zero
0.2s src/utils/__tests__/url.utils.test.ts › uses the public BASE_URL in Capacitor, not the localhost WebView origin
0.2s src/utils/__tests__/demo-balance.test.ts › starts at the full balance on a fresh install and stamps a timestamp
0.2s src/components/Card/share-asset/__tests__/shareAssetLayout.test.ts › keeps stickers off the username pill (final pass respects the keep-out)
📍 Inline annotations are in the **Unit test report** check above. Coverage artifact: `coverage-unit`. Generated by `.github/workflows/tests.yml`.

@kushagrasarathe

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/hooks/usePrimaryNameServer.ts`:
- Around line 36-48: Update readNameCache to validate the parsed JSON root
before returning it: only accept a non-null object, and return an empty cache
for null or other non-object values. Keep getCachedName’s existing lookup and
TTL behavior unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 4d3e75d9-5609-4448-bd54-4591c7ae2c2a

📥 Commits

Reviewing files that changed from the base of the PR and between 04b20e7 and fdbde98.

📒 Files selected for processing (2)
  • src/hooks/__tests__/usePrimaryNameServer.test.tsx
  • src/hooks/usePrimaryNameServer.ts

Comment thread src/hooks/usePrimaryNameServer.ts
coderabbit: JSON.parse('null') passes the try/catch but null[address]
throws during render. treat any non-object root as an empty cache.
…k href in sync

code-review findings: (1) with the server route erroring (today's prod
state) the only eviction path was server null, which never fires — a
client lookup settling '' (justaname's not-found) now also masks and
evicts, so a released/transferred ens name can't keep painting from
cache. (2) AddressLink's effect never reset urlAddress on the
name→address downgrade, leaving the href pointing at a name the address
may no longer own — reachable now that cached names can be evicted
mid-mount. (3) server '' responses treated same as null.
@kushagrasarathe

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@kushagrasarathe
kushagrasarathe marked this pull request as ready for review July 29, 2026 15:06
@kushagrasarathe
kushagrasarathe requested a review from Hugo0 July 29, 2026 15:06
@jjramirezn
jjramirezn merged commit ede6c7a into main Jul 29, 2026
26 of 28 checks passed
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.

2 participants