From 016651b3ed4ad9fb6be55cc3535b2b6287bbac0e Mon Sep 17 00:00:00 2001 From: j0ntz Date: Fri, 24 Apr 2026 16:24:34 -0700 Subject: [PATCH 1/6] Fix lint warnings in TransactionListRow --- eslint.config.mjs | 1 - src/components/themed/TransactionListRow.tsx | 26 +++++++++++--------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index 5220a1a32fa..4d9c57b8e91 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -358,7 +358,6 @@ export default [ 'src/components/themed/Thermostat.tsx', 'src/components/themed/Title.tsx', 'src/components/themed/TransactionListComponents.tsx', - 'src/components/themed/TransactionListRow.tsx', 'src/components/themed/VectorIcon.tsx', 'src/components/themed/WalletList.tsx', diff --git a/src/components/themed/TransactionListRow.tsx b/src/components/themed/TransactionListRow.tsx index aa2fdf78798..4de4f287665 100644 --- a/src/components/themed/TransactionListRow.tsx +++ b/src/components/themed/TransactionListRow.tsx @@ -57,15 +57,7 @@ interface TransactionViewInnerProps extends TransactionListRowProps { isCard?: boolean } -export const TransactionView = (props: TransactionListRowProps) => { - return -} - -export const TransactionCard = (props: TransactionListRowProps) => { - return -} - -function TransactionViewInner(props: TransactionViewInnerProps) { +const TransactionViewInner: React.FC = props => { const theme = useTheme() const styles = getStyles(theme) @@ -130,7 +122,9 @@ function TransactionViewInner(props: TransactionViewInnerProps) { ) const cryptoAmountString = `${isSentTransaction ? '-' : '+'}${ - denominationSymbol ? denominationSymbol + ' ' : '' + denominationSymbol != null && denominationSymbol !== '' + ? denominationSymbol + ' ' + : '' }${cryptoAmountFormat}` // Fiat Amount @@ -249,13 +243,13 @@ function TransactionViewInner(props: TransactionViewInnerProps) { failOnCancel: false, url } - Share.open(shareOptions).catch(e => { + Share.open(shareOptions).catch((e: unknown) => { showError(e) }) }) // HACK: Handle 100% of the margins because of SceneHeader usage on this scene - return isCard ? ( + return isCard === true ? ( <> @@ -334,6 +328,14 @@ function TransactionViewInner(props: TransactionViewInnerProps) { ) } +export const TransactionView: React.FC = props => { + return +} + +export const TransactionCard: React.FC = props => { + return +} + const getStyles = cacheStyles((theme: Theme) => ({ cardlessView: { flexDirection: 'column', From b2117a4f802844c31739df9b325eecb00b478031 Mon Sep 17 00:00:00 2001 From: j0ntz Date: Fri, 24 Apr 2026 16:26:01 -0700 Subject: [PATCH 2/6] Add ZcashNames (ZNS) resolver utility --- jest.config.js | 2 +- package.json | 3 ++- src/util/zns.ts | 40 ++++++++++++++++++++++++++++++++++++++++ yarn.lock | 13 +++++++++++++ 4 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 src/util/zns.ts diff --git a/jest.config.js b/jest.config.js index 374d6f16b32..b04a05bf1b3 100644 --- a/jest.config.js +++ b/jest.config.js @@ -11,6 +11,6 @@ module.exports = { preset: 'react-native', setupFilesAfterEnv: ['./jestSetup.js'], transformIgnorePatterns: [ - '/node_modules/(?!(@react-native|react-native|@react-navigation))' + '/node_modules/(?!(@react-native|react-native|@react-navigation|zcashname-sdk|@noble/ed25519))' ] } diff --git a/package.json b/package.json index b641ba5b9f8..55d0c1be0de 100644 --- a/package.json +++ b/package.json @@ -186,7 +186,8 @@ "url-parse": "^1.5.2", "use-context-selector": "^2.0.0", "yaob": "^0.3.12", - "yavent": "^0.1.5" + "yavent": "^0.1.5", + "zcashname-sdk": "^0.7.2" }, "devDependencies": { "@babel/core": "^7.25.2", diff --git a/src/util/zns.ts b/src/util/zns.ts new file mode 100644 index 00000000000..ae364d05ec6 --- /dev/null +++ b/src/util/zns.ts @@ -0,0 +1,40 @@ +import { ZNS } from 'zcashname-sdk' + +export const ZNS_SUFFIX = '.zcash' + +// The SDK's `network` option only selects the registry address; its `url` +// always falls back to the testnet indexer unless overridden explicitly. +const ZNS_MAINNET_URL = 'https://main.zcashnames.com' + +let znsClient: ZNS | null = null + +const getZns = (): ZNS => { + znsClient ??= new ZNS({ network: 'mainnet', url: ZNS_MAINNET_URL }) + return znsClient +} + +export const resetZnsClient = (): void => { + znsClient = null +} + +export const isZnsName = (input: string): boolean => + input.toLowerCase().endsWith(ZNS_SUFFIX) + +export const stripZnsSuffix = (input: string): string => { + const lower = input.toLowerCase() + return lower.endsWith(ZNS_SUFFIX) + ? lower.slice(0, lower.length - ZNS_SUFFIX.length) + : lower +} + +export const resolveZnsName = async (input: string): Promise => { + const reg = await getZns().resolveName(stripZnsSuffix(input)) + return reg?.address ?? null +} + +export const reverseResolveZnsAddress = async ( + address: string +): Promise => { + const regs = await getZns().resolveAddress(address, 1, 0) + return regs.length > 0 ? `${regs[0].name}${ZNS_SUFFIX}` : null +} diff --git a/yarn.lock b/yarn.lock index 30c3ae6ab09..ec853249fa7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3416,6 +3416,11 @@ dependencies: "@noble/hashes" "1.7.1" +"@noble/ed25519@^2.0.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@noble/ed25519/-/ed25519-2.3.0.tgz#df0dbe424cfd7da4968b956b819c15db5fbe7f21" + integrity sha512-M7dvXL2B92/M7dw9+gzuydL8qn/jiqNHaoR3Q+cb1q1GHV7uwE17WCyFMG+Y+TZb5izcaXk5TdJRrDUxHXL78A== + "@noble/hashes@1.3.1": version "1.3.1" resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.1.tgz#8831ef002114670c603c458ab8b11328406953a9" @@ -18960,6 +18965,14 @@ yoctocolors-cjs@^2.1.2: resolved "https://registry.yarnpkg.com/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz#f4b905a840a37506813a7acaa28febe97767a242" integrity sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA== +zcashname-sdk@^0.7.2: + version "0.7.2" + resolved "https://registry.yarnpkg.com/zcashname-sdk/-/zcashname-sdk-0.7.2.tgz#472d4265dd0b9778f8cb327b8f0382f89229ca2f" + integrity sha512-VVq9LHNRgpFq0+Qzxqd7bRmk+KAt3uzJW2lthKeqsgSedRjjhML9Y+IBhRYyh/gge8rFOiKWdx3EyM9LJkwqAg== + dependencies: + "@noble/ed25519" "^2.0.0" + bech32 "^2.0.0" + zod@^3.21.4: version "3.23.8" resolved "https://registry.yarnpkg.com/zod/-/zod-3.23.8.tgz#e37b957b5d52079769fb8097099b592f0ef4067d" From 88dbda6d0e27c567b27bce41819ae195462e3721 Mon Sep 17 00:00:00 2001 From: j0ntz Date: Fri, 24 Apr 2026 16:27:49 -0700 Subject: [PATCH 3/6] Resolve ZNS names in Zcash send flow --- eslint.config.mjs | 4 ++-- src/components/modals/AddressModal.tsx | 29 ++++++++++++++++++++++++++ src/components/scenes/SendScene2.tsx | 18 +++++++++++++--- src/components/tiles/AddressTile2.tsx | 20 +++++++++++++++++- 4 files changed, 65 insertions(+), 6 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index 4d9c57b8e91..0bf83d4d6fb 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -167,7 +167,7 @@ export default [ 'src/components/layout/Peek.tsx', 'src/components/modals/AccelerateTxModal.tsx', - 'src/components/modals/AddressModal.tsx', + 'src/components/modals/AirshipFullScreenSpinner.tsx', 'src/components/modals/AutoLogoutModal.tsx', 'src/components/modals/BackupModal.tsx', @@ -371,7 +371,7 @@ export default [ 'src/components/themed/WalletListSwipeableCurrencyRow.tsx', 'src/components/themed/WalletListSwipeableLoadingRow.tsx', - 'src/components/tiles/AddressTile2.tsx', + 'src/components/tiles/AprCard.tsx', 'src/components/tiles/CountdownTile.tsx', 'src/components/tiles/CryptoFiatAmountTile.tsx', diff --git a/src/components/modals/AddressModal.tsx b/src/components/modals/AddressModal.tsx index c5cb8f877fc..b197212b80d 100644 --- a/src/components/modals/AddressModal.tsx +++ b/src/components/modals/AddressModal.tsx @@ -29,6 +29,7 @@ import { getFioAddressCache } from '../../util/FioAddressUtils' import { resolveName } from '../../util/resolveName' +import { isZnsName, resolveZnsName } from '../../util/zns' import { EdgeButton } from '../buttons/EdgeButton' import { EdgeTouchableWithoutFeedback } from '../common/EdgeTouchableWithoutFeedback' import { showDevError, showError } from '../services/AirshipInstance' @@ -180,9 +181,14 @@ export class AddressModalComponent extends React.Component { } checkIfDomain = (domain: string): boolean => { + // ZNS only resolves on Zcash wallets — gating here so non-zcash wallets + // don't trip into the domain-resolution path on `.zcash` input and end + // up showing a spurious "unsupported domain" error. + const isZcash = this.props.coreWallet.currencyInfo.pluginId === 'zcash' return ( this.checkIfUnstoppableDomain(domain) || this.checkIfEnsDomain(domain) || + (isZcash && this.checkIfZnsName(domain)) || this.checkIfAlias(domain) ) } @@ -193,6 +199,8 @@ export class AddressModalComponent extends React.Component { checkIfEnsDomain = (name: string): boolean => ENS_DOMAINS.some(domain => name.endsWith(domain)) + checkIfZnsName = (name: string): boolean => isZnsName(name) + fetchUnstoppableDomainAddress = async ( resolver: Resolver, domain: string, @@ -232,6 +240,13 @@ export class AddressModalComponent extends React.Component { return address } + fetchZnsAddress = async (domain: string): Promise => { + const address = await resolveZnsName(domain) + if (address == null) + throw new ResolutionError('UnregisteredDomain', { domain }) + return address + } + resolveName = async (name: string, currencyTicker: string): Promise => { this.setState({ errorLabel: undefined }) if (name === '') return @@ -255,6 +270,11 @@ export class AddressModalComponent extends React.Component { ) } else if (this.checkIfEnsDomain(name)) { address = await this.fetchEnsAddress(name) + } else if ( + this.checkIfZnsName(name) && + this.props.coreWallet.currencyInfo.pluginId === 'zcash' + ) { + address = await this.fetchZnsAddress(name) } if (address == null) { throw new ResolutionError('UnsupportedDomain', { domain: name }) @@ -413,6 +433,15 @@ export class AddressModalComponent extends React.Component { ) { submitData = uri } + // Same idea for ZNS (.zcash) names on Zcash — return the original name so + // the caller can capture znsName and persist it in transaction metadata. + if ( + coreWallet.currencyInfo.pluginId === 'zcash' && + typeof uri === 'string' && + isZnsName(uri) + ) { + submitData = uri + } if (errorLabel != null) return this.props.bridge.resolve(submitData) } diff --git a/src/components/scenes/SendScene2.tsx b/src/components/scenes/SendScene2.tsx index f8aa17178b3..720af5c4d68 100644 --- a/src/components/scenes/SendScene2.tsx +++ b/src/components/scenes/SendScene2.tsx @@ -428,7 +428,7 @@ const SendComponent = (props: Props): React.ReactElement => { const handleChangeAddress = (spendTarget: EdgeSpendTarget) => async (changeAddressResult: ChangeAddressResult): Promise => { - const { addressEntryMethod, parsedUri, fioAddress, alias } = + const { addressEntryMethod, parsedUri, fioAddress, alias, znsName } = changeAddressResult if (parsedUri != null) { @@ -468,7 +468,8 @@ const SendComponent = (props: Props): React.ReactElement => { } spendTarget.otherParams = { fioAddress, - zanoAlias: alias + zanoAlias: alias, + znsName } // We can assume the spendTarget object came from the Component spendInfo so simply resetting the spendInfo @@ -495,10 +496,12 @@ const SendComponent = (props: Props): React.ReactElement => { spendTarget: EdgeSpendTarget ): React.ReactElement => { const { publicAddress, nativeAmount, otherParams = {} } = spendTarget - const { fioAddress } = otherParams + const { fioAddress, znsName } = otherParams let title = '' if (fioAddress != null) { title = `Send To (${fioAddress}) ${publicAddress}` + } else if (znsName != null) { + title = `Send To (${znsName}) ${publicAddress}` } else { title = `Send To ${publicAddress}` } @@ -1320,6 +1323,15 @@ const SendComponent = (props: Props): React.ReactElement => { payeeName = zanoAliases[0] } } + // Same idea for ZNS (.zec) names on Zcash + if (coreWallet.currencyInfo.pluginId === 'zcash') { + const znsNames = spendInfo.spendTargets + .map(t => t.otherParams?.znsName) + .filter((a): a is string => a != null && a.length > 0) + if (znsNames.length === 1) { + payeeName = znsNames[0] + } + } for (const target of spendInfo.spendTargets) { const { fioAddress } = target.otherParams ?? {} if (fioAddress != null) { diff --git a/src/components/tiles/AddressTile2.tsx b/src/components/tiles/AddressTile2.tsx index a2bed4b8d6b..77749f4574e 100644 --- a/src/components/tiles/AddressTile2.tsx +++ b/src/components/tiles/AddressTile2.tsx @@ -25,6 +25,7 @@ import { parseDeepLink } from '../../util/DeepLinkParser' import { checkPubAddress } from '../../util/FioAddressUtils' import { resolveName } from '../../util/resolveName' import { isEmail } from '../../util/utils' +import { isZnsName, resolveZnsName } from '../../util/zns' import { EdgeAnim } from '../common/EdgeAnim' import { EdgeTouchableOpacity } from '../common/EdgeTouchableOpacity' import { AddressModal } from '../modals/AddressModal' @@ -47,6 +48,7 @@ export interface ChangeAddressResult { parsedUri?: EdgeParsedUri addressEntryMethod: AddressEntryMethod alias?: string + znsName?: string } export interface AddressTileRef { @@ -150,6 +152,7 @@ export const AddressTile2 = React.forwardRef( const enteredInput = address.trim() address = enteredInput let zanoAlias: string | undefined + let znsName: string | undefined let fioAddress if (fioPlugin != null) { try { @@ -226,6 +229,20 @@ export const AddressTile2 = React.forwardRef( } catch (_) {} } + // Preserve and resolve ZcashNames like "alice.zcash" + if ( + coreWallet.currencyInfo.pluginId === 'zcash' && + isZnsName(enteredInput) + ) { + try { + const resolved = await resolveZnsName(enteredInput) + if (resolved != null) { + znsName = enteredInput.toLowerCase() + address = resolved + } + } catch (_) {} + } + try { const parsedUri: EdgeParsedUri & { paymentProtocolUrl?: string } = await coreWallet.parseUri(address, currencyCode) @@ -270,7 +287,8 @@ export const AddressTile2 = React.forwardRef( fioAddress, parsedUri, addressEntryMethod, - alias: zanoAlias + alias: zanoAlias, + znsName }) } catch (e: unknown) { const currencyInfo = coreWallet.currencyInfo From a8e23aa940263c66f94fedbdcec12c459896c85b Mon Sep 17 00:00:00 2001 From: j0ntz Date: Tue, 28 Apr 2026 14:02:47 -0700 Subject: [PATCH 4/6] Show ZNS names for known Zcash recipients --- eslint.config.mjs | 2 - src/actions/LoginActions.tsx | 4 ++ .../scenes/TransactionDetailsScene.tsx | 13 ++++- src/components/themed/TransactionListRow.tsx | 14 ++++- src/hooks/useZnsName.ts | 58 +++++++++++++++++++ 5 files changed, 87 insertions(+), 4 deletions(-) create mode 100644 src/hooks/useZnsName.ts diff --git a/eslint.config.mjs b/eslint.config.mjs index 0bf83d4d6fb..4ed016e51dd 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -286,8 +286,6 @@ export default [ 'src/components/scenes/SwapSettingsScene.tsx', 'src/components/scenes/SwapSuccessScene.tsx', - 'src/components/scenes/TransactionDetailsScene.tsx', - 'src/components/scenes/TransactionsExportScene.tsx', 'src/components/scenes/WalletRestoreScene.tsx', diff --git a/src/actions/LoginActions.tsx b/src/actions/LoginActions.tsx index 1593ae420f2..6429570a7c0 100644 --- a/src/actions/LoginActions.tsx +++ b/src/actions/LoginActions.tsx @@ -19,6 +19,7 @@ import { ConfirmContinueModal } from '../components/modals/ConfirmContinueModal' import { SurveyModal } from '../components/modals/SurveyModal' import { Airship, showError } from '../components/services/AirshipInstance' import { getExperimentConfig } from '../experimentConfig' +import { clearZnsLookupCache } from '../hooks/useZnsName' import { lstrings } from '../locales/strings' import type { WalletCreateItem } from '../selectors/getCreateWalletList' import { config } from '../theme/appConfig' @@ -32,6 +33,7 @@ import { currencyCodesToEdgeAssets } from '../util/CurrencyInfoHelpers' import { logActivity } from '../util/logger' import { logEvent, trackError } from '../util/tracking' import { runWithTimeout } from '../util/utils' +import { resetZnsClient } from '../util/zns' import { loadAccountReferral, refreshAccountReferral @@ -323,6 +325,8 @@ export function logoutRequest( Keyboard.dismiss() Airship.clear() resetLocalAccountSettingsCache() + resetZnsClient() + clearZnsLookupCache() dispatch({ type: 'LOGOUT' }) if (typeof account.logout === 'function') await account.logout() diff --git a/src/components/scenes/TransactionDetailsScene.tsx b/src/components/scenes/TransactionDetailsScene.tsx index 8add9e8078c..dc36fb94a08 100644 --- a/src/components/scenes/TransactionDetailsScene.tsx +++ b/src/components/scenes/TransactionDetailsScene.tsx @@ -27,6 +27,7 @@ import { useHandler } from '../../hooks/useHandler' import { useHistoricalRate } from '../../hooks/useHistoricalRate' import { useIconColor } from '../../hooks/useIconColor' import { useWatch } from '../../hooks/useWatch' +import { useZnsName } from '../../hooks/useZnsName' import { toPercentString } from '../../locales/intl' import { lstrings } from '../../locales/strings' import { getExchangeDenom } from '../../selectors/DenominationSelectors' @@ -444,7 +445,17 @@ export const TransactionDetailsComponent: React.FC = props => { direction === 'receive' ? lstrings.transaction_details_sender : lstrings.transaction_details_recipient - const personName = localMetadata.name ?? personLabel + // Reverse-lookup only for outgoing txs — `spendTargets[0]` on a receive + // would be our own address. + const recipientAddress = + direction === 'send' + ? transaction.spendTargets?.[0]?.publicAddress + : undefined + const znsName = useZnsName(wallet.currencyInfo.pluginId, recipientAddress) + const personName = + localMetadata.name != null && localMetadata.name !== '' + ? localMetadata.name + : znsName ?? personLabel const personHeader = sprintf( lstrings.transaction_details_person_name, personLabel diff --git a/src/components/themed/TransactionListRow.tsx b/src/components/themed/TransactionListRow.tsx index 4de4f287665..b74ed61eeb0 100644 --- a/src/components/themed/TransactionListRow.tsx +++ b/src/components/themed/TransactionListRow.tsx @@ -24,6 +24,7 @@ import { useDisplayDenom } from '../../hooks/useDisplayDenom' import { displayFiatAmount } from '../../hooks/useFiatText' import { useHandler } from '../../hooks/useHandler' import { useHistoricalRate } from '../../hooks/useHistoricalRate' +import { useZnsName } from '../../hooks/useZnsName' import { formatNumber } from '../../locales/intl' import { lstrings } from '../../locales/strings' import { getExchangeDenom } from '../../selectors/DenominationSelectors' @@ -101,7 +102,18 @@ const TransactionViewInner: React.FC = props => { account, wallet ) - const { category, name } = mergedData + const { category, name: metadataName } = mergedData + // Reverse-lookup only for outgoing txs — `spendTargets[0]` on a receive + // would be our own address. + const recipientAddress = + direction === 'send' + ? transaction.spendTargets?.[0]?.publicAddress + : undefined + const znsName = useZnsName(currencyInfo.pluginId, recipientAddress) + const name = + metadataName != null && metadataName !== '' + ? metadataName + : znsName ?? metadataName const isSentTransaction = direction === 'send' const cryptoAmount = div( diff --git a/src/hooks/useZnsName.ts b/src/hooks/useZnsName.ts new file mode 100644 index 00000000000..0ef059420de --- /dev/null +++ b/src/hooks/useZnsName.ts @@ -0,0 +1,58 @@ +import { useEffect, useState } from 'react' + +import { reverseResolveZnsAddress } from '../util/zns' + +const cache = new Map() +const inflight = new Map>() + +export const clearZnsLookupCache = (): void => { + cache.clear() + inflight.clear() +} + +const lookupZnsName = async (address: string): Promise => { + if (cache.has(address)) return cache.get(address) ?? null + let promise = inflight.get(address) + if (promise == null) { + promise = reverseResolveZnsAddress(address).catch((_err: unknown) => null) + inflight.set(address, promise) + } + const result = await promise + cache.set(address, result) + inflight.delete(address) + return result +} + +export const useZnsName = ( + pluginId: string, + address: string | undefined +): string | null => { + const enabled = pluginId === 'zcash' && address != null && address !== '' + const [name, setName] = useState( + enabled ? cache.get(address) ?? null : null + ) + + useEffect(() => { + if (!enabled) { + // Clear stale name when the hook is disabled (e.g. component recycled + // onto a non-zcash row, or address became undefined). + setName(null) + return + } + // Reset to the current cache value (or null) immediately on address + // change so a recycled component doesn't briefly show the prior row's + // resolved name while the async lookup is in flight. + setName(cache.get(address) ?? null) + let cancelled = false + lookupZnsName(address) + .then(result => { + if (!cancelled) setName(result) + }) + .catch((_err: unknown) => null) + return () => { + cancelled = true + } + }, [enabled, address]) + + return name +} From a0c8950f8283bc48bb33f37b6e6e6d034e2dc06e Mon Sep 17 00:00:00 2001 From: j0ntz Date: Mon, 27 Apr 2026 14:44:34 -0700 Subject: [PATCH 5/6] Show recipient name above address in send tile --- src/components/scenes/SendScene2.tsx | 5 +++-- src/components/tiles/AddressTile2.tsx | 12 ++++++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/components/scenes/SendScene2.tsx b/src/components/scenes/SendScene2.tsx index 720af5c4d68..6cf4bb68dfe 100644 --- a/src/components/scenes/SendScene2.tsx +++ b/src/components/scenes/SendScene2.tsx @@ -543,7 +543,8 @@ const SendComponent = (props: Props): React.ReactElement => { if (coreWallet != null && hiddenFeaturesMap.address !== true) { // TODO: Change API of AddressTile to access undefined recipientAddress const { publicAddress = '', otherParams = {} } = spendTarget - const { fioAddress } = otherParams + const { fioAddress, zanoAlias, znsName } = otherParams + const recipientName = fioAddress ?? znsName ?? zanoAlias const title = lstrings.send_scene_send_to_address + (spendInfo.spendTargets.length > 1 ? ` ${(index + 1).toString()}` : '') @@ -562,7 +563,7 @@ const SendComponent = (props: Props): React.ReactElement => { resetSendTransaction={handleResetSendTransaction(spendTarget)} lockInputs={lockTilesMap.address} isCameraOpen={doOpenCamera} - fioToAddress={fioAddress} + recipientName={recipientName} navigation={navigation as NavigationBase} /> ) diff --git a/src/components/tiles/AddressTile2.tsx b/src/components/tiles/AddressTile2.tsx index 77749f4574e..ef79a0df466 100644 --- a/src/components/tiles/AddressTile2.tsx +++ b/src/components/tiles/AddressTile2.tsx @@ -64,7 +64,11 @@ interface Props { resetSendTransaction: () => void lockInputs?: boolean isCameraOpen: boolean - fioToAddress?: string + /** + * Friendly recipient name to render above the public address — e.g. a FIO + * handle, Zano alias, or ZNS (.zcash) name. Display-only. + */ + recipientName?: string navigation: NavigationBase } @@ -73,7 +77,7 @@ export const AddressTile2 = React.forwardRef( const { coreWallet, tokenId, - fioToAddress, + recipientName, isCameraOpen, lockInputs, navigation, @@ -511,8 +515,8 @@ export const AddressTile2 = React.forwardRef( enter={{ type: 'stretchInY' }} exit={{ type: 'stretchOutY' }} > - {fioToAddress == null ? null : ( - {fioToAddress + '\n'} + {recipientName == null ? null : ( + {recipientName + '\n'} )} Date: Mon, 27 Apr 2026 15:49:52 -0700 Subject: [PATCH 6/6] Build address-modal title from supported name services MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Localize scan_address_modal_title and add scan_address_modal_title_1s Hand-translated alongside the en_US source-of-truth update from the prior commit. Goes against the usual Crowdin-only-edits-translations practice; that's intentional — Crowdin is being phased out, so editing locale JSON directly here is fine. --- CHANGELOG.md | 1 + src/components/tiles/AddressTile2.tsx | 15 ++++++++++++++- src/locales/en_US.ts | 3 ++- src/locales/strings/de.json | 3 ++- src/locales/strings/enUS.json | 3 ++- src/locales/strings/es.json | 3 ++- src/locales/strings/esMX.json | 3 ++- src/locales/strings/fr.json | 3 ++- src/locales/strings/it.json | 3 ++- src/locales/strings/ja.json | 3 ++- src/locales/strings/kaa.json | 3 ++- src/locales/strings/ko.json | 3 ++- src/locales/strings/pt.json | 3 ++- src/locales/strings/ru.json | 3 ++- src/locales/strings/vi.json | 3 ++- src/locales/strings/zh.json | 3 ++- 16 files changed, 43 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a50570a2568..9afeca0ec0a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased (develop) +- added: Resolve and display ZcashNames (.zcash) in the Zcash send flow and transaction history. - changed: Remove free FIO handle creation flows. ## 4.49.0 (staging) diff --git a/src/components/tiles/AddressTile2.tsx b/src/components/tiles/AddressTile2.tsx index ef79a0df466..a738786a748 100644 --- a/src/components/tiles/AddressTile2.tsx +++ b/src/components/tiles/AddressTile2.tsx @@ -363,12 +363,25 @@ export const AddressTile2 = React.forwardRef( }) const handleChangeAddress = useHandler(async () => { + const nameServices: string[] = [] + if (fioPlugin != null) nameServices.push('FIO') + if (coreWallet.currencyInfo.pluginId === 'ethereum') + nameServices.push('ENS') + if (coreWallet.currencyInfo.pluginId === 'zcash') nameServices.push('ZNS') + const title = + nameServices.length > 0 + ? sprintf( + lstrings.scan_address_modal_title_1s, + nameServices.join(', ') + ) + : lstrings.scan_address_modal_title + Airship.show(bridge => ( )) .then(async result => { diff --git a/src/locales/en_US.ts b/src/locales/en_US.ts index d451fd23a61..6b289adf405 100644 --- a/src/locales/en_US.ts +++ b/src/locales/en_US.ts @@ -1368,7 +1368,8 @@ const strings = { personalize_wallet_title: 'Personalize Your Wallet', get_started_button: 'Get Started', not_now_button: 'Not Now', - scan_address_modal_title: 'Enter Recipient FIO, ENS, or Public Address', + scan_address_modal_title: 'Enter Recipient Public Address', + scan_address_modal_title_1s: 'Enter Recipient %1$s or Public Address', enter_any_title: 'Enter any of the following:', enter_any_body: '1. Public address to send money to\n2. Private key to sweep\n3. Wallet Connect URI to connect to a dApp\n4. Edge Login URI to login to another device\n\nEdge will auto-detect the URI format and allow you to select an appropriate wallet', diff --git a/src/locales/strings/de.json b/src/locales/strings/de.json index 5e05c65d412..ee4ba22ad62 100644 --- a/src/locales/strings/de.json +++ b/src/locales/strings/de.json @@ -1086,7 +1086,8 @@ "personalize_wallet_title": "Personalize Your Wallet", "get_started_button": "Get Started", "not_now_button": "Not Now", - "scan_address_modal_title": "Empfänger FIO, ENS oder öffentliche Adresse eingeben", + "scan_address_modal_title": "Öffentliche Adresse des Empfängers eingeben", + "scan_address_modal_title_1s": "Empfänger %1$s oder öffentliche Adresse eingeben", "enter_any_title": "Enter any of the following:", "enter_any_body": "1. Public address to send money to\n2. Private key to sweep\n3. Wallet Connect URI to connect to a dApp\n4. Edge Login URI to login to another device\n\nEdge will auto-detect the URI format and allow you to select an appropriate wallet", "enter_any_input_hint": "Enter any of the above", diff --git a/src/locales/strings/enUS.json b/src/locales/strings/enUS.json index 584b22395b4..deedd2840cd 100644 --- a/src/locales/strings/enUS.json +++ b/src/locales/strings/enUS.json @@ -1086,7 +1086,8 @@ "personalize_wallet_title": "Personalize Your Wallet", "get_started_button": "Get Started", "not_now_button": "Not Now", - "scan_address_modal_title": "Enter Recipient FIO, ENS, or Public Address", + "scan_address_modal_title": "Enter Recipient Public Address", + "scan_address_modal_title_1s": "Enter Recipient %1$s or Public Address", "enter_any_title": "Enter any of the following:", "enter_any_body": "1. Public address to send money to\n2. Private key to sweep\n3. Wallet Connect URI to connect to a dApp\n4. Edge Login URI to login to another device\n\nEdge will auto-detect the URI format and allow you to select an appropriate wallet", "enter_any_input_hint": "Enter any of the above", diff --git a/src/locales/strings/es.json b/src/locales/strings/es.json index 06ed6c81b2c..27e780a98c5 100644 --- a/src/locales/strings/es.json +++ b/src/locales/strings/es.json @@ -1086,7 +1086,8 @@ "personalize_wallet_title": "Personalize Your Wallet", "get_started_button": "Get Started", "not_now_button": "Not Now", - "scan_address_modal_title": "Introduce el destinatario FIO, ENS, o dirección pública", + "scan_address_modal_title": "Introduce la dirección pública del destinatario", + "scan_address_modal_title_1s": "Introduce el destinatario %1$s o dirección pública", "enter_any_title": "Enter any of the following:", "enter_any_body": "1. Public address to send money to\n2. Private key to sweep\n3. Wallet Connect URI to connect to a dApp\n4. Edge Login URI to login to another device\n\nEdge will auto-detect the URI format and allow you to select an appropriate wallet", "enter_any_input_hint": "Enter any of the above", diff --git a/src/locales/strings/esMX.json b/src/locales/strings/esMX.json index 052f5bbe16d..1bb99b7b4b5 100644 --- a/src/locales/strings/esMX.json +++ b/src/locales/strings/esMX.json @@ -1086,7 +1086,8 @@ "personalize_wallet_title": "Personaliza tu billetera", "get_started_button": "Comenzar", "not_now_button": "Ahora no", - "scan_address_modal_title": "Introduce el destinatario FIO, ENS, o dirección pública", + "scan_address_modal_title": "Introduce la dirección pública del destinatario", + "scan_address_modal_title_1s": "Introduce el destinatario %1$s o dirección pública", "enter_any_title": "Ingresa cualquiera de los siguientes:", "enter_any_body": "1. Dirección pública para enviar dinero\n2. Clave privada para barrer fondos\n3. URI de Wallet Connect para conectar con una dApp\n4. URI de Edge Login para iniciar sesión en otro dispositivo\n\nEdge detectará automáticamente el formato de la URI y te permitirá seleccionar una billetera adecuada", "enter_any_input_hint": "Ingresa cualquiera de los anteriores", diff --git a/src/locales/strings/fr.json b/src/locales/strings/fr.json index a3b44137e07..adc6a34b860 100644 --- a/src/locales/strings/fr.json +++ b/src/locales/strings/fr.json @@ -1086,7 +1086,8 @@ "personalize_wallet_title": "Personalize Your Wallet", "get_started_button": "Get Started", "not_now_button": "Not Now", - "scan_address_modal_title": "Enter Recipient FIO, ENS, or Public Address", + "scan_address_modal_title": "Enter Recipient Public Address", + "scan_address_modal_title_1s": "Enter Recipient %1$s or Public Address", "enter_any_title": "Enter any of the following:", "enter_any_body": "1. Public address to send money to\n2. Private key to sweep\n3. Wallet Connect URI to connect to a dApp\n4. Edge Login URI to login to another device\n\nEdge will auto-detect the URI format and allow you to select an appropriate wallet", "enter_any_input_hint": "Enter any of the above", diff --git a/src/locales/strings/it.json b/src/locales/strings/it.json index d1cfedaef7e..e0f646a1ca7 100644 --- a/src/locales/strings/it.json +++ b/src/locales/strings/it.json @@ -1086,7 +1086,8 @@ "personalize_wallet_title": "Personalizza Il Tuo Portafoglio", "get_started_button": "Inizia", "not_now_button": "Non ora", - "scan_address_modal_title": "Inserisci indirizzo FIO, ENS o indirizzo pubblico del destinatario", + "scan_address_modal_title": "Inserisci l'indirizzo pubblico del destinatario", + "scan_address_modal_title_1s": "Inserisci %1$s o l'indirizzo pubblico del destinatario", "enter_any_title": "Inserisci uno dei seguenti elementi:", "enter_any_body": "1. Indirizzo pubblico a cui inviare denaro\n2. Chiave privata per importare i fondi \n3. URI di Wallet Connect per connetterti a una dApp\n4. Edge Login URI per accedere su un altro dispositivo\n\nEdge rileverà automaticamente il formato URI e ti consentirà di selezionare un portafoglio appropriato", "enter_any_input_hint": "Inserisci uno qualsiasi dei precedenti", diff --git a/src/locales/strings/ja.json b/src/locales/strings/ja.json index be50373ecf5..fef46367aad 100644 --- a/src/locales/strings/ja.json +++ b/src/locales/strings/ja.json @@ -1086,7 +1086,8 @@ "personalize_wallet_title": "ハンドル名を決めてください", "get_started_button": "始める", "not_now_button": "後で", - "scan_address_modal_title": "受信者のFIO、ENS、または公開アドレスを入力してください", + "scan_address_modal_title": "受信者の公開アドレスを入力してください", + "scan_address_modal_title_1s": "受信者の%1$sまたは公開アドレスを入力してください", "enter_any_title": "次のいずれかを入力します:", "enter_any_body": "1. 送金先の公開アドレス\n2. スイープ用の秘密鍵\n3. dAppに接続するためのWallet Connect URI\n4. 別のデバイスにログインするためのEdge Login URI\n\nEdgeはURIの形式を自動で検出するため、適切なウォレットを選択することができます。", "enter_any_input_hint": "上記のいずれかを入力してください", diff --git a/src/locales/strings/kaa.json b/src/locales/strings/kaa.json index dd63a06a38b..0c69ea551da 100644 --- a/src/locales/strings/kaa.json +++ b/src/locales/strings/kaa.json @@ -1086,7 +1086,8 @@ "personalize_wallet_title": "Personalize Your Wallet", "get_started_button": "Get Started", "not_now_button": "Not Now", - "scan_address_modal_title": "Enter Recipient FIO, ENS, or Public Address", + "scan_address_modal_title": "Enter Recipient Public Address", + "scan_address_modal_title_1s": "Enter Recipient %1$s or Public Address", "enter_any_title": "Enter any of the following:", "enter_any_body": "1. Public address to send money to\n2. Private key to sweep\n3. Wallet Connect URI to connect to a dApp\n4. Edge Login URI to login to another device\n\nEdge will auto-detect the URI format and allow you to select an appropriate wallet", "enter_any_input_hint": "Enter any of the above", diff --git a/src/locales/strings/ko.json b/src/locales/strings/ko.json index 8619ef0c6ef..76ea6b2b0bf 100644 --- a/src/locales/strings/ko.json +++ b/src/locales/strings/ko.json @@ -1086,7 +1086,8 @@ "personalize_wallet_title": "Personalize Your Wallet", "get_started_button": "Get Started", "not_now_button": "Not Now", - "scan_address_modal_title": "Enter Recipient FIO, ENS, or Public Address", + "scan_address_modal_title": "Enter Recipient Public Address", + "scan_address_modal_title_1s": "Enter Recipient %1$s or Public Address", "enter_any_title": "Enter any of the following:", "enter_any_body": "1. Public address to send money to\n2. Private key to sweep\n3. Wallet Connect URI to connect to a dApp\n4. Edge Login URI to login to another device\n\nEdge will auto-detect the URI format and allow you to select an appropriate wallet", "enter_any_input_hint": "Enter any of the above", diff --git a/src/locales/strings/pt.json b/src/locales/strings/pt.json index cd5ab747be8..c427287b0c1 100644 --- a/src/locales/strings/pt.json +++ b/src/locales/strings/pt.json @@ -1086,7 +1086,8 @@ "personalize_wallet_title": "Personalize Sua Carteira", "get_started_button": "Começar", "not_now_button": "Agora não", - "scan_address_modal_title": "Digite o destinatário FIO, ENS ou Endereço Público", + "scan_address_modal_title": "Digite o endereço público do destinatário", + "scan_address_modal_title_1s": "Digite o %1$s ou endereço público do destinatário", "enter_any_title": "Digite qualquer um dos seguintes:", "enter_any_body": "1. Public address para enviar dinheiro\n2. Private key para varrer\n3. Wallet Connect URI para se conectar a um dApp\n4. Edge Login URI para fazer login em outro dispositivo\n\nA Edge detectará automaticamente o formato URI e permitirá que você selecione uma carteira apropriada", "enter_any_input_hint": "Digite qualquer um dos itens acima", diff --git a/src/locales/strings/ru.json b/src/locales/strings/ru.json index ee728acf965..e31e18302cd 100644 --- a/src/locales/strings/ru.json +++ b/src/locales/strings/ru.json @@ -1086,7 +1086,8 @@ "personalize_wallet_title": "Personalize Your Wallet", "get_started_button": "Get Started", "not_now_button": "Not Now", - "scan_address_modal_title": "Введите FIO, ENS или публичный адрес получателя", + "scan_address_modal_title": "Введите публичный адрес получателя", + "scan_address_modal_title_1s": "Введите %1$s или публичный адрес получателя", "enter_any_title": "Enter any of the following:", "enter_any_body": "1. Public address to send money to\n2. Private key to sweep\n3. Wallet Connect URI to connect to a dApp\n4. Edge Login URI to login to another device\n\nEdge will auto-detect the URI format and allow you to select an appropriate wallet", "enter_any_input_hint": "Enter any of the above", diff --git a/src/locales/strings/vi.json b/src/locales/strings/vi.json index 690f127d552..7c75d3232d2 100644 --- a/src/locales/strings/vi.json +++ b/src/locales/strings/vi.json @@ -1086,7 +1086,8 @@ "personalize_wallet_title": "Personalize Your Wallet", "get_started_button": "Get Started", "not_now_button": "Not Now", - "scan_address_modal_title": "Enter Recipient FIO, ENS, or Public Address", + "scan_address_modal_title": "Enter Recipient Public Address", + "scan_address_modal_title_1s": "Enter Recipient %1$s or Public Address", "enter_any_title": "Enter any of the following:", "enter_any_body": "1. Public address to send money to\n2. Private key to sweep\n3. Wallet Connect URI to connect to a dApp\n4. Edge Login URI to login to another device\n\nEdge will auto-detect the URI format and allow you to select an appropriate wallet", "enter_any_input_hint": "Enter any of the above", diff --git a/src/locales/strings/zh.json b/src/locales/strings/zh.json index 999096147e5..86168d73c17 100644 --- a/src/locales/strings/zh.json +++ b/src/locales/strings/zh.json @@ -1086,7 +1086,8 @@ "personalize_wallet_title": "Personalize Your Wallet", "get_started_button": "Get Started", "not_now_button": "Not Now", - "scan_address_modal_title": "Enter Recipient FIO, ENS, or Public Address", + "scan_address_modal_title": "Enter Recipient Public Address", + "scan_address_modal_title_1s": "Enter Recipient %1$s or Public Address", "enter_any_title": "Enter any of the following:", "enter_any_body": "1. Public address to send money to\n2. Private key to sweep\n3. Wallet Connect URI to connect to a dApp\n4. Edge Login URI to login to another device\n\nEdge will auto-detect the URI format and allow you to select an appropriate wallet", "enter_any_input_hint": "Enter any of the above",