web3icons

Displaying wallet addresses: truncation, names and copy

How to truncate wallet addresses safely, keep EIP-55 checksum casing, show ENS names and avatars, build copy buttons, and limit address poisoning risk.

5 min read

An Ethereum address is 42 characters: 0x followed by 40 hexadecimal digits. Nobody reads it, nobody remembers it, and it doesn't fit in a header. So interfaces shorten it, replace it with a name, or hide it behind a copy button. Each of those choices is fine on its own, and each can make it easier for someone to send funds to the wrong place.

This guide covers how to truncate, when to show the full address, how to handle checksum casing and ENS names, how to build a copy button people trust, and why truncation is exactly what address poisoning attacks exploit.

Keep the checksum casing

Addresses are often shown in mixed case, like this:

0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed

That casing is not decoration. EIP-55 encodes a checksum in the capitalization of the letters, derived from a hash of the lowercase address. A wallet or library can detect most typos in a mixed-case address because the casing no longer matches.

Two rules follow. Display addresses in their checksummed form, the way wallets and explorers do. Compare addresses case-insensitively, by lowercasing both sides, because the same address can arrive from an API in all lowercase. Never uppercase an address for styling with text-transform; it destroys the checksum information people may be comparing against.

Truncation patterns

A shortened address keeps the start and the end, joined by an ellipsis. How many characters you keep depends on how much room you have and how much is at stake.

Pattern Example Where it fits
4 + 4 0x5aAe…eAed Avatars, compact headers, dense tables
6 + 4 0x5aAeb6…eAed Account menus, activity rows
6 + 6 0x5aAeb6…1BeAed Contact lists, recipient pickers
Full, grouped 0x5aAe b605 3F3E 94C9 … Send confirmation, receive screens

A small helper covers the first three. The counts below refer to hex digits after the 0x prefix, which matches how people compare addresses:

function shortAddress(address: string, head = 4, tail = 4): string {
  if (address.length <= 2 + head + tail) return address
  return `${address.slice(0, 2 + head)}…${address.slice(-tail)}`
}

shortAddress('0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed') // '0x5aAe…eAed'
shortAddress('0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed', 6, 6) // '0x5aAeb6…1BeAed'

Use a real ellipsis character (…) rather than three periods, and a monospace or tabular font for addresses so the characters line up when two are shown together.

Address poisoning and what truncation hides

Address poisoning works like this. An attacker watches for transfers between two addresses, generates a new address whose first and last few characters match one of them, and sends a zero-value or dust transfer from it. The fake address now appears in the victim's activity feed, truncated to look identical to the real one. The next time the victim copies "the usual address" from history, they copy the attacker's.

The math is unforgiving. Matching four hex characters at each end means matching 8 characters, or 16⁸, about 4.3 billion possibilities. That's a lot of attempts, but well within reach of address generation tools running on ordinary hardware. Matching 6 + 6 multiplies the work by about 65,000, which makes it much harder without making it impossible.

What helps in the interface:

  • Show the full address on any screen where money is about to move. On the send confirmation, display all 40 characters, grouped in fours so they can be compared chunk by chunk. Grouping should be visual only (spacing or separate spans), so a copy still produces the unbroken address.
  • Flag first-time recipients. "You haven't sent to this address before" is a useful prompt for someone who thinks they're paying a familiar contact.
  • Treat zero-value and dust transfers as suspect. Hide them from the activity feed by default, or mark them clearly and make their addresses harder to copy.
  • Encourage saved contacts and names over history. A label the person set themselves is safer than a truncated string they recognize by shape.

The same lookalike problem affects tokens, which fake tokens and spoofed logos covers.

ENS names and avatars

ENS lets an address have a readable name such as alice.eth and an avatar. Showing a name in place of an address is a large readability improvement, provided you resolve it correctly.

To show the name for an address, use reverse resolution to get the primary name, then resolve that name forward and check it points back to the same address. Anyone can set a reverse record claiming any name, so the forward check is what makes the name trustworthy. Most Ethereum client libraries have a helper that does both steps; confirm that yours does.

When you display a name, keep the address reachable: show it on hover and focus, in the account menu, and always on confirmation screens. A name tells you who someone claims to be. The address is what the transaction actually uses.

Avatars come from the name's avatar text record, which can point to an HTTPS URL, an IPFS resource or an NFT. Load them like any third-party image: with a fixed size so the layout doesn't shift, a neutral fallback while loading, and a generated pattern or initials when there's no avatar at all.

A copy button people trust

Copying is the most common thing people do with an address, so the button should be obvious, labeled, and confirm success.

import { useState } from 'react'

function CopyAddress({ address }: { address: string }) {
  const [copied, setCopied] = useState(false)

  async function copy() {
    try {
      await navigator.clipboard.writeText(address)
      setCopied(true)
      setTimeout(() => setCopied(false), 1500)
    } catch {
      setCopied(false)
    }
  }

  return (
    <>
      <button type="button" onClick={copy}>
        {copied ? 'Copied' : 'Copy address'}
      </button>
      <span role="status" className="visually-hidden">
        {copied ? 'Address copied' : ''}
      </span>
    </>
  )
}

Copy the full checksummed address, never the truncated text that's on screen. The visible "Copied" swap confirms the action for sighted users and the status region announces it to screen reader users. If the clipboard write fails, which can happen on insecure origins or when browser rules about user activation aren't met, show the full address selected so it can be copied manually. Copy to clipboard: browser rules and pitfalls explains those rules.

If the button shows only an icon, it still needs an accessible name, such as aria-label="Copy address". Screen readers also deserve a better reading of the address itself than a string of letters and digits; screen readers and long crypto addresses covers how to give truncated addresses a useful accessible name.

Quick checklist

  • Display checksummed, compare lowercased.
  • Pick a truncation length per context, and use 6 + 6 or more where people choose recipients.
  • Show the full, grouped address wherever funds move.
  • Verify ENS names with a forward lookup before showing them.
  • Copy the full address, confirm visibly and to assistive technology.
  • Hide or flag zero-value transfers from unknown addresses.

More in Web3 UX

All guides