Skip to content

Resolve ZcashNames (.zec) in Zcash send and tx history#6000

Open
j0ntz wants to merge 4 commits intodevelopfrom
jon/feat/zcash-names
Open

Resolve ZcashNames (.zec) in Zcash send and tx history#6000
j0ntz wants to merge 4 commits intodevelopfrom
jon/feat/zcash-names

Conversation

@j0ntz
Copy link
Copy Markdown
Contributor

@j0ntz j0ntz commented Apr 24, 2026

CHANGELOG

Does this branch warrant an entry to the CHANGELOG?

  • Yes
  • No

Dependencies

none

Requirements

If you have made any visual changes to the GUI. Make sure you have:

  • Tested on iOS device
  • Tested on Android device
  • Tested on small-screen device (iPod Touch)
  • Tested on large-screen device (tablet)

Description

Asana: https://app.asana.com/1/9976422036640/project/1213880789473005/task/1214252169890166

Adds ZcashNames (ZNS) — like ENS but for Zcash — to the Zcash send flow and transaction display.

  • Forward lookup: typing alice.zec (or any .zec name) in AddressTile2 / AddressModal for a Zcash wallet resolves it to the registered unified address. Mirrors the existing ENS / Unstoppable Domains / Zano-alias hooks. Silent fallback to parseUri on miss.
  • Reverse lookup: a new useZnsName hook async-resolves Zcash recipient addresses to their registered .zec name (with in-memory cache + in-flight dedupe) and renders it on the transaction list row and the transaction details screen when no metadata name is set.
  • Persistence: the entered ZNS name lives on spendTarget.otherParams.znsName and is propagated into metadata.name on broadcast — same pattern as zanoAlias / fioAddress.
  • Implementation: pure GUI integration via zcashname-sdk@^0.7.2. No react-native-zcash or native code changes. The SDK ships pure-JS deps (bech32, @noble/ed25519); both are added to the Jest transformIgnorePatterns whitelist so the test suite still loads them.

Pre-existing lint warnings in TransactionListRow.tsx are addressed in the first commit so the file can be removed from the warning suppression list cleanly. Verified locally with yarn prepare, yarn lint, and yarn test. Device-level UI verification pending.


Note

Medium Risk
Adds new Zcash-specific name-resolution paths and a new third-party SDK dependency, which can affect send address handling and transaction labeling if resolution or caching behaves unexpectedly.

Overview
Adds ZcashNames (ZNS) support so users can enter .zec names when sending Zcash and see resolved names in transaction UI.

Forward resolution is integrated into AddressTile2 and AddressModal, persisting the original .zec input via spendTarget.otherParams.znsName and displaying it in send tiles/notes. Reverse resolution is added via a new useZnsName hook (with in-memory cache and in-flight dedupe) and used by TransactionListRow and TransactionDetailsScene as a fallback label when no metadata name exists.

Introduces zcashname-sdk (and Jest transform exceptions for zcashname-sdk/@noble/ed25519) plus a small refactor in TransactionListRow to remove prior lint-suppression entries; updates the changelog accordingly.

Reviewed by Cursor Bugbot for commit b48db4d. Bugbot is set up for automated code reviews on this repo. Configure here.

Comment thread src/components/themed/TransactionListRow.tsx
Comment thread src/hooks/useZnsName.ts
import { reverseResolveZnsAddress } from '../util/zns'

const cache = new Map<string, string | null>()
const inflight = new Map<string, Promise<string | null>>()
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Module-level ZNS caches never cleared on logout

Low Severity

The module-level cache and inflight maps in useZnsName.ts and the znsClient singleton in zns.ts persist across logout/login with no exported clear function. Per the module-level-cache-bugs rule, module-level caches must reset on logout/login.

Additional Locations (1)
Fix in Cursor Fix in Web

Triggered by project rule: Bugbot Review Rules

Reviewed by Cursor Bugbot for commit 0ccd68b. Configure here.

@j0ntz
Copy link
Copy Markdown
Contributor Author

j0ntz commented Apr 25, 2026

Action item — not blocking this PR, but blocks the ZNS claim UX

While testing, the ZNS:CLAIM:edge:u1fm0cctk8xycu7a5… memo from a zcashnames.com claim QR (a ZIP-321 URI: zcash:u1k0…?amount=0.015&memo=<base64url>) is silently dropped — on-chain shielded memo is empty when decrypted with the sender OVK on a block explorer.

Root cause is upstream of this PR, in edge-currency-accountbased:

  • parseUriCommon only extracts label, message, category, amount. It does not read the memo query param, base64url-decode it, or surface it as EdgeParsedUri.memos. It also doesn't handle paramindex (multi-recipient) or reject unknown req-* params.
  • ZcashTools.parseUri just delegates to parseUriCommon with no zcash-specific layer, so the memo disappears before it ever reaches SendScene2.

ZIP-321 spec: https://zips.z.cash/zip-0321 — memo is base64url without = padding, decoded contents ≤ 512 bytes.

Suggested fix (separate PR in edge-currency-accountbased):
The plumbing for the proper fix already exists — ZcashEngine.makeSpend (~L400–409) checks edgeSpendInfo.otherParams.zip321Uri and, if set, calls synchronizer.proposeFulfillingPaymentURI(zip321Uri). That native call uses the Zcash SDK's full ZIP-321 implementation (memo, paramindex, etc.). But nothing currently populates otherParams.zip321Uri, so this path is dead from the GUI side.

Two options:

  1. Preferred: in ZcashTools.parseUri, detect zcash: URIs with non-trivial ZIP-321 params (memo / paramindex / req-*) and stash the raw URI on EdgeParsedUri.otherParams.zip321Uri; pass it through SendScene2EdgeSpendInfo.otherParams.zip321Uri. Native SDK handles the rest.
  2. Add inline memo base64url decoding to ZcashTools.parseUri and surface as EdgeParsedUri.memos. Smaller change but only covers single-output URIs.

Repro: scan the claim QR from zcashnames.com → send → block explorer → decrypt outgoing shielded memo with sender FVK/OVK → expect ZNS:CLAIM:edge:…, get an empty memo.

@j0ntz j0ntz force-pushed the jon/feat/zcash-names branch from 0ccd68b to b48db4d Compare April 25, 2026 01:14
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

There are 3 total unresolved issues (including 1 from previous review).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit b48db4d. Configure here.

this.checkIfZnsName(name) &&
this.props.coreWallet.currencyInfo.pluginId === 'zcash'
) {
address = await this.fetchZnsAddress(name)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

AddressModal drops ZNS name on submit

High Severity

When resolving .zec names, AddressModal returns the resolved Zcash address instead of the original .zec input. This differs from Zano alias handling, causing the original .zec name to be lost and not propagated to spendTarget.otherParams or metadata.name for transaction metadata.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit b48db4d. Configure here.

Comment thread src/util/zns.ts
export const reverseResolveZnsAddress = async (
address: string
): Promise<string | null> => {
const regs = await getZns().resolveAddress(address, 1, 0)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Reversed parameters make reverse ZNS lookup always empty

High Severity

The resolveAddress SDK method signature is (address, offset, limit), but the call passes (address, 1, 0) — meaning offset=1 and limit=0. A limit of zero returns zero results, so reverseResolveZnsAddress always returns null. The correct call is resolveAddress(address, 0, 1) to fetch the first result. This silently breaks the entire reverse-lookup display path: useZnsName never resolves a name, so .zec names never appear on the transaction list or details screen.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit b48db4d. Configure here.

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.

1 participant