Getting Metadata for Every Crypto Asset That Exists.

Getting Metadata for Every Crypto Asset That Exists.

masonchain

masonchain

2/24/2026

#api#metadata#portfolio#tokens
As a builder, I set off believing that building a pipeline to fetch the metadata for every crypto asset was going to be simple. Make a few onchain calls and we'd be done.
Token metadata had felt like a solved problem to me for years.
Names, symbols, decimals, images. A few RPC calls, some JSON parsing, a database table, and a cache. Nothing about it is costly or confusing, and most engineers have done this work before.
You wire it up, test it locally, and it works on the token you're using to test. Metadata resolves, images load, tokens render correctly in the UI. When something doesn't, it's easy to dismiss as a strange contract or an edge case you'll come back to later. MKRMKR was deployed before faviconERC-20 even existed, but that's a problem for future you. The rest of the product moves forward assuming this layer exists and behaves.
For us, this assumption held right up until our very first user appeared. Turns out, getting metadata for every crypto asset that exists is not simple. It's not even close.
The ERC-20 specification defines a name(), a symbol(), and a decimals() function. In practice, it guarantees none of them.
Compliance SlotsJackpot
9
8
7
6
5
4
3
2
1
0
9
8
7
6
5
4
3
2
1
0
.
9
8
7
6
5
4
3
2
1
0
9
8
7
6
5
4
3
2
1
0
9
8
7
6
5
4
3
2
1
0
,
9
8
7
6
5
4
3
2
1
0
9
8
7
6
5
4
3
2
1
0
$
1
2
3
4
5
6
7
8
9
10
name()
symbol()
decimals()
Mantle
REVERT
Wrapped Ether
MNT
REVERT
WETH
18
18
18
Contracts deployed before the standard was finalized return bytes32 instead of string for symbol(). Others omit decimals() entirely because it was never a required function. Proxy contracts change their implementation behind a stable address, breaking cached ABI assumptions without warning. Some contracts revert on metadata calls unless the caller is whitelisted.
None of these examples are hypothetical. These contracts hold liquid value and show up in user wallets. A metadata system either handles them or displays nothing.
Handling them means special-casing. Every special case is a conditional branch in your ingestion pipeline that exists permanently, because the contracts it targets will exist permanently. The number of edge cases only grows.
And it's not a handful of tokens. Anyone can deploy a token contract at any time on any chain. There is no registry, no approval process, no way to know in advance which contracts your users will hold. The surface area is unbounded and grows continuously. Metadata has to resolve for whatever shows up, the moment it shows up, with no prior knowledge that it exists.
The obvious response is to throw more resources at the problem. More workers, larger queues, higher rate limits, more aggressive retries. Costs increase, latency improves slightly, and the system looks a bit healthier. But by the time you've invested real resources into keeping metadata responsive, the product has started to depend on it. UI components assume it will be there. Portfolio views assume it will resolve quickly. Features are written as if metadata is cheap and fast and always available, because you've made it expensive enough for that to usually be true. What was specced as a vanity feature is now load-bearing.

Nonfungible Metadata Is a Different Problem Entirely

Eventually you'll find that many of your users hold NFTs. Major protocols like faviconUniswap still use NFTs for their liquidity positions, so choosing not to support them means choosing to deliver half an experience.
NFT metadata is structurally adversarial.
Every ERC-721 token exposes a tokenURI that points to a JSON document describing that specific token. Where that URI points is entirely up to the contract deployer. faviconIPFS. faviconArweave. faviconAn HTTP endpoint. faviconA base64-encoded data URI containing a raw SVG like Art Blocks uses. Each resolution path has its own failure mode, latency profile, and infrastructure requirements.
faviconIPFS gateways are unreliable, rate-limited, and slow. Public gateways throttle aggressively under load. Private gateways require pinning infrastructure you now have to operate. Arweave is more stable but resolves differently. HTTP endpoints go down, change their response format, or disappear entirely when a project shuts down. Onchain SVGs need to be decoded, rendered, and sanitized against embedded scripts.
Of all nonfungible tokens we've indexed, 43% no longer resolve to any data. When a request does succeed, the JSON isn't enforced by the standard. Contracts return image, or image_url, or animation_url with no image at all. Others nest the image three levels deep in a custom schema. Some return HTML. Some return nothing.
9
8
7
6
5
4
3
2
1
0
9
8
7
6
5
4
3
2
1
0
9
8
7
6
5
4
3
2
1
0
%
9
8
7
6
5
4
3
2
1
0
%
ERC-1155 introduces a token type system on top of this, with a URI pattern that uses {id} substitution. A single contract can hold thousands of token types, each with different metadata, resolved through the same URI template. The metadata for token ID 1 and token ID 10,000 may live in completely different places, returned by the same function call with different parameters.
Dynamic NFTs change their metadata based on onchain state. A gaming NFT that levels up. A generative piece that evolves. The cache is wrong the moment the state changes, and there's no reliable signal for when that happens.
Everything you built for fungible tokens (the queue, the cache, the retry logic) is wrong here. Different resolution paths, different failure modes, different invalidation requirements, and orders of magnitude more individual items to track.
You start to feel the scale of this problem once you realize every solution compounds with every other section in this post.

The Same Token on Every Chain

A token exists on Ethereum. The same token, representing the same asset, also exists on Arbitrum, Optimism, Base, Polygon, and whatever launched last week. Different contract address on every chain, different deployer, sometimes different metadata.
USDC

USD Coin

$4,218.84
Ethereum4,218.42USDC
+0.01%
USDC.e

Bridged USDC

$1,841.78
Arbitrum1,842.15USDC.e
-0.02%
USDC.e

Bridged USD Coin

$967.99
Optimism967.80USDC.e
+0.02%
USDC

USD Coin

$3,105.00
Base3,105.00USDC
0.00%
USDC

USD Coin (PoS)

$521.17
Polygon521.33USDC
-0.03%
ETH

Ether

$29,241.98
EthereumArbitrumOptimismBasePolygon
12.48ETH
+3.42%
USDC

USD Coin

$10,654.79
EthereumArbitrumOptimismBasePolygon
10,654.70USDC
+0.00%
WBTC

Wrapped Bitcoin

$55,692.12
EthereumArbitrumBase
0.82WBTC
+1.87%
DAI

Dai

$8,418.39
EthereumArbitrumOptimismBase
8,420.50DAI
-0.01%
stETH

Lido Staked Ether

$12,217.64
EthereumArbitrumOptimism
5.21stETH
+3.38%
There is no canonical registry that maps these together. Token lists attempt it, but they're maintained by volunteers, lag behind deployments, and conflict with each other. The Ethereum token list says one thing. The Arbitrum bridge says another. The project's own documentation says a third.
Bridged tokens add another layer. A token bridged through the canonical bridge has a different address than the same token bridged through a third-party bridge. Both represent the same underlying asset, with independent metadata, and both show up in user wallets.
The metadata system needs to understand that these are the same asset. Deduplication, canonical metadata selection, and conflict resolution across chains for contradicting data. When you're heads down getting one chain working, it's easy to write this off as an edge case. It isn't. This is every major token.

Images Are Infrastructure

Once you've resolved a URI and parsed the JSON, you have an image reference. It points to a file you don't control, in a format you didn't choose, at a size you can't predict.
PNGs can be 50 megabytes. SVGs can contain embedded JavaScript. GIFs can have thousands of frames. image fields that point to MP4 videos, GLB 3D models, or other JSON documents are not uncommon.
You are now operating an image pipeline. Fetching from hostile sources, transcoding between formats, enforcing size limits, sanitizing SVG content, generating thumbnails at multiple resolutions, and caching the results behind a CDN that needs its own invalidation logic.
PNG128×128
SVGscalable
PNG2048×2048
PNG64×64
MP4
SVG<script>
PNG50MB
PNG256×256
WEBP512×512
b64data URI
GLB
GIF847 frames
PNG32×32
IPFS
SVG480×480
ARArweave
404
JSON
AVIF320×320
PNG1024×1024
HTML
ICO16×16
Every token without an image needs a fallback. The fallback logic is per-standard, per-contract, sometimes per-token. An ERC-20 with no image gets a generated icon from its symbol. An NFT with no image is just broken.
On top of this, every chain, protocol, and token list expects different image dimensions and formats. The same token on Ethereum uses a different image than on Arbitrum. The protocol's image doesn't match the token list's image. Neither matches what the user expects to see.

Metadata That Doesn't Exist Until You Create It

Some assets have no metadata of their own because the concept doesn't apply to them the way a metadata system expects.
A Uniswap V2 LP token is an ERC-20 with a name() and symbol() that tell you nothing useful. To represent it meaningfully, you need to know it's a liquidity position, resolve the two underlying tokens, fetch their metadata, and composite an image that communicates the pair. The LP token itself has none of this information. You have to understand the protocol to construct it.
Yield vault tokens are similar. wstETHwstETH, cbETHcbETH, rETHrETH are all wrappers around underlying assets. The meaningful metadata belongs to the thing inside, not the wrapper the user holds. Displaying the wrapper's metadata is technically correct and completely useless.
Aave debt tokens, Compound borrow positions, and staked derivatives all require protocol-specific knowledge to represent. The metadata system can't just read from contracts. It needs to understand the relationships between them, determine what a position represents, and synthesize something a user can actually interpret in realtime.
AAVE · AAVE · AAVE ·
COMPOUND · COMPOUND ·
YEARN · YEARN ·
LIDO · LIDO · LIDO ·
SPARK · SPARK ·
AAVE · AAVE · AAVE ·
COMPOUND · COMPOUND ·
YEARN · YEARN ·
SPARK · SPARK ·
AAVE · AAVE · AAVE ·
COMPOUND · COMPOUND ·
LIDO · LIDO · LIDO ·
This is protocol interpretation at this point, not metadata resolution, and every protocol speaks its own language. This realization almost broke our team's appetite for solving it.

Spam, Impersonation, and Trust

Every wallet on every chain is an open inbox. Anyone can send tokens to any address with any name, any symbol, and any image.
Scam tokens flood user wallets with metadata designed to phish. The token name is a URL. The image mimics a legitimate project. The symbol matches a real asset. A metadata system that faithfully displays what the contract returns is a metadata system that faithfully displays phishing attacks.
Token impersonation means name, symbol, and image are not identity. Two contracts with identical metadata can represent completely different assets, one legitimate and one designed to steal funds. Without a trust layer on top of metadata (verification status, community reports, contract analysis), the scam renders alongside the real thing and the user has no way to tell.
BONUS
PHISH
SCAM
DAI
PHISH
NFT
WBTC
PHISH
HONEY
NFT
GEMS
CLAIM
DROP
WIN
WIN
VISIT
PUMP
stETH
APY
HONEY
PHISH
$BTC
CLAIM
DROP
HONEY
CLAIM
DAI
PUMP
SWAP
BONUS
USDC
BAIT
YIELD
NFT
FREE
DROP
BAIT
PRIZE
FREE
LINK
PHISH
100X
WIN
WBTC
LINK
GEMS
BONUS
BONUS
HONEY
WIN
$BTC
PRIZE
VISIT
BAIT
MOON
MINT
MINT
BAIT
MOON
WIN
BONUS
MINT
NFT
CLAIM
HONEY
BAIT
$ETH
RUG
USDC
PRIZE
DROP
FAKE
SCAM
stETH
PRIZE
BAIT
BAIT
MINT
HONEY
YIELD
VISIT
BONUS
GEMS
SNX
There is no universal signal for legitimacy. Token lists are incomplete, and the best practice is to not use one at all. Contract verification is voluntary. Social consensus is unstructured and constantly shifting. You just pick an approach and ship it.

Freshness Has No Strategy

Metadata is not static, and this goes well beyond NFTs changing their images. ERC-20 tokens rebrand. Names, symbols, and logos shift without any onchain signal. ERC-4626 vaults expose a share price that changes every block. The totalAssets and convertToAssets reads that define a vault position's value are live computations, not cached values. Even decimals on a proxy token can change if the implementation is upgraded.
For fungible tokens, the "metadata" that matters to users (price, market cap, supply, balance) is entirely offchain or derived from state that moves continuously. None of it lives in name() or symbol(). For vault tokens, the fields that define the position (share price, APY, underlying asset, strategy allocation) require multiple contract reads that are only valid for the block they were fetched in.
Rebasing tokens make this worse. stETHstETH, AMPLAMPL, OHMOHM: the balance itself changes without a transfer event. balanceOf returns a different number every block even though no transaction touched the address. Yield-bearing tokens have the same problem from a different angle. Aave's aTokens accrue interest continuously via an internal index. Compound's cTokens use an exchange rate that drifts. Every cached balance is stale the moment it's rendered. There is no event to subscribe to, no callback to register. The state just moves underneath you.
1.04820000
Actual
1.04820000
Cached
When do you re-fetch? On user access, and you've introduced latency into every page load. On a timer, and you're wasting resources refreshing metadata that hasn't changed while missing updates that have. On onchain events, and you've built an event indexing system for a problem that was supposed to be simple.
Users expect a refresh button. That button implies an invalidation strategy per source, per standard, per chain. It implies you can determine what's stale, where the fresh version lives, and how to replace the cached version without breaking whatever depends on it downstream.
Realtime updates for standards that were never designed for them is a contradiction. ERC-721 has no event for metadata changes. ERC-4906 attempted to add one, but adoption is minimal. ERC-20 has no concept of metadata updates at all. ERC-4626 vaults emit deposit and withdraw events but nothing for the share price changes between them.

The Incentives That Prevent Improvement

The companies that provide token metadata today are data providers. They sell API access to blockchain data as their core business. Their product is the feed itself.
This creates a structural misalignment. A data provider's quality bar is set by what customers complain about, not by what breaks their own product. When metadata is wrong or missing, the provider's app keeps running. The customer's app is the one that breaks. The feedback loop is a support ticket, not a crashed page.
Breadth gets prioritized over contextual accuracy. A provider can return metadata for a million tokens and consider the job done. Whether an LP token renders as a meaningful position or a vault wrapper surfaces its underlying asset is outside their scope. They return what the contract returns. What it means is your problem.
Every team building at the application layer ends up maintaining their own interpretation layer on top of their data provider's output. The provider handles resolution, the consumer handles meaning, and the gap between the two is where every product quality issue lives.
Plug exists on the other side of this gap. faviconEvery input in a Plug sentence is contextual. When a user selects a token to swap, the options they see (balances, prices, protocol positions, underlying assets) are drawn directly from the metadata pipeline. When the metadata is wrong, the Plug is wrong. When a position isn't interpreted correctly, the user can't build the strategy they came for.
Ethereum

Ethereum

9
8
7
6
5
4
3
2
1
0
9
8
7
6
5
4
3
2
1
0
9
8
7
6
5
4
3
2
1
0
.
9
8
7
6
5
4
3
2
1
0

ETH

$8,541.00

USD Coin

USD Coin

9
8
7
6
5
4
3
2
1
0
9
8
7
6
5
4
3
2
1
0
.
9
8
7
6
5
4
3
2
1
0
9
8
7
6
5
4
3
2
1
0
9
8
7
6
5
4
3
2
1
0
,
9
8
7
6
5
4
3
2
1
0

USDC

$4,250.00

Lido Staked ETH

Lido Staked ETH

9
8
7
6
5
4
3
2
1
0
9
8
7
6
5
4
3
2
1
0
9
8
7
6
5
4
3
2
1
0
.
9
8
7
6
5
4
3
2
1
0

stETH

$3,612.00

The metadata infrastructure behind Plug wasn't built to sell API access. It was built because our product doesn't work without it. The quality floor is set by whether a user can construct and execute a transaction that does what they intended.
The faviconPlug API exposes this infrastructure directly. Realtime balances, prices, DeFi positions, collectibles, and the token data needed to represent the actual state of a portfolio across every chain we support.
If you're building something that needs this data, faviconyou can play with our interactive documentation here without having to download a thing.