On-Chain Metadata
Two mechanisms let a token carry its own icon without a company’s approval: a metadata document referenced from the contract, and a wallet method your interface can call to push the icon at the user directly. Neither needs a form, a fee, or a queue, and both work in the same block the contract is deployed in.
ERC-1046: a tokenURI on a fungible token#
ERC-1046 is Final, and adds one function to ERC-20:
|
|
It resolves to a JSON document with an interop object saying which token interfaces the contract implements, plus the display fields:
|
|
interop is the only required field. decimals is left out on purpose: the standard says that for an ERC-20 whose decimals are 18 it is “NOT RECOMMENDED to include the decimals field”, and a value that is present must equal what decimals() returns, as must name and symbol.
The image fields are not interchangeable. icons is the token logo, and the standard’s constraints on it are hard ones: each icon “MUST have a height equal to its width” and “use a transparent background”. image, and its plural images, are specified with an aspect ratio between 1.91:1 and 4:5 inclusive, so they hold a header graphic, not a second copy of the icon. All three ask bitmaps to be 320 to 1080 pixels wide, which puts the 512-pixel render in icons and rules the 256-pixel one out of any of them.
Implementing it costs one constant string. A public constant generates the getter, so there is no storage slot and no SLOAD on read:
|
|
The supply goes to an address passed to the constructor, not to msg.sender. Deployed through the deterministic deployer — the usual way to get one address on every chain, and what Foundry uses for a CREATE2 deployment by default — msg.sender inside the constructor is the deployer contract, which cannot move tokens, so a _mint(msg.sender, …) would lock the entire supply there. The holder is part of the creation code, so the same holder on every chain gives the same address on every chain.
Point it at content-addressed storage — IPFS or Arweave — rather than a domain. A hardcoded constant on a finalized contract outlives the domain registration, and an unreachable metadata URL is worse than none, because it looks like abandonment. If the document may need to change, hold the string in storage behind an owner-gated setter and accept that you have added a privileged role someone now has to trust.
Almost no wallet or explorer fetches tokenURI on an ERC-20 today, and the field will not put a logo in MetaMask on its own. What it buys is a permanent, self-hosted assertion of the token’s own metadata that no registrar can revoke, get wrong, or lose in a migration — at a cost of about thirty thousand gas at deployment — 149 bytes of extra runtime code for the string above, at 200 gas a byte.
Pushing the icon at the wallet#
EIP-747 is also Final and defines a provider method that prompts the user to add a token. Your own interface calls it and the icon appears — no registry involved, no waiting.
|
|
The call returns true as soon as the request is recognized as valid, not when the user accepts it, so the boolean tells you nothing about whether the token was added.
The finalized text of the EIP pared the ERC-20 options down to address and an optional chainId, deferring name, symbol and image to the ERC-1046 document. MetaMask’s implementation still reads symbol, decimals and image from the call, and MetaMask is what runs on most of the machines that will execute this code. Send all four fields: the spec-compliant subset works everywhere, and the extra three are what actually renders the icon today.
The image may be an https: URL or a data: URI, and MetaMask’s guidance is no larger than 512 × 512 and 256 kB. A data URI removes the fetch, so the icon cannot fail to load because of a CDN outage or a cross-origin header; it also means changing the icon requires shipping new frontend code.
MetaMask recommends this method to token developers. Its contract-metadata repository, the pull-request route for getting an icon into the wallet, calls itself “effectively frozen” and points here — though it still merges submissions slowly, as registering on MetaMask describes. This call is the only MetaMask route that works on the day the token is deployed.
Chains where the icon is simply on-chain#
Ethereum’s omission is not universal. Sui’s coin standard creates a CoinMetadata object alongside the TreasuryCap when a currency is created, and it has an icon_url field:
|
|
Whoever holds the TreasuryCap can update the metadata afterwards, or freeze the object to make it permanent. Wallets and explorers read the field directly, so a Sui coin has a working icon at creation with no submission anywhere. On Solana, the Metaplex token metadata account holds a uri pointing at a JSON document whose image field carries the logo, which puts the resolution one hop off-chain but still leaves it entirely under the issuer’s control.
The practical consequence for a multi-chain deployment: the Ethereum side needs the whole registration campaign, and the Sui and Solana sides need one constructor argument.