Files
explorer-monorepo/frontend/libs/frontend-ui-primitives/Address.tsx
defiQUG b87ebee6a1
Some checks failed
Deploy Explorer Live / deploy (push) Failing after 20s
Validate Explorer / frontend (push) Failing after 24s
Validate Explorer / smoke-e2e (push) Has been skipped
feat(explorer): dual-chain wallet metadata, native coin pricing, and UI refresh.
Add Chain 138 wallet network metadata and stats coin-price enrichment; sync frontend explorer SPA, command center, and address/token pages with backend config.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-19 16:16:17 -07:00

102 lines
3.2 KiB
TypeScript

import { useEffect, useState } from 'react'
import clsx from 'clsx'
import { getKnownDisplayName } from '@/utils/web3IdentityRegistry'
import { resolveEnsName } from '@/utils/ens'
interface AddressProps {
address: string
chainId?: number
/** Blockscout or caller-provided label (highest precedence). */
label?: string | null
showCopy?: boolean
showENS?: boolean
truncate?: boolean
className?: string
}
export function Address({
address,
chainId: _chainId,
label,
showCopy = true,
showENS = false,
truncate = false,
className,
}: AddressProps) {
const [copied, setCopied] = useState(false)
const [ensName, setEnsName] = useState<string | null>(null)
const registryLabel = getKnownDisplayName(address)
useEffect(() => {
if (!showENS || !address) return
let active = true
void resolveEnsName(address).then((name) => {
if (active) setEnsName(name)
})
return () => {
active = false
}
}, [address, showENS])
const primaryLabel = label || registryLabel || (showENS ? ensName : null) || null
const displayAddress = truncate
? `${address.slice(0, 6)}...${address.slice(-4)}`
: address
const handleCopy = async () => {
try {
await navigator.clipboard.writeText(address)
setCopied(true)
setTimeout(() => setCopied(false), 2000)
} catch {
setCopied(false)
}
}
return (
<div
className={clsx(
'flex min-w-0 items-start gap-2',
truncate ? 'flex-nowrap' : 'flex-wrap',
className
)}
>
<div className="min-w-0">
{primaryLabel ? (
<div className="truncate text-sm font-medium text-gray-900 dark:text-gray-100">{primaryLabel}</div>
) : null}
<span
className={clsx(
'min-w-0 font-mono text-sm leading-6 text-gray-900 dark:text-gray-100',
truncate ? 'truncate' : 'break-all',
primaryLabel ? 'text-xs text-gray-500 dark:text-gray-400' : '',
)}
>
{displayAddress}
</span>
</div>
{showCopy && (
<button
type="button"
onClick={handleCopy}
className="shrink-0 rounded-md p-1 text-gray-500 transition hover:bg-gray-100 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-gray-800 dark:hover:text-gray-200"
title="Copy address"
aria-label="Copy address"
>
{copied ? (
<svg className="h-4 w-4" viewBox="0 0 20 20" fill="currentColor" aria-hidden>
<path fillRule="evenodd" d="M16.704 5.29a1 1 0 0 1 .006 1.414l-7.25 7.313a1 1 0 0 1-1.42 0L4.79 10.766a1 1 0 1 1 1.414-1.414l2.546 2.546 6.544-6.602a1 1 0 0 1 1.41-.006Z" clipRule="evenodd" />
</svg>
) : (
<svg className="h-4 w-4" viewBox="0 0 20 20" fill="currentColor" aria-hidden>
<path d="M6 2.75A2.25 2.25 0 0 0 3.75 5v8A2.25 2.25 0 0 0 6 15.25h1.25V14H6A1 1 0 0 1 5 13V5a1 1 0 0 1 1-1h5a1 1 0 0 1 1 1v1.25h1.25V5A2.25 2.25 0 0 0 11 2.75H6Z" />
<path d="M9 6.75A2.25 2.25 0 0 0 6.75 9v6A2.25 2.25 0 0 0 9 17.25h5A2.25 2.25 0 0 0 16.25 15V9A2.25 2.25 0 0 0 14 6.75H9Zm0 1.25h5a1 1 0 0 1 1 1v6a1 1 0 0 1-1 1H9a1 1 0 0 1-1-1V9a1 1 0 0 1 1-1Z" />
</svg>
)}
</button>
)}
</div>
)
}