web3icons

Fake tokens and spoofed logos: designing for safety

Why a token logo proves nothing, how scam tokens copy names and tickers, and how to use contract addresses, verified lists and warnings to protect people.

5 min read

Anyone can deploy a token contract and give it any name, any ticker and any number of decimals. Calling it "USD Coin" with the ticker USDC takes no permission from anyone on-chain. Attaching the real USDC logo takes even less: the image is public, and icon libraries, including this one, distribute well-known logos freely. A token that looks exactly like the real one in a wallet list can be completely worthless, or part of a scam.

So interfaces have to identify tokens by something that can't be copied, and treat names, tickers and logos as labels, not proof. This guide covers how scam tokens work, how to identify tokens reliably, and what to show when a token isn't known.

What a fake token copies

Scam tokens imitate whatever people use to recognize the real one:

  • The name and ticker. Exact copies, or near copies with lookalike characters and extra spaces.
  • The logo. Pulled from the real project's press kit, a token list, or an icon library.
  • The decimals. Matching the original so amounts look normal.
  • The context. Sent as an unsolicited airdrop, so it simply appears in the wallet, often with a name that includes a URL ("Claim rewards at …").

None of these copied traits change the one thing that identifies a token: its contract address on a specific chain.

A logo is decoration, not verification

This point deserves to be stated plainly, because the interface implies the opposite whenever it shows a logo next to an unknown token. A logo tells the person "this is the asset you know." If the token isn't verified, the logo is making a claim nobody checked.

The fix is simple: only show a brand logo for tokens you have verified. For anything else, use a neutral fallback, such as initials on a gray circle, so an unverified token never borrows the credibility of a real one. Fallback icons for tokens without a logo covers how to design those.

Identify tokens by chain and address

Everywhere in your code, key tokens by chain ID and address, never by ticker or name. Two tokens with the ticker USDC on the same chain are two different assets, and only the address tells them apart.

type TokenKey = `${number}:${string}`

function tokenKey(chainId: number, address: string): TokenKey {
  return `${chainId}:${address.toLowerCase()}`
}

const verified = new Map(
  trustedList.tokens.map((t) => [tokenKey(t.chainId, t.address), t]),
)

function displayToken(chainId: number, address: string, onchain: { symbol: string; name: string }) {
  const known = verified.get(tokenKey(chainId, address))
  return known
    ? { ...known, trust: 'verified' as const }
    : { ...onchain, logoURI: undefined, trust: 'unknown' as const }
}

Lowercasing makes the comparison work for addresses that arrive in different casing. Display the address in its checksummed form, as described in displaying wallet addresses.

Verified lists, and what "verified" means

The usual source of verified tokens is a curated token list: a JSON file in the format described at tokenlists.org, listing chain ID, address, name, symbol, decimals and a logoURI for each token. Many teams combine a few lists they trust with their own allowlist.

Be precise about what your trust levels mean, and make the interface match:

Level What it means How to show it
Verified On a list you curate or trust Brand logo, no warning
Imported The person added it themselves Fallback icon or their chosen logo, "Imported" label
Unknown Arrived in the wallet, on no list Fallback icon, warning, hidden by default if suspicious

Don't use the word "verified" or a check mark unless there's a defined process behind it. A badge that means "we found this token's logo somewhere" is worse than no badge. More on where list logos come from, and the risks of displaying third-party images, is in token lists and logoURI.

Warn before importing unknown tokens

People sometimes need tokens that aren't on any list: a new project, a small community token. Let them import by pasting the contract address, and use that moment to inform them, not to block them.

A good import screen shows:

  • The name, ticker and decimals read from the contract, clearly labeled as reported by the contract itself.
  • The full contract address and the chain, with a link to the block explorer.
  • A plain warning: "Anyone can create a token with any name and logo. Make sure this is the token you meant to find."
  • If a verified token with the same ticker exists, say so: "This is not the USDC on your verified list."
  • An explicit confirmation, such as a checkbox or a button that reads "Import anyway".

After import, keep the "Imported" label on the token wherever it appears, including in the token selector.

Handle airdropped spam

Unsolicited tokens are a common delivery mechanism for scams. They appear in the balance list and activity feed, and the names often contain links or instructions. Some patterns help:

  • Hide unknown tokens by default in a collapsed "Hidden" section with a count, so they're available but not prominent.
  • Never render token names as links, even when they contain a URL. Display them as plain text, and consider truncating names that contain domains.
  • Flag common spam signals, such as URLs or "claim" wording in the name, zero-value transfers, or no tradable market. Treat these as signals for sorting and warning, not as proof.
  • Don't offer actions on hidden tokens beyond "Unhide", "View on explorer" and "Report". A prominent "Swap" button on a scam token pushes people toward the scam's site or contract.

When the real token has twins

Some legitimate assets have several versions on one chain: a native issuance and a bridged one, or an old contract and a migrated one. These aren't scams, but the same confusion applies. Show a distinguishing label next to the ticker, such as "Bridged", use the address to tell them apart, and avoid showing two rows that look identical. The same principle runs through the whole topic: identity comes from the address, and everything else in the row should help the person see which address they're dealing with.

More in Web3 UX

All guides