ERC-721 dynamic metadata
The tokenURI trap.
tokenURI returns one string. Dynamic NFTs depend on whatever server is hosting the JSON.
tokenURI(tokenId) returns (string) getDataForTokenId(tokenId, key) If you're dealing with ERC-721 dynamic metadata, the LUKSO route is LSP8 + LSP4. LSP8 stores per-token metadata through ERC-725Y data keys, not one tokenURI string. Apps read getDataForTokenId(tokenId, key) and get a typed value back. Dynamic updates are setData calls, gated by whatever account permissions you want.
Why this breaks
tokenURI(tokenId) returns one string. The contract has no opinion on what changes, when,
or by whom. If metadata is dynamic, the truth lives in whatever server hosts the JSON — and
marketplaces, wallets, and explorers each cache it on their own schedule.
“Refresh metadata” buttons exist because there is no standard signal for what changed.
ERC-4906 added a MetadataUpdate event, but consumers still have to ask the server what the
new state actually is.
What people try
Mutable IPFS pointers
Convenient. The JSON is still off-chain and trust-anchored to whoever can repin the CID.
Server-rendered metadata APIs
Works at scale. Couples the NFT to your infrastructure. Falls over when the server does.
ERC-4906 MetadataUpdate events
Signals “something changed.” Doesn’t say what changed, doesn’t carry the new state.
Fully on-chain SVG generators
Strongest integrity. Expensive. Awkward for rich media. Right answer for a narrow set of collections.
How LSP solves it
LSP8 assets store per-token metadata through ERC-725Y data keys, not through one tokenURI
string. LSP4 defines metadata conventions (name, symbol, JSON schema). VerifiableURI lets
a key point to an off-chain payload with a hash, so the chain enforces what the off-chain
blob must be.
Apps read getDataForTokenId(tokenId, key) and get a typed value back. Dynamic updates are
setData calls, gated by whatever account permissions you want. The metadata stops being a
URL — it becomes a typed key-value graph.