Reverse-lookup names (ENS / UD / ZNS) across send flow and tx history#6008
Reverse-lookup names (ENS / UD / ZNS) across send flow and tx history#6008
Conversation
Convert AddressModal and SendComponent to React.FC<Props>, and replace
the two `styled()` wrappers in SendScene2 with regular React Native
components driven by `useTheme()` + `cacheStyles()`. No behavior change.
The slider-view snapshot updates reflect two equivalent shifts: the old
`styled(View)` HOC was incidentally forwarding `hasNotifications` and
`insetBottom` as DOM props (which the underlying View ignored), and the
inlined style is now an array `[base, { bottom }]` rather than a merged
object. Both render to the same pixels.
Touched here in preparation for reverse-lookup edits in subsequent
commits, per the lint-warnings.sh workflow contract for files entering
the working set.
Introduces `src/util/nameServices.ts` with a `reverseLookupName(pluginId, address)` function that maps each chain to its supported reverse-lookup services and tries them in order (positive result wins, transient failures don't poison the cache). Service eligibility: - ZNS: zcash only - ENS: ethereum only (ENSIP-3 L1 mainnet via ethers v5 lookupAddress) - Unstoppable Domains: any EVM chain when UNSTOPPABLE_DOMAINS_API_KEY is set (UD reverse only resolves EVM addresses per their docs) The cache uses guard-on-success semantics: results are cached when the dispatch chain completes cleanly, but transient errors leave the entry empty so the next call retries. Inflight dedup prevents duplicate network hits when the same address renders in many list rows. This replaces the ad-hoc cache in `useZnsName` with a service-aware equivalent that subsequent commits will wire into the send and transaction-history scenes.
Adds `useReverseName` (a multi-service replacement for `useZnsName`) and the `NameServicePrefix` visual element, then swaps both TransactionListRow and TransactionDetailsScene over to the new hook. UX surfaces: - TransactionListRow: text-only render. The list is dense enough that an inline logo would crowd the row; the resolved name still appears, just without the badge. - TransactionDetailsScene: prefix the resolved name with a 1rem-tall logo. The prefix only renders when the displayed name actually came from a reverse lookup — user-set contact names and the default "To"/"From" label render plain. Missing logo assets (UD, ZNS today) fall back to no-prefix without reserving space. LoginActions swaps `clearZnsLookupCache` for `clearReverseLookupCache` on the same logout boundary so per-login cache state stays isolated. The legacy `useZnsName` hook has no remaining consumers and is removed. The TransactionDetailsScene snapshot picks up the new row-layout `<View>` wrapper that hosts the optional prefix; the wrapper is benign when no prefix is present.
Wires the reverse-lookup dispatcher into AddressTile2's address-entry
path and threads the result through SendScene2 so resolved names appear
in the send tile and persist into transaction metadata.
AddressTile2 changes:
- Replace `ChangeAddressResult.znsName` with `resolvedName`, a
`{ name, service }` pair that captures both forward-typed names
(alice.eth, alice.zcash, alice.zec) and reverse-resolved names from
raw addresses.
- After parseUri succeeds and we have a public address, attempt a
reverse lookup if no forward-typed name was captured. The dispatcher
caches per (pluginId, address) so repeated entry of the same address
is a no-op.
- New `recipientNameService` prop drives an inline `NameServicePrefix`
badge above the address. FIO and Zano handles continue to render
plain (no badge), preserving the prior look for those flows.
SendScene2 changes:
- Carry `resolvedName` through `spendTarget.otherParams` and the
`EditableAmountTile` title (still text-only per the design — the tile
is a confirmation snapshot, not the live entry surface).
- Generalize the post-broadcast `payeeName` derivation so any
single-resolved-name spendInfo (ENS / UD / ZNS) produces a payeeName,
replacing the chain-specific Zcash branch.
Outcome: pasting a 0x address to an Ethereum send shows the resolved
ENS / UD name above the hex address as soon as the lookup completes,
and that name persists as `payeeName` in the broadcasted transaction's
metadata so it surfaces in the transaction history.
When the user pastes or types a string that doesn't match any forward- domain pattern, attempt a reverse lookup against the wallet's pluginId and surface the resolved name in the input's `validLabel` slot. The existing forward-resolution path (which puts the resolved address in the same slot) is unchanged, so the green helper text is symmetric: - type alice.eth → see 0x… resolve below the input - paste 0x… → see alice.eth resolve below the input A monotonically increasing sequence counter scopes each lookup to the input it was issued for, so a slow late-arriving result can't clobber the label after the user has moved on. Adds the user-visible CHANGELOG entry covering the full reverse-lookup feature (this commit plus the prior three on this branch).
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit a1911f1. Configure here.
| this.setState({ validLabel: result.name }) | ||
| }) | ||
| .catch((_err: unknown) => undefined) | ||
| } |
There was a problem hiding this comment.
Stale reverse lookup not cancelled on domain input
Medium Severity
The reverseLookupSeq counter is only incremented inside tryReverseLookup (called for non-domain inputs). When the user switches from a raw address to a domain name (e.g. alice.eth), onChangeTextDelayed takes the checkIfDomain branch and never bumps the counter. A slow in-flight reverse lookup from the previous raw-address input still passes the seq === this.reverseLookupSeq guard. Since validLabel was cleared to undefined at the top of onChangeTextDelayed, the stale callback can briefly set validLabel to the wrong resolved name before the forward resolution overwrites it.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit a1911f1. Configure here.


CHANGELOG
Does this branch warrant an entry to the CHANGELOG?
Dependencies
none
Requirements
If you have made any visual changes to the GUI. Make sure you have:
Description
Asana task
Asana task
Reverse-resolve recipient addresses to ENS / Unstoppable Domains / ZNS names across the send flow, address modal, and transaction history. Previously only forward resolution worked (typing
alice.ethresolved to0x…); now pasting0x…surfacesalice.ethand that name persists into transaction metadata.What's in the branch (oldest → newest)
Fix lint warnings before reverse-lookup feature —
lint-warnings.shprep on the touched files: convertAddressModalandSendComponenttoReact.FC<Props>, and replace twostyled()wrappers inSendScene2withuseTheme()+cacheStyles()equivalents. Pure refactor; behavior preserved (snapshot diff is incidental DOM-prop leakage that the new code correctly drops).Add reverse-lookup dispatcher for ENS, UD, and ZNS — new
src/util/nameServices.tswith areverseLookupName(pluginId, address)function. Per-chain dispatch:ethersv5lookupAddress; ENSIP-19 multichain reverse needs ethers v6, out of scope).caip2.namespace === 'eip155'), only whenUNSTOPPABLE_DOMAINS_API_KEYis set. UD'sreverseonly resolves EVM addresses per their docs.Cache uses guard-on-success semantics: positive results cache eagerly, but transient errors don't get cached as
null(avoids poisoning addresses across the process lifetime). Inflight dedup prevents duplicate network hits when the same address renders in many list rows on first paint.Use reverse-lookup dispatcher in transaction history — adds
useReverseName(multi-service replacement for the olduseZnsName) and a smallNameServicePrefixvisual element (1rem inline logo). Swaps bothTransactionListRowandTransactionDetailsSceneover.TransactionListRow: text-only render (the row is dense; an inline logo would crowd it).TransactionDetailsScene: prefix the resolved name with a 1rem-tall logo, but only when the displayed name actually came from a reverse lookup. User-set contact names and the default "To"/"From" label render plain. Missing logo assets (UD, ZNS today) fall back to no-prefix without reserving space.Reverse-lookup names in send flow — wires the dispatcher into
AddressTile2's entry path. AfterparseUrisucceeds, attempts a reverse lookup if no forward-typed name was captured. The newrecipientNameServiceprop drives the inlineNameServicePrefixbadge above the address. FIO and Zano handles continue to render plain.SendScene2carriesresolvedNamethroughspendTarget.otherParamsand the post-broadcastpayeeNameso the resolved name persists into transaction metadata (the chain-specific Zcash branch is generalized to handle any service).Live reverse-lookup feedback in AddressModal — when the user pastes an address into the address modal, attempt a reverse lookup gated on
coreWallet.parseUrisucceeding (so we only hit the network when the input is a valid address for the chain — no per-keystroke lookups). Surfaces the resolved name in the input's existing greenvalidLabelslot, mirroring the forward-resolution UX. A monotonically increasing sequence counter scopes each lookup so a slow late-arriving result can't clobber the label after the user has moved on.Visual design notes
AddressTile2andTransactionDetailsScene. Transaction list rows stay text-only (per discussion).src/assets/images/ens_logo.png). UD and ZNS render text-only until assets are added — the component degrades gracefully (no placeholder, no reserved space).Caveats / known limitations
UNSTOPPABLE_DOMAINS_API_KEY; only resolves EVM addresses per UD's documentedResolution.reversescope.The dispatcher is structured so adding a new service or expanding chain support is a one-spot change in
getReverseLookupServices.Note
Medium Risk
Adds new network-backed reverse-resolution logic and wires it into send and transaction UI/metadata, which could impact address handling and performance if lookups fail or are slow. Changes are scoped to name display/metadata but touch core send entry points.
Overview
Adds reverse address-to-name resolution across the app. A new
nameServicesdispatcher performs cached reverse lookups for ENS (Ethereum mainnet), Unstoppable Domains (EVM chains when API key is set), and ZNS (Zcash), with inflight de-duping and guarded null-caching.Send flow now captures a unified
resolvedName(from forward entry or reverse lookup) inAddressTile2/SendScene2, displays an optional inline service badge, and persists the resolved name into transaction metadata/payee naming.Transaction history UI replaces the Zcash-only
useZnsNamehook with a genericuseReverseName, showing resolved recipient names (and an inline ENS logo where available) inTransactionListRowandTransactionDetailsScene. Logout now clears the new reverse-lookup cache, and snapshots are updated due to SendScene layout/styling refactors.Reviewed by Cursor Bugbot for commit a1911f1. Bugbot is set up for automated code reviews on this repo. Configure here.