On-chain art
The single biggest improvement over the original Fantums is that the art lives on-chain. No API. No IPFS pin. No server to go 502. When you call tokenURI(uint256), the contract assembles an SVG from on-chain bytes and base64-encodes it inline. Marketplaces read the bytes and render them. Forever.
The 502 lesson
The original Fantums collection stored only a per-token Keccak hash and a base URI of https://api.fantums.com/token/. Image rendering happened on a server. When the server died — and it did, and it has been returning 502 across all 10,860 tokens for months — the art effectively disappeared from the network. Of 10,860 PNGs, only 7 were ever publicly cached by Wayback Machine. The collection's visual identity, from a marketplace's point of view, was erased.
We don't repeat that mistake.
Pattern A — what we shipped
We considered two patterns:
Pattern A (cheap, recommended, shipped)
Store per-tier SVG templates in the contract as immutable bytes. Per-token traits (archetype, fluro variant, OG badge, hat/cape/item) pack into 4 bytes inside the existing Fantums struct. tokenURI() assembles the SVG from the template and trait inputs, base64-encodes it inline.
Pattern B (full on-chain pixel grid) Store the entire 32x32 pixel grid per token. ~1 KB per token × 10,860 = ~11 MB of contract storage. At Sonic gas rates this is $50k-$100k one-time. Too expensive for marginal gain over Pattern A.
Pattern A is the call. Templates live in contracts/lib/FantumRenderer.sol. Trait sprites live in a small lookup library next to it.
Gas math
- Mint hot path: unchanged. No extra writes happen at mint. The renderer reads from immutables.
- Read path (
tokenURI): ~30k extra gas per call vs a static URI. Paid by the caller (marketplace, indexer), not the user.
The trade-off is: every tokenURI call burns 30k more gas, in exchange for the collection never disappearing. Worth it.
What's stored on-chain per token
| Field | Bytes | Purpose |
|---|---|---|
archetype | 1 | Which of the 19 archetypes |
tier | 1 | Common / Uncommon / Rare / Epic / Legendary |
fluroVariant | 1 | Pumpkin / Pink / Blue / Green / Orange / Phantom / Holo / two-tone codes |
traits | 4 | Packed hat / cape / item / face-mod indices |
isOG | bit | Set true if minted via legacy window |
legacyDna | 32 | Ancestor FUM hash. Immutable. Forever. |
Combat stats (str/dex/con/int/wis/cha + weapon + ELO) live in a separate slot for fast read in Duel.sol. They're also fully on-chain.
What's in the templates
Each rarity tier has one SVG template baked in as immutable bytes:
bytes private constant TEMPLATE_COMMON = hex"..."; // Pumpkin Gold base
bytes private constant TEMPLATE_UNCOMMON = hex"..."; // Fluro Blue base
bytes private constant TEMPLATE_RARE = hex"..."; // Fluro Green base
bytes private constant TEMPLATE_EPIC = hex"..."; // Fluro Pink base
bytes private constant TEMPLATE_LEGENDARY = hex"..."; // Multi-variant template
The templates contain slots — {TRAIT_HAT}, {TRAIT_CAPE}, {TRAIT_ITEM}, {TRAIT_FACE} — that the renderer substitutes with the per-token trait sprites.
Trait sprite library
Trait sprites live in a small library indexed by traitId:
| Category | Count | Examples |
|---|---|---|
| Hats | 12 | top hat, beret, headphones, crown, halo, monk hood, horns, opera flower, bandana, jester cap, ringmaster, hood |
| Capes | 8 | velvet red, fur-lined, electric cyan, vinyl black, gold leaf, jester harlequin, opera tails, none |
| Held items | 12 | candelabra, microphone, dagger, scroll, music sheets, mask, torch, wand, vinyl disc, magnifying glass, baton, palette |
| Face mods | 8 | scar, eyepatch, monocle, glasses, blindfold, none, etc. |
Each sprite is a tiny byte string (a few dozen bytes) — they pack tightly into the library's bytecode.
The DNA hook
Every OG Reborn token has a non-zero legacyDna field. The renderer reads it and surfaces it as a trait:
Descendant of Fantum #146
DNA: 0x000000926c16...
The ancestor's tokenId is derivable from the hash by looking up the published data/fantum-snapshot/dna.json. Marketplaces with rich-trait support display this; basic marketplaces show it as a normal trait.
What this means for the holder
- Your Reborn cannot disappear from a marketplace because we hosted an image server.
- Your Reborn renders identically on every marketplace that supports
tokenURISVG rendering (which is all of them). - Off-chain rendering services can build cached versions, but those caches are decoration — the canonical art is on Sonic forever.
See also
- Art direction — the palette the renderer uses
- Rarity tiers — what each tier template looks like
- On-chain rendering — the technical deep-dive on Pattern A
- Security — the rest of the audit-grade improvements
Last updated: 2026-05-21