Token Lists
A token list is a JSON document that maps chain identifier and contract address to a name, symbol, decimals count and logo URL. It is the only registration route where you are the publisher rather than the applicant: you write the file, you host it, and a user adds it to their interface by pasting a URL. Uniswap wrote the specification in 2020 and most swap interfaces and several wallets read the format.
The document#
|
|
name, timestamp, version and tokens are required at the top level; a token entry requires chainId, address, decimals, name and symbol. Everything else, logoURI included, is optional as far as the schema is concerned and mandatory as far as the reader is concerned.
Four constraints reject more lists than anything else:
- The list
nameis capped at 30 characters and matched against^[\w ]+$. Letters, digits, underscores and spaces only — an em dash, an ampersand or a hyphen in your project name fails validation. addresscasing must stay stable between publishes. The schema itself accepts any casing, but consumers key tokens by the exactchainId+addressstring, so recasing an address reads as a removal plus an addition and forces a major version bump — a breaking-change warning shown to every user, for no change at all. Pick the EIP-55 checksummed form and never touch it again.timestampmust be a date-time string, and it has to move forward on every publish.- Token
nameis capped at 60 characters andsymbolat 20.
Versioning is a protocol, not a courtesy#
Consumers diff versions to decide whether to warn the user, so the three numbers carry defined meanings:
| Change | Bump |
|---|---|
| A token is removed, or an address or chain identifier changes | major |
| A token is added | minor |
Name, symbol, decimals or logoURI of an existing token changes |
patch |
Changing an address is a removal plus an addition, so it is a major bump, not a patch. Interfaces surface a major bump to the user as a potentially breaking change and a patch silently — which is exactly what you want when you are only swapping in a better icon.
Validate before publishing#
The schema ships in the package, so validation is four lines and belongs in continuous integration:
|
|
Reach into src/ for the schema rather than the package root. The upstream documentation shows import { schema } from '@uniswap/token-lists', which resolves under a bundler consuming the TypeScript sources but throws under Node: the published tarball declares main: dist/index.js and ships no dist/.
A list that fails validation is not partially loaded; the interface refuses it whole and shows the user an error, so one malformed entry takes down every token on the list.
Hosting#
Three options, in ascending order of durability:
- An HTTPS URL. Simplest, and it needs a permissive cross-origin resource sharing (CORS) header — the fetch is made by a browser from someone else’s origin, so without
access-control-allow-originthe list silently fails to load. This is the single most common reason a self-hosted list “doesn’t work.” - IPFS, pinned. Content-addressed, so the hash changes on every publish and consumers pinned to the old hash keep the old list.
- An Ethereum Name Service (ENS) name with a
contenthashrecord pointing at the IPFS hash. The specification’s preferred form: the name is stable, the content underneath it is not, and updating the list is a transaction rather than a server deploy.
Uniswap’s interface accepts all three under Manage → Lists: a URL, an ENS name, or a raw IPFS hash.
Your list is not the default list#
Publishing a list makes the token available to anyone who imports it. It does not put the token in front of anyone who does not. The list shipped enabled in the Uniswap interface is a separate, curated repository with its own criteria, and there is no submission path from one to the other.
That is a smaller limitation than it sounds. The list URL is a link you can put in a Discord pin, a docs page, or the swap button on your own site, and users who arrive by that link see the correct name and icon before they have transacted. Combined with wallet_watchAsset, it covers the audience that matters on launch day, which is people who already came to you — and it covers them without approval from anyone.