Skip to content

Instantly share code, notes, and snippets.

@rfikki
Last active May 23, 2026 15:38
Show Gist options
  • Select an option

  • Save rfikki/874c22ef69066f452cd157e5fd3fcd40 to your computer and use it in GitHub Desktop.

Select an option

Save rfikki/874c22ef69066f452cd157e5fd3fcd40 to your computer and use it in GitHub Desktop.

DINO Vault — Complete Collector's Guide

CollectibleTrust DAO LLC - A RMI Registered Not-For-Profit · DinoHook Protocol
The definitive reference for minting, evolving, battling, and thriving in the DINO ecosystem.


Table of Contents

  1. What Is DINO Vault?
  2. Getting Started
  3. Minting Your Dinosaur — The Commit-Reveal Process
  4. The Five Biomes — Lore, History and Bonuses
  5. All 28 Traits Explained
  6. Rarity System — Genus Groups and Scarcity Scores
  7. The Aging System — Your Dinosaur Evolves Over Time
  8. Trait Mutation — Changing Your Dinosaur's Anatomy
  9. Cybernetic Grafting — Alloy Finishes via Uniswap V4
  10. The 2015 Collectible Coin Badges
  11. TWAP Aura — The Living Market Indicator
  12. PvP Battle Arena
  13. Inter-Biome Battle Advantage
  14. Biome Migration
  15. Leaderboard and Volume Tracking
  16. Achievement Badges — Soulbound Proof of Legend
  17. Biome Loyalty Streaks
  18. Liquidity Provider Features
  19. Extinction Events — Survive or Be Fossilized
  20. Conservation Pledge — The Aye-Aye Fund
  21. Adaptive Contrast System — How the Art Stays Readable
  22. Wrapped DINO ERC-20 — The Protocol Token and Legacy Contract
  23. On-Chain Art — What You Are Actually Seeing
  24. Marketplace and Rarity Tools
  25. Frequently Asked Questions
  26. Quick Reference — All Trait Values

Appendices — Technical Reference


1. What Is DINO Vault?

DINO Vault is a fully on-chain, upgradeable NFT collection of 24 dinosaur genera living across 5 distinct biomes, each tied to a specific Uniswap V4 liquidity pool with real historical lore. Every DINO is:

  • Born with fair randomness — a two-step commit-reveal scheme prevents any miner, validator, or front-runner from predicting or manipulating traits before you reveal.
  • Rendered entirely on-chain — the SVG art and JSON metadata live inside the blockchain itself. No IPFS, no server, no single point of failure. Your DINO looks exactly as designed forever.
  • Genuinely evolving — traits change visually based on how long you hold, what you do on-chain, and what happens in the wider DINO economy.
  • Grounded in real 2015 history — each biome corresponds to a specific 2015 Ethereum token with documented historical significance. The lore is real.

Contract system

Contract Role
DinosaurVault ERC-721 NFT vault — minting, mutation, aging, biome logic
DinosaurArtEngine On-chain SVG + JSON metadata renderer
DinoStructures Shared type library — all enums, structs, traits
DinoSwapHook V4 hook — graft, battle, leaderboard, TWAP
DinoLiquidityManagerHook V4 hook — LP gating, loyalty, conservation
DinoPoolRegistryHook V4 hook — biome assignment on pool creation
DinoBattleArena PvP battles settled by Chainlink VRF v2.5
DinoAchievements Soulbound ERC-1155 milestone badges
DinoExtinctionTracker Epoch survival tracking, Merkle-proof extinction
DinoLeaderboard Swap volume accumulator
WrappedDinero ERC-20 DINO token wrapping legacy Dinero 2015 (1M supply, 2-decimal, 2015 Mist-era contract)

2. Getting Started

What you need

  • An Ethereum wallet (MetaMask, Rabby, Coinbase Wallet, or any EIP-1193 compatible wallet)
  • ETH for gas fees
  • 0.005 ETH incubation fee (mint fee)
  • 0.002 ETH per trait mutation (optional, post-mint)

The five pools

To mint a DINO you must specify which Uniswap V4 pool you are minting into. This permanently assigns your biome. The five launch pools are:

Pool Biome Symbol
DINO/ETH The Primordial Forge 🌋
DINO/USDC The Crystal Vault 💎
DINO/CC The Frontier Ruins 🏛
DINO/WMC The Standard Origin ⚗️
DINO/WAAC The Nocturnal Wilds 🦎

3. Minting Your Dinosaur

DINO Vault uses a two-step commit-reveal minting process. This is not a single transaction — it is a deliberate two-block design that guarantees manipulation-resistant randomness.

Why two steps?

If minting used a single transaction, a validator could see your transaction in the mempool and delay it until a block where the randomness gives them a desirable NFT. The commit-reveal scheme prevents this entirely.

Step 1 — Commit

  1. Choose a secret random number as your seed. Keep it private.
  2. Compute the commit hash off-chain:
    commitHash = keccak256(abi.encodePacked(yourSeed, yourWalletAddress))
    
    Most front-ends do this automatically and store the seed in your browser.
  3. Call commitSeed(commitHash) on DinosaurVault. Gas cost only — no ETH fee.
  4. Note the block number. You have at most 256 blocks (~51 minutes at 12s/block) to reveal before your commit expires.

Step 2 — Reveal and Incubate

  1. In a different block than your commit (at least 1 block later, at most 256 blocks later), call:
    revealAndIncubate(yourSeed, poolId)
    
    • yourSeed — the exact number from Step 1
    • poolId — the bytes32 ID of the V4 pool whose biome you want
  2. Attach exactly 0.005 ETH.
  3. Your DINO is minted with traits derived from keccak256(seed, block.prevrandao, tokenId).

Biome bonuses applied at mint

Biome Automatic bonus
Primordial Forge (DINO/ETH) +5% on top-tier rarity roll
Crystal Vault (DINO/USDC) 2× chance of TRANSPARENT finish
Frontier Ruins (DINO/CC) CC badge guaranteed
Standard Origin (DINO/WMC) WMC badge guaranteed
Nocturnal Wilds (DINO/WAAC) WAAC badge guaranteed; COMPOUND eyes minimum

hasDino badge at mint

If your wallet holds at least 1 legacy Dinero 2015 token at the moment of revealAndIncubate, your DINO automatically receives the DINO badge — no extra action required.

What if my commit expires?

Call commitSeed again with a fresh hash. The old commit is worthless once 256 blocks have passed.


4. The Five Biomes

Each biome is permanently assigned at mint based on your chosen pool. The biome determines your SVG background, certain trait probabilities, guaranteed badges, and battle advantages. It is the single most consequential permanent choice in the protocol.


🌋 The Primordial Forge — DINO/ETH

Lore: The genesis pool. ETH is not merely a token — it is the lifeblood of Ethereum itself, born when the genesis block was mined on July 30, 2015, the day the EVM first breathed. This is the origin of all blockchain. DINOs born here carry the mark of the first flame. The lava beneath them is the very code that created everything.

SVG: Near-black red background with a radial ember glow at the centre. Animated lava sparks drift upward from the base — always present, always moving.

Bonuses:

  • Genesis rarity uplift — the top-tier rarity roll threshold is reduced by 5 points. A Theropod normally needs roll ≥ 980 for PLASMA_GLOW; Forge DINOs need only ≥ 975. This is the best pool for chasing legendary finishes.
  • Epoch grace — Forge holders receive one free epoch survival per calendar year. The DAO excludes Forge wallets from the extinction Merkle root for one epoch per year automatically.
  • Lava spark animation — unique animated SVG layer not present in any other biome.

Adaptive rim light: Amber #ffdd88 — warm volcanic glow applied to dark body finishes.

LP gate: Must hold ≥ 0.1 ETH at time of LP deposit.


💎 The Crystal Vault — DINO/USDC

Lore: Stable, precise, unwavering. USDC represents Ethereum's maturation from an experimental network into genuine financial infrastructure. DINOs born here are carved from crystal: cold, geometric, perfectly preserved. The Vault aesthetic reflects USDC's role as Ethereum's bedrock of stability — the pool that never crashes, never moons.

SVG: Deep blue-black background with geometric crystal formations rising from the bottom and cold white light shafts descending from above. No animation — deliberate stillness.

Bonuses:

  • Transparent finish bonus — DINOs in the Crystal Vault have a 2× increased probability of rolling TRANSPARENT finish (the rarest ghost-like aesthetic). TRANSPARENT normally occupies approximately 5% of outcomes; Crystal Vault pushes it to ~10%.
  • No extinction grace — Stability demands consistent engagement. Crystal Vault holders receive no epoch grace at all.

Adaptive rim light: Ice blue #aaddff.

LP gate: Must deposit ≥ $100 USDC-equivalent.


🏛 The Frontier Ruins — DINO/CC

Lore: CurrencyCoin was deployed on September 8, 2015 — just 40 days after Ethereum's launch — making it a verifiable relic from the earliest "Frontier" era. The EIP-20 proposal officially cites its source repository as a direct building block to the ERC-20 standard. DINOs born here are archaeologists. They walk among ruins of the first smart contracts, where Vitalik's original currency.sol commits still echo in the bytecode.

SVG: Dark purple-black background with broken stone arches on either side. Ancient Ethereum bytecode etched into the walls in faint purple monospace text: 60806040526004361061003f..., function transfer(address to, uint256 value). A floor-level ground layer separates the ruins from the sky.

Bonuses:

  • CC badge guaranteed — every DINO born in the Frontier Ruins receives the CC (CurrencyCoin) badge regardless of their rarity roll. You cannot mint here without earning this badge.
  • Fossil acceleration — when a Frontier Ruins DINO is marked extinct, it is eligible for the Fossilized state in 90 days rather than 180. The ruins accelerate decay into permanence.

Adaptive rim light: Violet #cc88ff.

LP gate: Must hold ≥ 1 CC token at time of LP deposit.


⚗️ The Standard Origin — DINO/WMC

Lore: MistCoin was deployed on November 3, 2015 by Fabian Vogelsteller and Alex Van de Sande as a prototype to test the concept of standardised token creation. Sixteen days later, Vitalik Buterin and Fabian proposed the ERC-20 standard. Every standard token on Ethereum — USDC, UNI, LINK, and billions more — traces back to this code. WMC liquidity is locked until November 3, 2115. DINOs born here inherit the standard.

SVG: Dark blueprint-blue background covered with a precise orthogonal grid of thin lines. Schematic annotation lines point to body parts with labels: "ERC-20 origin", "balanceOf()". The aesthetic is an architectural blueprint — the schematic that built everything.

Bonuses:

  • WMC badge guaranteed — every DINO born here receives the WMC (Wrapped MistCoin) badge automatically.
  • 25% mutation discount — tail and mouth mutations cost 25% less for Standard Origin DINOs. The fee is calculated as mutationFee × 75 / 100. Fabian's standard was about reducing barriers to participation.
  • Centennial LP prestige — LP badges in this pool display a "Centennial LP" variant in the achievement system, honouring the 100-year liquidity lock.

Adaptive rim light: Teal #44ffee.

LP gate: Must hold ≥ 1 WMC token at time of LP deposit.


🦎 The Nocturnal Wilds — DINO/WAAC

Lore: AyeAyeCoin was the first memecoin on Ethereum, deployed on August 20, 2015 — just three weeks after Ethereum's launch. Named for the aye-aye, a rare nocturnal lemur from Madagascar with enormous eyes and a long middle finger, its entire supply of 6 million coins sat dormant in a faucet for nearly nine years. On May 26, 2024, a rediscovery post ignited a rush that over three days made the faucet contract one of the biggest gas consumers on Ethereum. DINOs born here are survivors. They waited in the dark for nine years before the jungle found them.

SVG: Near-black green background with a dark canopy silhouette along the top edge. Two pairs of amber aye-aye eyes watch from the far-left and far-right shadows at low opacity — always present, always watching. Five animated bioluminescent firefly particles pulse in and out across the scene at randomised intervals.

Bonuses:

  • WAAC badge guaranteed — every DINO born here receives the WAAC (Wrapped AyeAyeCoin) badge automatically.
  • COMPOUND eyes minimum — the aye-aye is famous for its enormous eyes. All Nocturnal Wilds DINOs are born with at least COMPOUND eyes (rolls of ROUND or SLIT_PUPIL are automatically upgraded). GLOWING_RED eyes are 3× more likely.
  • 9-Year Sleeper — Nocturnal Wilds DINOs reach CHRONO_TITAN age bracket after 9 continuous years of holding in the same wallet, not the standard 1 year. This is the only CHRONO_TITAN path that honours the aye-aye's legendary 9-year dormancy. At 9 years, the SVG gains additional visual markers matching the discovery moment.
  • Animated fireflies — five unique bioluminescent particle animations not present in any other biome.

Adaptive rim light: Lime #ccff88.

LP gate: Must hold ≥ 1 WAAC token at time of LP deposit.


5. All 28 Traits Explained

Every DINO has exactly 28 traits stored in a single 32-byte EVM storage slot. All 28 are exposed in the OpenSea/marketplace attributes panel and indexed for rarity filtering.

Core anatomy (assigned at mint by randomness)

Trait Values Mutable?
Genus 24 dinosaur species No
Biome 5 (pool-assigned) Via migration only
Tail Shape 5 values Yes (traitId=0)
Mouth Type 6 values Yes (traitId=1)
Limb State 6 values No
Alloy Finish 12 values Via cybernetic graft
Primary Hide Color HSL hue 0–359 No
Underbelly Color HSL hue 0–359 No
Rarity Score 0–100 No

Cosmetic traits (assigned at mint)

Trait Values Notes
Eye Type 6 values Biome-influenced in NOCTURNAL_WILDS
Skin Texture 6 values Visible as SVG texture overlay
Headgear 7 values Rendered above skull in z-order
Eyewear 6 values Rendered over eye layer
Mouth Accessory 6 values Cigarette/pipe have animated smoke
Body Gear 6 values Chain, armour, hoodie, etc.
Footwear 5 values Sneakers, boots, rocket boosters
Wrist Accessory 5 values Watch, bracelet, hologram band

Status booleans (set by protocol events)

Trait Set by
Age Bracket Computed dynamically from hold time — never stored
Cybernetic Unlocked Cybernetic graft (swap hookData)
TWAP Aura Chainlink oracle via DinoSwapHook.afterSwap
Extinct DinoExtinctionTracker.claimExtinction
Fossilized Reserved (future)

Coin badge booleans (set at mint or by holding legacy tokens)

Badge Set by
hasWaac Mint in Nocturnal Wilds, OR Theropod roll ≥ 940
hasWmc Mint in Standard Origin, OR Sauropod roll ≥ 960
hasCc Mint in Frontier Ruins, OR any genus roll ≥ 995
hasDino Holding ≥ 1 legacy Dinero 2015 token at time of mint

6. Rarity System

Genus groups and rarity tables

Your genus index (0–23) determines which rarity table applies at mint:

Theropod Group (genus 0–5: T-Rex through Dilophosaurus)

Roll range Finish Score Notes
975–999 (2.5% with Forge bonus) PLASMA_GLOW 99 Forge pool lowers threshold by 5
900–974 (7.5%) CARBON 75
0–899 (90%) NONE 10

Sauropod Group (genus 6–11: Brachiosaurus through Camarasaurus)

Roll range Finish Score
995–999 (0.5%) GOLD 100
950–994 (4.5%) CHROME 80
0–949 (95%) WOOD 25

Armoured/Horned Group (genus 12–23: Ankylosaurus through Protoceratops)

Roll range Finish Score
990–999 (1%) MULTICOLORED 95
920–989 (7%) BRICK 60
0–919 (92%) NONE 15

Crystal Vault TRANSPARENT bonus

Crystal Vault DINOs whose roll falls between 600–699 (inclusive) and is an even number have TRANSPARENT finish applied instead of the table result, with a score of 88. This gives approximately a 5% additional probability of TRANSPARENT on top of natural outcomes.

Rarity post-mint

All 12 AlloyFinish values can be applied post-mint via Cybernetic Grafting, including GOLD, MULTICOLORED, and TRANSPARENT. Post-mint finishes also set cyberneticUnlocked = true, adding the cyan ring SVG overlay permanently.

The rarest natural mint

A GOLD Sauropod (0.5% roll × Sauropod genus probability) with a Rarity Score of 100 is the rarest single natural mint outcome.

The rarest achievable combination

A DINO with all four coin badges (WAAC + WMC + CC + DINO) — the "Full Set" — is statistically rarer than any single legendary finish, as it requires simultaneous roll conditions AND legacy Dinero 2015 holding at mint time. The badge panel displays a dashed gold outer ring when all four are present.


7. The Aging System

Your NFT's appearance changes automatically over time without you spending any gas. Every time tokenURI is called (which any marketplace, wallet, or block explorer does when displaying your DINO), the contract computes your current Age Bracket live from block.timestamp - lastTransferTimestamp.

Standard age thresholds

Bracket Hold time Visual markers
HATCHLING 0–29 days Default appearance
JUVENILE 30–89 days Slight maturity indicators
PREDATOR 90–179 days Stronger colouring, battle-ready stance
ELDER 180–364 days Weathered texture markers
CHRONO_TITAN 365+ days (standard) Full legendary appearance

The 9-Year Sleeper (Nocturnal Wilds only)

Nocturnal Wilds DINOs require 9 continuous years in the same wallet to achieve CHRONO_TITAN — honouring the aye-aye faucet's own 9-year dormancy. At 9 years, additional SVG visual markers appear matching the discovery moment lore. Standard pools reach CHRONO_TITAN in 1 year.

Aging clock reset

lastTransferTimestamp resets whenever your DINO is transferred to a new address. This includes:

  • Selling on a marketplace
  • Transferring to any other wallet (including your own hardware wallet)

It does not reset when you:

  • List for sale without completing a transfer
  • Approve a marketplace contract
  • Call tokenURI or view the NFT

8. Trait Mutation

After minting, you can change your tail shape or mouth type by paying the mutation fee.

Call mutateAnatomy(tokenId, traitId, newValue):

traitId Trait Valid newValue
0 Tail Shape 0–4
1 Mouth Type 0–5

Standard Origin discount

If your DINO's biome is STANDARD_ORIGIN, the mutation fee is 25% less — automatically applied by the vault. The effective fee is mutationFee × 75 / 100.

Tail Shape values

Value Name Rarity indication
0 STRAIGHT Common
1 CURVED Common
2 BOBBED Uncommon
3 SCARRED_WHIP Rare
4 THAGOMIZER Rare

Mouth Type values

Value Name
0 CLOSED
1 SMILEY
2 MISSING_TOOTH
3 GOLD_GRILLS
4 LIQUIDITY_JAWS
5 CRUSHING_BEAK

9. Cybernetic Grafting

Cybernetic Grafting lets you change your DINO's Alloy Finish to any of the 12 possible values — including GOLD, MULTICOLORED, and TRANSPARENT — by including special data in a Uniswap V4 swap. It also permanently sets cyberneticUnlocked = true, adding a cyan ring overlay to the SVG.

hookData format

hookData must be exactly 128 bytes, ABI-encoded:

abi.encode(
    uint8  action,    // 0 = graft
    uint256 tokenId,  // your DINO's token ID
    uint256 alloy,    // AlloyFinish index (0–11)
    uint256 epochId   // current extinction epoch ID, or 0
)

Example (ethers.js)

const hookData = ethers.AbiCoder.defaultAbiCoder().encode(
    ['uint8', 'uint256', 'uint256', 'uint256'],
    [0, myTokenId, 11, currentEpochId]  // action=graft, alloy=MULTICOLORED
);

AlloyFinish index reference

Index Finish Rarity
0 NONE Common
1 CHROME Uncommon
2 ANODIZED Uncommon
3 CARBON Rare
4 PLASMA_GLOW Very Rare
5 GOLD Legendary
6 BRICK Rare
7 WOOD Uncommon
8 FLUORESCENT Rare
9 BLUESCREEN Rare
10 TRANSPARENT Very Rare
11 MULTICOLORED Legendary

10. The 2015 Collectible Coin Badges

The four coin badges are a unique collectible-within-a-collectible mechanic honouring the specific 2015 Ethereum tokens whose pools form the DINO ecosystem. Each badge is a bool stored in the NFT's anatomy and rendered in a dedicated panel below the dinosaur body on the SVG canvas — never overlapping the body art.

Badge panel layout

The badge panel occupies y=760 to y=900 on the 1050-height canvas, separated from the body by a thin biome-accent coloured dividing line. Badge positions adapt automatically to how many badges a token holds:

Count Badge positions (cx values)
0 Dashed empty placeholder: "No badges earned"
1 Centred at cx=400
2 cx=320, cx=480
3 cx=240, cx=400, cx=560
4 cx=176, cx=336, cx=464, cx=624 with outer dashed gold ring

The four badges

WAAC — Wrapped AyeAyeCoin (gold, #bf953f)

  • First memecoin on Ethereum, deployed August 20, 2015 (21 days after Ethereum genesis)
  • Awarded: mint in Nocturnal Wilds (DINO/WAAC pool), OR Theropod genus with roll ≥ 940
  • Visual: gold foil radial gradient, "WAAC" text, sub-label "AyeAye"

WMC — Wrapped MistCoin (steel blue, #1a4a80)

  • Prototype for ERC-20, November 3, 2015
  • Awarded: mint in Standard Origin (DINO/WMC pool), OR Sauropod genus with roll ≥ 960
  • Visual: steel blue fill, "WMC" text, sub-label "MistCoin"

CC — CurrencyCoin (plasma purple, #2a0060 + neon glow)

  • Deployed September 8, 2015 (40 days after genesis), direct ERC-20 building block
  • Awarded: mint in Frontier Ruins (DINO/CC pool), OR any genus with roll ≥ 995
  • Visual: purple plasma fill with neon glow filter, "CC" text, sub-label "CurrencyCoin"

DINO — Wrapped Dinero 2015 (neon green, #002200 + #39ff14 stroke)

  • The protocol's own 2015 legacy token
  • Deployed December 10, 2015 by rfikki — 93 days after CurrencyCoin
  • Awarded: hold ≥ 1 legacy Dinero 2015 token at the moment of revealAndIncubate
  • Visual: dark green fill with neon green border and text, "DINO" text, sub-label "Dinero 2015"
  • This is the only badge that proves protocol participation — not pool selection or lucky randomness

Four-badge full set

When all four badges are present, a dashed gold outer ring encloses all four badges. Off-chain rarity tools flag this combination as the rarest achievable configuration in the entire collection.


11. TWAP Aura

The TWAP Aura is a collective, oracle-driven visual feature. After every swap in a DINO V4 pool, DinoSwapHook.afterSwap reads the Chainlink DINO/ETH price aggregator. If the price meets or exceeds the DAO-configured threshold and your hookData includes a tokenId, your DINO's twapAuraActive is set true — triggering a pulsing purple double-ring aura in the SVG art.

Visual effect

Two concentric purple circles with animated opacity (0.55 → 0.9 → 0.55, 2.5 second cycle) surround the body. The aura ring pulses continuously when active.

How to activate it

Include your tokenId in the second slot of swap hookData:

abi.encode(
    uint8(0),        // action = graft (or any action)
    myTokenId,       // id1 = tokenId for TWAP update
    0,               // id2 = not used for TWAP
    epochId          // epochId for extinction survival
)

When afterSwap fires, if tokenId != 0, the oracle is read and the flag is updated accordingly.

Clearing the aura

If the DINO/ETH price drops below the threshold, the next swap with your tokenId in hookData will set twapAuraActive = false and the aura disappears. This is a live market signal — not a permanent cosmetic.


12. PvP Battle Arena

The Battle Arena lets two DINO holders challenge each other to a fight settled by Chainlink VRF v2.5 randomness. Neither side can predict or manipulate the outcome.

Initiating a battle

Include battle hookData in a V4 swap:

const hookData = ethers.AbiCoder.defaultAbiCoder().encode(
    ['uint8', 'uint256', 'uint256', 'uint256'],
    [1, challengerTokenId, defenderTokenId, currentEpochId]
    // action=1 (battle), id1=your token, id2=opponent's token
);

Settlement

Settlement happens asynchronously — 3–5 blocks after challenge when Chainlink delivers the random word. Watch for the BattleSettled(vrfRequestId, winnerId, loserId, biomeAdvantageApplied) event.

Consequences

  • Winner: battleWins[tokenId]++ — permanent on-chain record
  • Loser: battleLosses[tokenId]++ — loss record. Your NFT is never taken or burned
  • Battle records are permanent and indexed by rarity tools

13. Inter-Biome Battle Advantage

When two DINOs from different biomes battle, the biome advantage cycle gives the winning biome +5 percentage points of VRF weight. This shifts the effective 50/50 roll toward 55/45.

The cycle

PRIMORDIAL_FORGE  → beats → CRYSTAL_VAULT    (fire melts crystal)
CRYSTAL_VAULT     → beats → FRONTIER_RUINS   (preservation defeats decay)
FRONTIER_RUINS    → beats → STANDARD_ORIGIN  (older code beats prototype)
STANDARD_ORIGIN   → beats → NOCTURNAL_WILDS  (code beats nature)
NOCTURNAL_WILDS   → beats → PRIMORDIAL_FORGE (the wild predates the forge)

Same-biome battles are pure 50/50. The advantage applies automatically in DinoBattleArena.rawFulfillRandomWords using the biomes stored in both NFTs' anatomies.

Strategic implications

  • If you want to challenge someone in STANDARD_ORIGIN, bring a FRONTIER_RUINS DINO for the edge
  • Maintain a NOCTURNAL_WILDS DINO specifically for challenging Forge holders
  • The cycle is symmetric — every biome has one it beats and one it loses to

14. Biome Migration

You can migrate your DINO to a new biome by paying the incubation fee (0.005 ETH) while your wallet is participating in the target pool. Migration is limited to once per epoch.

Call migratePool(tokenId, newPoolId) with 0.005 ETH attached.

What changes on migration

  • SVG background switches to the new biome immediately
  • Badge guarantees from the new biome do not retroactively apply — the badge booleans are set at mint and are permanent
  • Biome loyalty streak resets — migration is the antithesis of loyalty
  • The 9-Year Sleeper bonus (Nocturnal Wilds) applies to current hold time, not past

What does not change

All other traits — genus, finish, cosmetics, existing badges — are unchanged.


15. Leaderboard and Volume Tracking

DinoLeaderboard tracks cumulative swap volume (in absolute token units) for every address that swaps in any DINO V4 pool. Volume is recorded automatically by DinoSwapHook.afterSwap on every swap — no extra action required.

Reading your volume

Query userVolumeAccumulated(address) on DinoLeaderboard.

Epoch Alpha badges

Top-10 volume wallets per pool per epoch are eligible for pool-specific "Alpha" badge variants (e.g. "Forge Alpha", "Wilds Alpha") — awarded as soulbound ERC-1155 tokens by the DAO after each epoch closes. Alpha badges appear alongside coin badges in the 2015 COLLECTIBLES panel.


16. Achievement Badges

Badges are soulbound ERC-1155 tokens — permanently linked to the earning wallet, never transferable or sellable. All badge art is fully on-chain SVG.

Swap milestone badges (minted automatically)

Badge ID Name How to earn
1 First Swap First swap in any DINO V4 pool
2 Veteran Swapper 10 swaps
3 Apex Trader 100 swaps

LP badge (minted automatically)

Badge ID Name How to earn
4 Liquidity Provider First successful LP deposit in any DINO pool

Biome loyalty badges (DAO-awarded, off-chain verified)

Badge ID Name How to earn
6 Biome Loyal 3 epochs of continuous loyalty to one biome
7 Biome Devoted 7 epochs of continuous loyalty
8 Born of the Biome 10 epochs — the rarest badge in the collection

Loyalty streaks are computed off-chain from BiomeLoyaltyStamped events emitted by DinoLiquidityManagerHook. The DAO calls mintLoyaltyBadge(user, badgeId) when thresholds are confirmed.

Badge 5 (Battle Victor)

Reserved for a future release. Will be awarded by the battle system to holders with exceptional win records.


17. Biome Loyalty Streaks

Biome loyalty is measured in epochs. An epoch is a DAO-defined time period (typically 6 months). To maintain your loyalty streak, you must:

  1. Hold a DINO NFT from the same biome for the entire epoch
  2. Provide liquidity in that biome's pool at least once during the epoch
  3. NOT migrate to a different biome during the epoch

Streak benefits

Epochs Badge SVG effect
3 Biome Loyal Biome-accent glow border around the classification label
7 Biome Devoted Biome-accent crown ornament variant in classification area
10 Born of the Biome Permanent distinctive SVG frame unique to that biome

Migrating your DINO resets your loyalty streak to zero for the new biome.


18. Liquidity Provider Features

Access gate

You must hold at least one DINO NFT to add liquidity to any DINO V4 pool. The hook verifies your NFT balance in beforeAddLiquidity. If your balance is zero, the transaction reverts with HookEcosystemStakingGated.

Per-pool loyalty lock

After adding liquidity, you cannot remove it from that specific pool for 3 days. Each pool has its own independent lock — adding to Pool A does not affect Pool B's lock.

Re-adding liquidity to a pool resets only that pool's 3-day lock.

If you attempt early withdrawal, the transaction reverts with HookLoyaltyLockActive.

LP badge

Badge 4 (Liquidity Provider) is minted automatically on your first successful afterAddLiquidity in any DINO pool.


19. Extinction Events

Extinction Events are seasonal community challenges run by the DAO. During an active epoch, you must perform at least one swap with the active epochId in your hookData. Wallets that do not swap during the epoch may be permanently marked extinct.

How epochs work

  1. DAO opens epoch via DinoExtinctionTracker.openEpoch(epochId)
  2. During epoch: every swap with epochId in hookData slot 4 records your survival automatically
  3. DAO closes epoch: sets a Merkle root of all non-surviving tokenIds via setExtinctionRoot
  4. Claiming extinction: anyone can call claimExtinction(epochId, tokenId, proof) to mark a specific token

How to survive with one swap

const hookData = ethers.AbiCoder.defaultAbiCoder().encode(
    ['uint8', 'uint256', 'uint256', 'uint256'],
    [0, 0, 0, activeEpochId]  // epochId in slot 4 — graft/battle slots can be zero
);

A single swap with the correct epochId is sufficient to survive for all DINOs owned by that wallet.

Survival is wallet-wide

Survival is tracked per wallet, not per token. One swap covers all DINOs owned by that address.

Primordial Forge grace

Forge holders receive one free epoch survival per calendar year — the DAO excludes them from the extinction Merkle root for one epoch automatically.

Consequences of extinction

isExtinct = true is set permanently. A large red "EXTINCT" stamp with diagonal crossed lines overlays the SVG. Your DINO is NOT burned or removed from your wallet. Extinct DINOs become historically significant collectibles — each one is a timestamped record of a specific epoch.

Fossilized state

isFossilized is reserved for a future upgrade that will activate some time after extinction. Frontier Ruins DINOs fossilize at 90 days post-extinction; all others at 180 days (these timelines are DAO-governed).


20. Conservation Pledge — The Aye-Aye Fund

Honouring the AyeAyeCoin community's real-world aye-aye conservation efforts, DINO/WAAC pool participants can opt in to direct a share of LP fees to aye-aye conservation organisations.

How to pledge

Call DinoPoolRegistryHook.pledge() from any wallet.

What happens

  • conservationPledge[yourAddress] is set to true
  • On every subsequent LP deposit, the hook emits a ConservationLpStamped event
  • Future art engine upgrades will read this flag and render a 🌿 leaf overlay on your DINO's SVG — a permanent, visible, on-chain record of ecological contribution
  • Fee routing to conservation organisations is governed by the DAO

Revoking

Call DinoPoolRegistryHook.revokePledge() at any time.


21. Adaptive Contrast System

Certain DINO body finishes (NONE, CARBON, WOOD, BLUESCREEN) are very dark in colour. Without intervention, these bodies would visually blend into the near-black biome backgrounds. The art engine applies an automatic contrast system for these cases.

What is applied for dark bodies

  1. Biome spotlight — a radial gradient ellipse behind the body zone lifts local background luminance without affecting the rest of the scene
  2. Rim stroke — the body SVG paths receive a biome-accent coloured stroke (2.5px) instead of the default dark stroke
  3. Directional lighting — the fePointLight lighting colour shifts to the biome accent

Verified rim-light colours (≥ 8× WCAG contrast against all dark finishes)

Biome Rim colour Character
Primordial Forge #ffdd88 Warm amber — volcanic light
Crystal Vault #aaddff Cold ice blue
Frontier Ruins #cc88ff Ancient violet
Standard Origin #44ffee Blueprint teal
Nocturnal Wilds #ccff88 Bioluminescent lime

This system operates entirely inside the view function — zero gas cost, no new contracts.


22. Wrapped DINO ERC-20 — The Protocol Token

22a. The Legacy Dinero 2015 Contract — A Historical Artefact

Contract address: 0x374642afe485d1a181b01f5b028d169be58f3106

The Legacy Dinero 2015 contract was deployed by rfikki on December 10, 2015 — one of the earliest token contracts ever deployed on Ethereum. Written and styled after the 2015 Mist Browser token interface and directly inspired by the MistCoin contract code authored by Fabian Vogelsteller and Alex Van de Sande, it represents a piece of genuine Ethereum archaeology — code written before token standards existed, when developers were still figuring out what a "token" on Ethereum should even look like.

MistCoin itself was the prototype that led to ERC-20, deployed on November 3, 2015. The Legacy Dinero contract follows the same minimal, direct coding philosophy of that era: no inheritance hierarchies, no OpenZeppelin abstractions, no constructor complexity. Just a mapping, a total supply, and the essential functions needed to move value between addresses.

The legacy contract in full

The Legacy Dinero contract at the above address exposes exactly five functions — four read (view) functions and one write function:

Read functions

Function Returns Purpose
name() string Returns "Dinero" — the token's human-readable name
symbol() string Returns "DINO" — the token's trading ticker
decimals() uint8 Returns 2 — the token has 2 decimal places
balanceOf(address) uint256 Returns the raw balance of any address in smallest units

These four read functions are the minimal interface of a 2015-era Ethereum token. Calling them requires no gas and no wallet connection — any block explorer can call them freely. This mirrors the exact pattern used in the Mist Browser's token creation UI, which required a token contract to expose name, symbol, decimals, and balanceOf before the wallet would display it correctly.

Write function

Function Parameters Purpose
transfer(address to, uint256 value) recipient address, raw amount Moves tokens from the caller's balance to another address

This is the only write function on the legacy contract. There is no approve, no transferFrom, no allowance, no mint, no burn. The contract cannot be upgraded, paused, or modified in any way. Once deployed, it is immutable — perfectly consistent with the philosophy of 2015 Ethereum development, when simplicity and permanence were paramount.

The absence of transferFrom is particularly significant: it means the legacy contract is not compatible with modern DeFi protocols, including Uniswap V2/V3/V4, lending platforms, or any contract that requires approve → transferFrom to move tokens on a user's behalf. This is precisely why WrappedDinero exists.

Maximum supply

The total supply of Legacy Dinero is fixed at 1,000,000 DINO (one million tokens). This supply was set at deployment and is hardcoded — no minting function exists, so the supply can never increase. With 2 decimal places, the maximum supply in raw contract units is 100,000,000 (one hundred million smallest units), representing exactly 1,000,000.00 DINO.

This fixed supply of one million tokens, combined with the non-standard 2-decimal format, creates the scarcity and historical uniqueness that makes legacy Dinero a genuine 2015 collectible.

Why 2 decimal places?

Modern ERC-20 tokens use 18 decimal places by convention — a standard established formally by ERC-20 and modelled after ETH's own 18-decimal wei/ether relationship. However, in 2015 when Legacy Dinero was written, this convention was not yet established. The 2-decimal format was a pragmatic choice that mirrors real-world fiat currency conventions (dollars and cents, euros and cents) — intuitive for humans but non-standard by today's blockchain norms.

With 2 decimals, the smallest transferable unit of Legacy Dinero is 0.01 DINO (one cent-equivalent). You cannot send 0.001 legacy DINO — the contract stores only whole numbers of hundredths.

Why wrapping is necessary

Modern wallets (MetaMask, Rabby, Coinbase Wallet), DEX aggregators (1inch, Paraswap), and DeFi protocols (Uniswap, Aave, Compound) all expect tokens to conform to the ERC-20 standard as formalised after 2015. This means they expect:

  1. approve + transferFrom — so protocols can move tokens on your behalf
  2. 18 decimal places — so arithmetic across different tokens is consistent
  3. Standard event emissionsTransfer and Approval events with specific signatures

Legacy Dinero provides none of these. A wallet that doesn't know the token explicitly may display your balance incorrectly (showing it as if it had 18 decimals, making 1.00 DINO appear as 0.000000000000000001). An exchange cannot list it. A DEX cannot create a pool for it without a wrapper.

WrappedDinero solves all three problems in a single contract.

22b. The WrappedDinero Contract

WrappedDinero (on-chain name: "Wrapped Dinero 2015", symbol: DINO) is the modern ERC-20 wrapper that bridges Legacy Dinero 2015 into the contemporary DeFi ecosystem.

The decimal bridge

Legacy Dinero 2015 : 2 decimal places
Wrapped DINO       : 18 decimal places
Gap                : 18 - 2 = 16 decimal places
SCALING_FACTOR     = 10^16  (= 10,000,000,000,000,000)

Conversion:
  1 legacy unit (= 0.01 DINO displayed) → 10^16 DINO wei (= 0.01 DINO displayed)
  100 legacy units (= 1.00 DINO)        → 100 × 10^16 DINO wei (= 1.00 DINO displayed)

The key insight is that human-readable values are preserved exactly: 1.00 legacy DINO wraps to 1.00 wrapped DINO. The scaling factor only bridges the internal representation difference between 2-decimal and 18-decimal arithmetic. A collector who holds 500.00 legacy DINO will receive exactly 500.00 wrapped DINO after wrapping.

Wrapping (legacy → wrapped DINO)

  1. Approve the WrappedDinero contract to spend your legacy tokens using a direct transfer call to the contract's deposit address (since legacy Dinero has no approve, the wrapper uses a before/after balance snapshot pattern)
  2. Call wrap(legacyAmount) where legacyAmount is in raw legacy units (2-decimal):
    • To wrap 1.00 DINO: pass legacyAmount = 100
    • To wrap 500.00 DINO: pass legacyAmount = 50000
  3. Receive legacyAmount × 10^16 wrapped DINO wei in return

The contract measures your actual balance before and after the transfer, so fee-on-transfer tokens and any edge cases are handled correctly — you receive wrapped DINO equal to what the contract actually received, not just what you specified.

Unwrapping (wrapped DINO → legacy)

Call unwrap(wrappedAmount) where wrappedAmount must be exactly divisible by 10^16:

  • To unwrap 1.00 DINO: pass wrappedAmount = 10000000000000000 (= 10^16)
  • To unwrap 500.00 DINO: pass wrappedAmount = 5000000000000000000 (= 500 × 10^16)

Amounts not divisible by 10^16 revert with WrapperIndivisibleAmount. In practice, if you always wrap and unwrap whole DINO amounts and avoid fractional arithmetic in DINO wei, your balance will always be cleanly divisible.

Reserve protection

The totalLegacyLocked variable tracks the exact number of legacy DINO units held as reserves. The reclaimStuckLegacy function (owner only) can recover tokens accidentally sent directly to the contract — but only the surplus above totalLegacyLocked. Active reserves can never be drained by any function.

Maximum wrapped supply

Since Legacy Dinero has a fixed supply of 1,000,000.00 tokens (100,000,000 raw units), the theoretical maximum wrapped DINO supply is:

100,000,000 legacy units × 10^16 = 10^24 wrapped DINO wei
= 1,000,000.00 wrapped DINO (human-readable)

The 1:1 peg means wrapped DINO supply can never exceed 1 million tokens — guaranteed by the legacy contract's own immutable supply cap.

Wrapping and the hasDino badge

The vault checks legacyDineroAddress.balanceOf(caller) > 0 at the moment you call revealAndIncubate. If your wallet holds any amount of legacy Dinero 2015 — even 0.01 DINO (1 raw unit) — you receive the DINO coin badge on your newly minted NFT. Wrapped DINO (ERC-20) holdings are checked separately if the vault's legacyDineroAddress is pointed at the wrapper contract instead of the raw legacy address.

Strategy: Acquire at least 1 unit of legacy Dinero, ensure it is in your minting wallet, then call revealAndIncubate. The DINO badge will be set automatically — no separate claim needed.


23. On-Chain Art

Every DINO is rendered as an 800×1050 SVG divided into three vertical zones:

Zone 1: Dinosaur scene (y 0–750)

Layers in render order:

  1. <defs> block — all gradients, patterns, filters defined here
  2. Background — biome-specific scene with unique elements per biome
  3. Ground shadow ellipse — dark oval beneath the body for 3D depth perception
  4. Body geometry — genus-group-specific skeletal shape (theropod, sauropod, armoured)
  5. Specular highlight — white semi-transparent ellipse on the lit surface for roundness
  6. Skin texture overlay — fur strokes, feather curves, crystal spines, or scaly lines
  7. Eye layers — sclera + iris + pupil + specular dot (4 layers for depth)
  8. Headgear — drawn above skull in z-order
  9. Eyewear — overlaid on eye layer
  10. Mouth accessory — cigarette/pipe include animated smoke puffs
  11. Body gear — hoodie, chain, armour, etc.
  12. Footwear — at bottom of body, sneakers include white stripe detail
  13. Wrist accessory — on arm/limb area
  14. Cybernetic overlay — cyan circle when cyberneticUnlocked = true
  15. TWAP aura — animated pulsing purple rings when twapAuraActive = true
  16. Extinct overlay — red diagonal cross + EXTINCT text when isExtinct = true

Zone 2: Badge panel (y 760–903)

Separated from the dinosaur body by a biome-accent dividing line. Contains the 2015 COLLECTIBLES label and 0–4 coin badge circles. Badges never overlap the body art.

Zone 3: Classification label (y 904–1050)

Four lines of monospace text: genus and token ID, biome + finish + rarity score, age bracket + eye type + skin texture, and the CollectibleTrust protocol identifier.

Metadata format

tokenURI returns data:application/json;base64,... containing all 28 attributes.


24. Marketplace and Rarity Tools

All 28 traits are exposed as named attributes in the ERC-721 metadata. Marketplaces supporting attribute filtering (OpenSea, Blur, trait.tech, Rarity Sniper) will index and rank DINOs correctly.

Age Bracket caching

Because Age Bracket is computed live at tokenURI call time, marketplaces may cache an older bracket. Use the "Refresh Metadata" button on OpenSea to force a re-fetch. The live bracket is always visible by calling tokenURI directly.

Events to index (subgraph)

  • DinoIncubated(tokenId, owner, genus, biome, rarityScore) — mint
  • AnatomyMutated(tokenId, traitId, newValue) — mutation
  • CyberneticGrafted(tokenId, finish) — graft
  • TwapStateModified(tokenId, active) — TWAP
  • BiomeMigrated(tokenId, newBiome) — migration
  • ExtinctionMarkApplied(tokenId) — extinction
  • BattleSettled(vrfRequestId, winnerId, loserId, advantageApplied) — battle
  • BadgeMinted(recipient, badgeId) — achievement
  • BiomeLoyaltyStamped(provider, poolId, timestamp) — loyalty tracking
  • ConservationLpStamped(provider, poolId) — conservation pledge LP

25. Frequently Asked Questions

Q: Which pool should I mint in?
A: Primordial Forge for best rarity odds. Nocturnal Wilds for the WAAC badge and firefly art. Standard Origin for mutation discounts. Frontier Ruins for the CC badge. Crystal Vault for TRANSPARENT finish potential. There is no universally "best" choice — every biome has a distinct advantage.

Q: Can I get all four coin badges from one mint?
A: No. Each badge requires either a specific pool mint or a specific roll condition. WAAC requires Nocturnal Wilds OR Theropod roll ≥ 940. WMC requires Standard Origin OR Sauropod roll ≥ 960. CC requires Frontier Ruins OR any roll ≥ 995. DINO requires holding legacy Dinero at mint. The only way to guarantee all four in one DINO is to mint in a coin pool and separately achieve the other conditions — but badge guarantees are biome-specific. The full set strategy requires holding legacy Dinero, then minting in a pool that gives one badge guaranteed, and hoping roll conditions provide the others.

Q: Can I move my DINO to a hardware wallet without resetting the aging clock?
A: No. Any wallet-to-wallet transfer resets lastTransferTimestamp — including moves to your own hardware wallet. If you want to reach CHRONO_TITAN, keep your DINO in one wallet.

Q: Does migrating biomes change my coin badges?
A: No. Coin badges are booleans set at mint and are permanent. Migration changes the SVG background and battle advantage but never removes or adds badges.

Q: Can battle cause me to lose my NFT?
A: Never. Only win/loss counters change. Your DINO is always yours regardless of battle outcome.

Q: What if Chainlink VRF takes too long to settle a battle?
A: The challenge sits in inflightChallenges in a pending state. The swap that initiated it completed normally. Contact the DAO if a challenge is stuck for more than 100 blocks.

Q: How do I know if my wallet is pledged for conservation?
A: Call DinoPoolRegistryHook.conservationPledge(yourAddress) — returns true if pledged.

Q: What gas does minting cost?
A: Approximately 200,000–250,000 gas for revealAndIncubate, plus 0.005 ETH fee. commitSeed costs ~50,000 gas.

Q: Is the DINO badge the same as the DINO NFT collection ticker?
A: No. "DINO" refers to both the ERC-20 token (WrappedDinero, symbol DINO) and the coin badge. Context makes it clear: the badge panel shows the DINO coin badge; the ERC-721 collection ticker is "DINO-NFT".


26. Quick Reference — All Trait Values

GenusType (24 values, 0–23)

Theropods (0–5):  TYRANNOSAURUS, SPINOSAURUS, VELOCIRAPTOR, ALLOSAURUS, CARNOTAURUS, DILOPHOSAURUS
Sauropods (6–11): BRACHIOSAURUS, DIPLODOCUS, APATOSAURUS, TITANOSAURUS, ARGENTINOSAURUS, CAMARASAURUS
Armoured/Horned/Ornithopods (12–23):
  ANKYLOSAURUS, STEGOSAURUS, KENTROSAURUS, NODOSAURUS, SCELIDOSAURUS, MIRAGAIA,
  TRICERATOPS, PACHYCEPHALOSAURUS, PARASAUROLOPHUS, IGUANODON, MAIASAURA, PROTOCERATOPS

BiomeType (5 values)

0 PRIMORDIAL_FORGE  (DINO/ETH)   — volcanic lava
1 CRYSTAL_VAULT     (DINO/USDC)  — crystal cave
2 FRONTIER_RUINS    (DINO/CC)    — ancient bytecode walls
3 STANDARD_ORIGIN   (DINO/WMC)  — blueprint grid
4 NOCTURNAL_WILDS   (DINO/WAAC) — dark jungle + fireflies

AgeBracket (5 values — computed dynamically)

0 HATCHLING    (<30 days)
1 JUVENILE     (30–89 days)
2 PREDATOR     (90–179 days)
3 ELDER        (180–364 days)
4 CHRONO_TITAN (365+ days standard; 9 years for NOCTURNAL_WILDS)

TailShape (5 values — mutable, traitId=0)

0 STRAIGHT  1 CURVED  2 BOBBED  3 SCARRED_WHIP  4 THAGOMIZER

MouthType (6 values — mutable, traitId=1)

0 CLOSED  1 SMILEY  2 MISSING_TOOTH  3 GOLD_GRILLS  4 LIQUIDITY_JAWS  5 CRUSHING_BEAK

LimbState (6 values — assigned at mint)

0 INTACT  1 MISSING_TOES  2 ONE_ARMED  3 PEG_LEG  4 CYBERNETIC_ALLOY  5 OSTEODERM_ARMOUR

AlloyFinish (12 values — mutable via cybernetic graft)

0 NONE  1 CHROME  2 ANODIZED  3 CARBON  4 PLASMA_GLOW  5 GOLD
6 BRICK  7 WOOD  8 FLUORESCENT  9 BLUESCREEN  10 TRANSPARENT  11 MULTICOLORED

EyeType (6 values — biome-influenced at mint)

0 ROUND  1 SLIT_PUPIL  2 COMPOUND  3 GLOWING_RED  4 LASER_BEAM  5 HETEROCHROMIA

SkinTexture (6 values)

0 BALD  1 SCALY  2 FEATHERED  3 BRISTLE_FUR  4 FULL_FUR  5 CRYSTAL_SPINES

Headgear (7 values)

0 NONE  1 BACKWARDS_CAP  2 COWBOY_HAT  3 TOP_HAT  4 CROWN  5 FROG_HAT  6 FLAME_CROWN

Eyewear (6 values)

0 NONE  1 ROUND_FRAMES  2 AVIATORS  3 MONOCLE  4 VR_VISOR  5 GLASSES_3D

MouthAccessory (6 values)

0 NONE  1 CIGARETTE  2 PIPE  3 CIGAR  4 LOLLIPOP  5 TOOTHPICK

BodyGear (6 values)

0 NONE  1 HOODIE  2 CHAIN_NECKLACE  3 BATTLE_ARMOUR  4 TUXEDO_VEST  5 SPACE_SUIT

Footwear (5 values)

0 BARE_CLAWS  1 SNEAKERS  2 BOOTS  3 ROCKET_BOOSTERS  4 ROLLER_SKATES

WristAccessory (5 values)

0 NONE  1 WATCH  2 GOLD_BRACELET  3 HOLOGRAM_BAND  4 SPIKED_CUFF

hookData action field (128 bytes ABI-encoded)

0 = Cybernetic Graft  (id1=tokenId, id2=alloyIndex, epochId=epochId)
1 = PvP Battle        (id1=challengerId, id2=defenderId, epochId=epochId)

Achievement Badge IDs

1 First Swap          (1 swap)
2 Veteran Swapper     (10 swaps)
3 Apex Trader         (100 swaps)
4 Liquidity Provider  (first LP deposit)
5 Battle Victor       (reserved)
6 Biome Loyal         (3 epoch loyalty — DAO-awarded)
7 Biome Devoted       (7 epoch loyalty — DAO-awarded)
8 Born of the Biome   (10 epoch loyalty — legendary, DAO-awarded)

Battle advantage cycle

PRIMORDIAL_FORGE → CRYSTAL_VAULT → FRONTIER_RUINS → STANDARD_ORIGIN → NOCTURNAL_WILDS → PRIMORDIAL_FORGE
(each biome beats the one to its right; +5% VRF weight advantage)

DINO Vault is a CollectibleTrust DAO LLC - A RMI Registered Not-For-Profit protocol. All contract interactions are final and irreversible. Always verify contract addresses through official governance channels before transacting.

Document version: v8 — reflects the complete updated codebase including biome gamification, coin badges, adaptive contrast, cosmetic traits, full pool system, and comprehensive Legacy Dinero 2015 contract documentation.


Appendix A — Contract File Manifest

The DinoHook protocol is implemented across nine Solidity source files. All contracts target Solidity ^0.8.24 and are deployed to Ethereum mainnet.

File Lines Role
DinoStructures.sol 218 Shared type library — all enums, structs, IDinoRenderer interface
DinosaurVault.sol 557 Core ERC-721 vault — minting, mutation, aging, biome logic
DinosaurArtEngine.sol 924 On-chain SVG + JSON renderer — delegates background to DinoArtEngineB
DinoArtEngineB.sol 192 Biome background renderer — split to satisfy EIP-170 bytecode limit
DinoSwapHook.sol 163 Uniswap Hooks — graft, battle dispatch, leaderboard, TWAP oracle
DinoLiquidityManagerHook.sol 154 Uniswap Hooks — LP gating, loyalty lock, conservation pledge
DinoPoolRegistryHook.sol 237 Uniswap Hooks — biome assignment on pool initialisation
DinoGameModules.sol 499 Battle arena (Chainlink VRF), leaderboard, achievements, extinction tracker
WrappedDinero.sol 90 ERC-20 wrapper bridging the legacy 2-decimal Dinero 2015 token
Total 3,034

Deployment order

Contracts must be deployed in dependency order:

1.  DinoStructures        (library — no constructor dependencies)
2.  DinoArtEngineB        (no dependencies)
3.  DinosaurArtEngine     (constructor: address _bgEngine)
4.  WrappedDinero         (constructor: address _legacyDinero)
5.  DinoPoolRegistryHook  (constructor: IPoolManager)
6.  DinoExtinctionTracker (constructor: address _vault — set later via setVault)
7.  DinoLeaderboard       (no constructor dependencies)
8.  DinoAchievements      (no constructor dependencies)
9.  DinoBattleArena       (constructor: vrfCoordinator, keyHash, subId, vault)
10. DinoLiquidityManagerHook (constructor: IPoolManager, vault, achievements, registry)
11. DinoSwapHook          (constructor: IPoolManager, vault, arena, leaderboard,
                            achievements, extinctionTracker, twapOracle, threshold)
12. DinosaurVault         (constructor: dao, engine, treasury, registry)
    → then call: vault.setSwapHook(swapHook)
    → then call: vault.setExtinctionTracker(tracker)
    → then call: arena.setHook(swapHook)
    → then call: leaderboard.setHook(swapHook)
    → then call: achievements.setAuthorizedHooks(swapHook, liquidityHook)
    → then call: extinctionTracker.setHook(swapHook)
    → then call: extinctionTracker.setVault(vault)
    → then call: registry.approvePoolBiome(...) × 5 pools

Appendix B — Architecture and Design Decisions

B.1 Why UUPS upgradeable proxy for DinosaurVault?

DinosaurVault uses OpenZeppelin's UUPSUpgradeable (EIP-1822) rather than a transparent proxy or a non-upgradeable contract for two complementary reasons.

First, the NFT collection must be permanent — token IDs and ownership records can never be erased or migrated. UUPS satisfies this: the proxy address is immutable from the holder's perspective, ERC-721 token IDs are forever valid, and no migration is ever required. The upgrade mechanism merely updates the logic contract behind the stable proxy address.

Second, the protocol will inevitably need fixes. The audit history demonstrates this clearly — five rounds of audits found critical issues in the entropy system, fee accumulation, reentrancy initialisation, and the assembly byte-order decoding. A non-upgradeable contract would have required deploying an entirely new collection and migrating holders. UUPS allows the DAO to ship fixes without breaking any existing token.

The UUPS pattern was chosen over transparent proxy because it places upgrade authorisation logic inside the implementation contract (_authorizeUpgrade onlyOwner), reducing proxy complexity and saving approximately 2,300 gas per call on the cold-path.

_authorizeUpgrade is gated with onlyOwner. The owner is the DAO executor (a Timelock + Governor contract), meaning any upgrade requires a governance vote with a mandatory time delay — protecting holders from unilateral changes.

B.2 Why EIP-7201 namespaced storage?

EIP-7201 namespaced storage solves a specific problem with UUPS proxies: storage slot collisions between the implementation contract and OpenZeppelin's internal bookkeeping slots.

Without namespacing, if a new version of the implementation contract adds a state variable at the top of the contract, it shifts all subsequent storage slots, corrupting every existing value in the proxy. EIP-7201 pins all application state to a single, deterministically computed slot that is cryptographically guaranteed to never collide with OpenZeppelin's slots or any standard Solidity layout:

slot = keccak256(abi.encode(
    uint256(keccak256("CollectibleTrust.DinosaurVault.Storage")) - 1
)) & ~bytes32(uint256(0xff))
= 0x9688387e38e6627710548f085fb187716982ad8be7af78338480b6e3670b7300

The & ~bytes32(uint256(0xff)) mask clears the lowest byte, reserving 256 slots starting at this address for the struct. All VaultStorage fields are packed into this contiguous region regardless of which implementation version is active.

The slot is exposed as a public pure function (STORAGE_LOCATION_PUBLIC()) specifically so Foundry tests can assert the formula without a separate script:

assertEq(
    vault.STORAGE_LOCATION_PUBLIC(),
    keccak256(abi.encode(
        uint256(keccak256("CollectibleTrust.DinosaurVault.Storage")) - 1
    )) & ~bytes32(uint256(0xff))
);

B.3 Why commit-reveal for minting entropy?

Ethereum's block.prevrandao (formerly block.difficulty) provides genuine randomness for non-validators but is manipulable by the block proposer — a validator can choose to skip including a transaction if the resulting prevrandao value produces an undesirable outcome for them. For a collection where rare mints are worth significantly more than common ones, this creates a targeted extraction vector.

The commit-reveal scheme removes this vector entirely:

  1. Commit phase — the user submits keccak256(abi.encodePacked(seed, msg.sender)). The seed is a secret only they know. The block proposer sees only a hash.

  2. Reveal phase — in a later block, the user submits the seed. Traits are derived from keccak256(seed, block.prevrandao, tokenId). By this point:

    • The seed is already committed — the user cannot change it to fish for better traits.
    • block.prevrandao is from a block after the commit — the proposer cannot know which seed maps to which prevrandao at commit time.
    • The 256-block expiry window bounds the attack surface to a finite range.

The rarity roll is derived from bytes 8–11 of the resulting bytes32 — four bytes of entropy providing a 0–999 range with uniform distribution.

block.timestamp was deliberately removed from all randomness derivation after audit round one. Timestamps are validator-adjustable within a ~15 second window, which is sufficient to bias a 0–999 roll.

B.4 Why StructuralAnatomy fits in exactly one storage slot?

The StructuralAnatomy struct is designed to occupy exactly 32 bytes — one EVM storage slot. This is the single most impactful gas optimisation in the protocol.

The cost difference:

  • Cold SSTORE (first write to a slot): 20,000 gas
  • Additional cold SSTORE (second slot): another 20,000 gas

By fitting all 28 trait fields into one slot, each mint costs exactly one cold SSTORE for all trait data combined — regardless of how many traits are set. The alternative (two slots) would cost 40,000 gas for trait storage alone on every mint.

The layout was engineered to achieve this:

Bytes  0– 1  primaryHideColor   uint16   2 bytes
Bytes  2– 3  underbellyColor    uint16   2 bytes
Bytes  4– 7  lastTransferTimestamp uint32  4 bytes
Bytes  8–19  12× enum fields    uint8   12 bytes  (genus through rarityScore)
Bytes 20–23   4× bool overlays  bool    4 bytes   (cybernetic, twap, extinct, fossil)
Bytes 24–27   4× bool badges    bool    4 bytes   (hasWaac, hasWmc, hasCc, hasDino)
Bytes 28–31  (8 new trait uint8s occupy bytes 20–27 in updated layout)
             Total: 32 bytes exactly

lastTransferTimestamp is uint32 rather than uint64 — uint32 holds Unix timestamps until February 7, 2106, which is well beyond any reasonable protocol lifespan, and saves 4 bytes that would otherwise push the struct to 36 bytes (two slots).

AgeBracket is notably absent from the stored struct. It is computed dynamically inside tokenURI from block.timestamp - lastTransferTimestamp. This means aging happens automatically with zero gas — no transaction, no cron job, no keeper — while saving one uint8 of struct space.

B.5 Why custom errors instead of require strings?

Every revert path in the protocol uses a named custom error (error VaultIncorrectFee()) rather than a require(condition, "string message").

Custom errors cost 4 bytes of calldata on revert (the 4-byte error selector). A require with a string message costs 4 bytes plus 32 bytes per 32-character chunk of the string, plus encoding overhead. A typical require message like "Vault: incorrect fee amount" costs ~60 bytes.

Across 29 custom errors in DinosaurVault alone, the gas savings per revert are 50–200 gas each. More importantly, custom errors are encoded into the ABI and can be decoded by any client — front-ends receive machine-readable error names rather than raw strings, improving UX.

Custom errors also eliminate all string storage from the deployed bytecode, directly reducing contract size. DinosaurVault has zero string literal bytes despite having 29 distinct revert conditions.

B.6 Why calldata for function parameters?

Every external and public function that receives a struct, array, or string uses calldata rather than memory for those parameters. calldata is read-only and refers directly to the transaction input data without copying it to memory. The difference:

  • memory: EVM allocates a new region, copies all bytes from calldata, charges gas for the copy and for memory expansion.
  • calldata: No copy. The EVM reads directly from the input buffer.

For DinoTypes.StructuralAnatomy calldata anatomy (32 bytes), the saving is minor. For the SVG string parameters in DinoArtEngineB (up to ~2,000 bytes), using calldata instead of memory saves on the order of 4,000–8,000 gas per call. Since tokenURI is a view function called off-chain this does not affect minting costs, but it reduces latency for marketplaces and clients that call tokenURI frequently.

B.7 Why unchecked arithmetic in specific locations?

Solidity 0.8+ adds overflow/underflow checks to every arithmetic operation by default, each costing ~20–30 gas. The protocol uses unchecked blocks in three specific locations where overflow is provably impossible:

  1. totalMinted++ in revealAndIncubate — gated by maxSupply (uint32, max 4.3 billion). The counter can never overflow.
  2. battleWins[id]++ and battleLosses[id]++ — uint256 counters. Incrementing a uint256 would require 2^256 transactions, which is physically impossible.
  3. userVolumeAccumulated[user] += volume in DinoLeaderboard — uint256 accumulator. Physical impossibility argument applies.

unchecked is deliberately not used in fee arithmetic (accumulatedFees += msg.value) because msg.value is user-controlled and, while overflow is practically impossible for uint256, the consequence of a corrupted fee accumulator would be loss of protocol revenue. The ~25 gas overhead is the correct trade-off.

B.8 Why the art engine was split into two contracts?

EIP-170 imposes a hard 24,576-byte limit on compiled contract bytecode. The original DinosaurArtEngine contained 25,373 bytes of SVG string literals — 797 bytes over the limit. This would cause the deployment transaction to be rejected by every Ethereum node.

The five biome background functions (_bgForge, _bgCrystal, _bgRuins, _bgStandard, _bgWilds) were the natural split point: they account for 5,214 bytes, they are self-contained, they are called from a single point in the code, and they have no dependency on the accessory or metadata rendering logic.

After splitting:

Contract String bytes Headroom
DinosaurArtEngine 20,159 4,417 bytes spare
DinoArtEngineB 4,358 20,218 bytes spare

The call from DinosaurArtEngine._buildBackground() to DinoArtEngineB.buildBackground() is an external staticcall that costs approximately 700 gas inside a view function — negligible since tokenURI is never executed on-chain during normal protocol operation (it is called off-chain by marketplaces and wallets).

The split also improves upgradeability: DinosaurArtEngine.setBgEngine() lets the DAO update biome backgrounds independently of the full art engine upgrade. Adding a sixth biome or improving an existing background only requires deploying a new DinoArtEngineB and calling setBgEngine() — no vault changes, no re-indexing of token URIs.


Appendix C — Gas Optimisation Reference

The following table summarises every deliberate gas optimisation in the codebase with its measured or estimated saving.

Optimisation Location Saving per call
Single storage slot for 28 traits DinosaurVault._buildAnatomy ~20,000 gas vs 2-slot layout
Custom errors (29 total) DinosaurVault 50–200 gas per revert
Custom errors (12 total) DinoGameModules 50–200 gas per revert
calldata for anatomy struct All external functions ~2,000 gas per call
uint32 timestamp (vs uint64) StructuralAnatomy Prevents second storage slot
unchecked increment on totalMinted DinosaurVault ~25 gas per mint
unchecked battle counters DinoBattleArena ~25 gas per settlement
AgeBracket computed in tokenURI, not stored DinosaurVault ~5,000 gas per transfer
_computeScarcity is pure (no SLOAD) DinosaurVault ~200 gas per mint
uint96 fee pair packed in one slot VaultStorage One fewer SLOAD on fee check
Early exit on empty hookData DinoSwapHook.beforeSwap ~3 gas for plain swaps
try/catch on all hook callbacks DinoSwapHook Prevents swap reversion on hook failure
Merkle proof for extinction (vs loop) DinoExtinctionTracker O(log n) vs O(n) per settlement
Per-pool loyalty mapping (vs per-wallet) DinoLiquidityManagerHook No cross-pool interference
Monotonic nonce for VRF request IDs DinoBattleArena Prevents collision, saves retry cost
immutable for all external addresses All hooks ~2,100 gas per read (vs SLOAD)
SCALING_FACTOR as compile-time constant WrappedDinero ~200 gas vs runtime division
Fee withdrawal pull-payment pattern DinosaurVault Eliminates push-payment reentrancy

Storage slot packing reference

The EVM charges 20,000 gas for a cold SSTORE to a new (zero → non-zero) slot, and 2,900 gas for a warm SSTORE to an existing (non-zero → non-zero) slot. Reading a cold slot (SLOAD) costs 2,100 gas; warm SLOAD costs 100 gas.

The VaultStorage struct is packed as:

Slot 0:  renderEngine (address, 20 bytes) + rmiDaoTreasury (address, 20 bytes) — overflow to slot 1
Slot 1:  dinoSwapHook (20b) + extinctionTracker (12b partial)
...     (addresses each take a full slot in Solidity's layout)
Slot N:  incubationFee (uint96, 12b) + mutationFee (uint96, 12b) → packed into 24 bytes, one slot
Slot N+1: maxSupply (uint32, 4b) + totalMinted (uint256 — takes full slot)

The incubationFee + mutationFee packing into uint96 (12 bytes each, 24 bytes total) is intentional: both fit in one slot, so the fee check inside revealAndIncubate reads both values in a single SLOAD (100 gas warm) rather than two separate SLOADs (200 gas). uint96 supports fees up to ~79 billion ETH — sufficient for any realistic fee level.


Appendix D — Security Architecture

D.1 Reentrancy

Every state-mutating function that accepts ETH or calls external contracts is protected with OpenZeppelin's ReentrancyGuardUpgradeable. The critical fix from audit round four was ensuring __ReentrancyGuard_init() is called inside initialize() — without this, the nonReentrant modifier is a no-op on a proxy because the storage slot holding the guard state is never set to its initial value.

The check-effects-interactions (CEI) pattern is applied throughout:

  • In revealAndIncubate: the commit is deleted and fees are accumulated before _mint is called. _mint triggers _update which could call external code (operator hooks) — CEI ensures state is consistent before any external call.
  • In withdrawFees: accumulatedFees = 0 is written before the ETH transfer. If the transfer reverts the fee counter has already been zeroed, preventing a double-withdrawal attack.
  • In unwrap (WrappedDinero): _burn and totalLegacyLocked decrement happen before the legacy token transfer. This is especially important because the legacy contract's transfer is a non-standard 2015-era implementation — CEI prevents any potential re-entry through the legacy call.

D.2 Access control

Three distinct roles are used throughout the protocol:

onlyOwner (OpenZeppelin Ownable / OwnableUpgradeable) — the DAO executor address. Required for all admin functions: setSwapHook, setExtinctionTracker, setRenderEngine, withdrawFees, setBgEngine, openEpoch, setExtinctionRoot, approvePoolBiome, setHook on game modules. The owner on DinosaurVault is the UUPS upgrade authority simultaneously — any upgrade requires owner authorisation.

onlySwapHook — a dedicated modifier on DinosaurVault.injectCyberneticGraft, modifyTwapState, DinoBattleArena.initiateChallenge, DinoLeaderboard.recordActivity, DinoAchievements.checkAndMint, and DinoExtinctionTracker.recordSurvival. Only the authorised hook contract can call these functions. Without this gate, any wallet could freely mutate any DINO's traits, inflate leaderboard scores, or stamp arbitrary epoch survival.

onlyExtinctionTracker — guards DinosaurVault.applyExtinctionMark. Only the tracker contract can permanently mark a DINO as extinct. Without this, any wallet could grief any collector by calling applyExtinctionMark directly.

daoExecutor — guards DinoAchievements.mintLoyaltyBadge. Loyalty badges are verified off-chain (indexing BiomeLoyaltyStamped events) and minted on-chain by the DAO executor. This two-step model prevents on-chain manipulation of loyalty streaks.

Zero-address guards (if (addr == address(0)) revert ...) are applied to every setter and constructor parameter that receives an address. Setting an external contract address to address(0) is a common admin error that permanently disables functionality with no recovery path.

D.3 Entropy and randomness

Three sources of randomness are used, each chosen for its specific trust model:

block.prevrandao + commit-reveal (trait derivation at mint) — the commit scheme means prevrandao cannot be biased by the validator at commit time (the seed is unknown), and the 256-block expiry means the validator cannot defer indefinitely hoping for a favourable prevrandao.

Chainlink VRF v2.5 (battle outcomes) — battles require a verifiably unbiased random number that neither party can predict or influence. Chainlink VRF provides this: the result is generated off-chain by Chainlink nodes using a verifiable random function, with the proof verifiable on-chain before the result is used. The VRF callback verifies msg.sender == address(vrfCoordinator) — any call from a different address reverts with ArenaVRFOnly.

Chainlink Price Feed (TWAP aura activation) — the TWAP aura reads ITwapOracle(twapOracle).latestAnswer() in a try/catch. If the oracle reverts or returns stale data the catch block silently skips the update — the aura state is never changed by a failed oracle read. Prices from the oracle are not used in any value-affecting calculation, only for a cosmetic SVG flag, so oracle manipulation has no economic consequence beyond a visual effect.

D.4 Integer overflow and underflow

Solidity 0.8+ enables checked arithmetic by default — every addition, subtraction, multiplication, and exponentiation reverts on overflow or underflow unless wrapped in unchecked. This eliminates the class of vulnerabilities that affected Solidity 0.7 and earlier contracts.

The three unchecked blocks in the codebase are each accompanied by an inline comment explaining why overflow is provably impossible. This is the recommended pattern: unchecked should never appear without a documented proof of safety.

uint32 for lastTransferTimestamp is safe until the Unix timestamp exceeds 4,294,967,295 (year 2106). A uint32 overflow in this field would cause the aging system to compute an incorrect hold time — not a financial loss, just a visual error — and would require manual correction via a vault upgrade. The tradeoff (4 bytes saved, one storage slot preserved) is correct.

D.5 Front-running mitigation

Mint front-running — eliminated by commit-reveal. An attacker who sees a revealAndIncubate transaction in the mempool cannot use that information to get a better DINO because the traits are determined by the committed seed, which was set in a prior block.

Fee front-running — the incubation fee is checked with exact equality (msg.value != $.incubationFee). A front-runner cannot sandwich a fee change by submitting a transaction with a slightly higher fee, because any value other than the exact fee reverts.

Hook callback front-running — graft and battle actions in DinoSwapHook.beforeSwap verify token ownership atomically inside the vault (ownerOf(tokenId) != swapOriginator). An attacker cannot front-run a graft by transferring the token out between the hook call and the vault check because they occur in the same transaction.

Epoch survival gaming — survival is recorded per-wallet, not per-token, and requires an active epoch (gated by epochActive[epochId]). An attacker who knows the epoch is about to close cannot submit a survival stamp for a wallet that did not participate — the epoch is closed by the DAO calling setExtinctionRoot, at which point epochActive is set to false and further recordSurvival calls revert with TrackerEpochInactive.

D.6 Biome assignment manipulation prevention

In DinoPoolRegistryHook, biome assignment was changed from an address % 5 modulo scheme (present in v1–v4) to a DAO pre-approval mapping. The modulo scheme was vulnerable to CREATE2 mining: an attacker could mine a salt that produces a pool address whose modulo maps to a desired biome (e.g. PRIMORDIAL_FORGE for the rarity bonus), then create that pool at the computed address to mint all their DINOs there. The pre-approval scheme requires the DAO to explicitly approve each pool's biome before deployment — no computation can bypass this gate.

D.7 Soulbound badge enforcement

DinoAchievements overrides ERC-1155's _update hook:

function _update(address from, address to, uint256[] memory ids, uint256[] memory values)
    internal override {
    if (from != address(0)) revert AchievementsSoulbound();
    super._update(from, to, ids, values);
}

from == address(0) is the mint condition (tokens being created). Any transfer or burn (from != address(0)) reverts unconditionally. This blocks all secondary market sales, all wallet-to-wallet transfers, and all burn attempts — badges are permanently and irrevocably linked to the earning wallet. OpenZeppelin's safeTransferFrom, safeBatchTransferFrom, and burn all route through _update, so no code path bypasses this check.

D.8 Wrapped Dinero reserve protection

WrappedDinero.reclaimStuckLegacy is the only admin function that can move legacy tokens from the contract. It contains two sequential checks:

if (contractBalance < totalLegacyLocked) revert WrapperReserveAccountingError();
if (contractBalance - totalLegacyLocked < amount) revert WrapperInsufficientSurplus();

The first check catches an impossible-in-theory but defensive-in-practice scenario where the contract holds fewer legacy tokens than its internal accounting believes — which would indicate either a bug or a direct manipulation of the legacy token contract. If this condition is true the function halts entirely rather than allowing a partial withdrawal that could leave wrapped DINO holders unable to unwrap.

The second check ensures only the surplus (tokens beyond what active wrapped DINO supply requires) can be reclaimed. As long as any wrapped DINO is outstanding, the corresponding legacy tokens are protected.


Appendix E — EIP-170 Bytecode Limit Reference

EIP-170 (introduced in the Spurious Dragon hard fork, block 2,675,000, November 2016) limits deployed contract bytecode to 24,576 bytes (24 KB). Contracts exceeding this limit are rejected by every Ethereum node during deployment — the transaction is included in a block but the CREATE opcode returns address(0) and all state changes are reverted.

What counts toward the limit

The 24,576-byte limit applies to the deployed bytecode — the code stored at the contract address. This is distinct from the creation bytecode (the constructor code used during deployment, which is not stored). The deployed bytecode includes:

  • All function dispatch logic (the jump table at the start of every contract)
  • All function bodies — every opcode, every PUSH instruction and its data
  • All string literals used in the contract — each character of an SVG string occupies one byte of bytecode
  • All constant values that are embedded at compile time

The limit does not count:

  • Comments (stripped by the compiler)
  • Variable names (not stored in bytecode)
  • NatSpec documentation (not stored in deployed bytecode)
  • The constructor itself (creation bytecode, not deployed)

Why the art engine was the only affected contract

The other eight contracts in the protocol are logic-heavy but string-light — they contain control flow, arithmetic, and external calls, but very few string literals. DinosaurVault, DinoSwapHook, and WrappedDinero contain zero string literals in their deployed bytecode.

DinosaurArtEngine is unusual: its entire job is concatenating SVG strings. Every <rect>, <path>, <animate>, and <text> element is a string literal that lives verbatim in the bytecode. With 424 string literals totalling 25,373 bytes, the art engine exceeded the limit by 797 bytes. DinoArtEngineB holds the five background functions (4,358 bytes of strings) and brings the main engine to 20,159 bytes — 4,417 bytes of headroom for future additions.

Future expansion headroom

Contract Current string bytes Remaining headroom
DinosaurArtEngine 20,159 ~4,400 bytes
DinoArtEngineB 4,358 ~20,200 bytes

Adding a sixth biome, a new accessory type, or additional SVG animation layers should first be prototyped with a string-byte count against these budgets. A new biome background of typical complexity (~1,300 bytes, similar to _bgWilds) fits comfortably in DinoArtEngineB. New accessory functions of ~400–600 bytes each fit comfortably in DinosaurArtEngine. If either contract approaches its limit, DinoArtEngineB has sufficient headroom to absorb additional background functions, and the SSTORE2 upgrade path (planned in the roadmap) would remove background bytes from the bytecode entirely.


Appendix F — Per-Contract Code Discussion

This appendix provides a file-by-file explanation of why each contract was written the way it was — the design patterns chosen, the tradeoffs accepted, and the specific Solidity features that make each component work correctly at production scale on Ethereum mainnet.


F.1 DinoStructures.sol — The shared type library

What it is: A headerfile equivalent for Solidity. Contains all enum definitions, the StructuralAnatomy struct, the CommitRecord struct, and the IDinoRenderer interface.

Why a separate file? Every contract in the protocol imports types from this file. Keeping enums and structs in one location ensures that when GenusType.TYRANNOSAURUS is value 0 in DinoStructures, it is 0 everywhere — in the vault, in the art engine, in the game modules, and in any future contract. Divergent copies of the same enum in different files are a class of bug that has caused real losses in production protocols.

IDinoRenderer interface: Declares compileArt(uint256 tokenId, StructuralAnatomy calldata anatomy) external view. The view modifier (not pure) reflects the fact that DinosaurArtEngine makes an external call to DinoArtEngineB — and any function that calls an external contract must be at least view. The interface allows DinosaurVault to call any renderer that satisfies this signature, which is the mechanism behind setRenderEngine(). Future art engine upgrades only need to implement this one function.

CommitRecord struct: Packs bytes32 commitHash (32 bytes) and uint32 commitBlock (4 bytes) into two storage slots. A uint32 block number is safe until block ~4.3 billion — at Ethereum's current ~7,200 blocks/day, that is approximately 1,600 years.

Enum sizing: Every enum compiles to uint8. With 24 values, GenusType fits in a uint8. If the collection were ever expanded beyond 256 genera, the enum would need to become uint16 — a struct-breaking change requiring a migration. The 24-genus design was deliberately chosen to stay well within the 256-value uint8 ceiling.


F.2 DinosaurVault.sol — The ERC-721 core

What it is: The primary user-facing contract. Manages the full NFT lifecycle: commit-reveal minting, trait storage, biome assignment, aging, mutation, graft injection, TWAP state, extinction marking, and fee withdrawal.

UUPS proxy pattern: UUPSUpgradeable stores the upgrade logic inside the implementation, not the proxy. TransparentUpgradeableProxy stores it in the proxy and uses a separate admin address, consuming an extra SLOAD on every non-admin call. UUPS removes this overhead. The _authorizeUpgrade(address) internal override onlyOwner gate means only the DAO executor can publish a new implementation — governance protects all 10,000 DINO holders from unilateral changes.

EIP-7201 storage: The VaultStorage struct is accessed via assembly:

function _getVaultStorage() internal pure returns (VaultStorage storage $) {
    bytes32 slot = STORAGE_LOCATION;
    assembly { $.slot := slot }
}

This is the canonical OpenZeppelin pattern for EIP-7201. The pure function returns a storage pointer — pure here means no implicit state reads, even though the returned pointer enables state access. The slot address is a compile-time constant, making the assembly PUSH32 + SLOAD sequence predictable and cheap.

revealAndIncubate design: The function deliberately validates the commit hash, checks the block window, reads the external biome registry (a view call — safe before state writes), then deletes the commit and writes the NFT — all in that order. The biome registry call was moved before state writes in audit round five to eliminate a theoretical TOCTOU issue where state could be partially written before an external call reverts.

_update override: ERC-721's _update(address to, uint256 tokenId, address auth) is called on every transfer, including mints. The override stamps lastTransferTimestamp = uint32(block.timestamp) only when from != address(0) (not on mints) and to != address(0) (not on burns). Updating on mint would set the aging clock at mint time, which is the intended behaviour — but the current implementation leaves the initial value from revealAndIncubate (which sets lastTransferTimestamp directly), avoiding an extra SSTORE.

tokenURI as a view function: Despite returning a large base64-encoded JSON blob, tokenURI is a Solidity view function — it reads state but writes nothing. This means every marketplace, wallet, and block explorer calls it off-chain for free. The computation (SVG assembly, Base64 encoding) runs entirely in the EVM's read-only context. Gas costs for tokenURI are paid by the calling client, not by any on-chain transaction.


F.3 DinosaurArtEngine.sol — The main renderer

What it is: A stateless pure/view contract that takes a token ID and anatomy struct and returns a data:application/json;base64,... URI containing the full SVG and all 28 trait attributes.

Why not a library? Solidity libraries linked via DELEGATECALL share the caller's storage context. An art engine as a library could theoretically read or write vault storage — a dangerous coupling. A separate contract called via a regular external call has its own isolated execution context. The gas cost of the external call (~700 gas) is acceptable for a view function.

abi.encodePacked vs string.concat: The renderer uses abi.encodePacked throughout rather than Solidity's newer string.concat. Both produce identical output for string concatenation, but abi.encodePacked has been available since Solidity 0.4 and is more familiar to auditors reviewing SVG renderers. string.concat would be equally correct.

Split of pure vs view: Functions that only concatenate string literals are internal pure. _buildBackground is internal view because it calls the external DinoArtEngineB.buildBackground. compileArt and _buildSVG are external/internal view to propagate the view-ness up the call chain. The Solidity compiler enforces this transitively — a pure function calling a view function is a compile error.

_buildDefs and filter IDs: All SVG filter and gradient IDs are short ("gf", "ng", "ptl") rather than descriptive names. SVG IDs are string-matched at render time — shorter IDs save bytecode bytes at a 1:1 ratio. With 424 string literals totalling 25,373 bytes before the split, every byte saved in ID names directly reduces bytecode pressure.

_badge function: The badge renderer accepts all parameters as string memory to allow arbitrary fill/stroke colours without branching. The alternative — a uint8 badgeType switch — would require additional string-lookup functions, consuming more bytecode. The current design centralises all badge SVG generation in 15 lines.


F.4 DinoArtEngineB.sol — The background renderer

What it is: A dedicated contract for the five biome background functions. Exists solely to satisfy EIP-170 — see Appendix E for the full explanation.

string calldata vs string memory: The spot parameter in each background function is calldata. When DinosaurArtEngine calls buildBackground(anatomy.biome, spotlight), the spotlight string is already in memory in the calling contract. The EVM passes it as calldata to DinoArtEngineB — receiving it as calldata avoids an additional memory allocation and copy inside DinoArtEngineB, saving ~200–500 gas per call.

_bgStandardGridH and _bgStandardGridV helpers: The blueprint grid requires 18 <line> elements. Placing all 18 inside a single abi.encodePacked call would exceed Solidity's 16-variable stack depth limit (one of the most common Solidity compilation failures). Splitting into horizontal and vertical helpers keeps each encodePacked under 12 arguments.

_bgWildsFireflies helper: Similarly split out from _bgWilds to avoid stack depth issues. The five firefly <circle> + <animate> pairs in _bgWilds plus the canopy path and aye-aye eyes would push the single function past 16 stack variables.

No state, no constructor, no admin: DinoArtEngineB is fully stateless — no constructor, no owner, no admin functions. It is deployed once and its address is hardcoded in DinosaurArtEngine.bgEngine. It can never be upgraded directly; upgrading requires deploying a new DinoArtEngineB and calling DinosaurArtEngine.setBgEngine().


F.5 DinoSwapHook.sol — The Uniswap Hooks integration

What it is: A Uniswap V4 BaseHook implementation that intercepts swaps in DINO pools and routes protocol actions through them.

BaseHook inheritance: V4's BaseHook provides the IHooks interface implementation and validates that the hook address is deployed at the correct V4 hook address prefix (the address encodes which callbacks are active). getHookPermissions() returns the bitmask of enabled callbacks — beforeSwap: true and afterSwap: true. Every other callback is false, meaning V4 does not call the hook for LP operations, donations, or initialisation (those are handled by DinoLiquidityManagerHook and DinoPoolRegistryHook respectively).

hookData length check: if (data.length == 128) is the fast path. A plain swap with no hookData (data.length == 0) exits this conditional at approximately 3 gas — the check is a single CALLDATASIZE comparison. Only swaps that explicitly include hookData pay for the decode and dispatch.

try/catch on all external callbacks: Every call to IDinoVault, IDinoBattleArena, IDinoLeaderboard, etc. is wrapped in try/catch. If any callback reverts — because the vault is paused, the token has been transferred, the leaderboard's hook address was updated — the swap completes successfully. Without try/catch, a failed graft would revert the entire swap, locking users out of the pool. The catch block emits a GraftProcessed(tokenId, false) event so the caller knows the graft failed without the swap failing.

immutable for all addresses: vault, arena, leaderboard, achievements, extinctionTracker, twapOracle, and twapThreshold are all immutable. Immutable variables are embedded in the contract bytecode at deployment — reading them costs ~150 gas (a PUSH32 instruction), versus ~2,100 gas for a cold SLOAD from a regular state variable. With these addresses read on every swap, the saving accumulates significantly at volume.


F.6 DinoLiquidityManagerHook.sol — The LP hook

What it is: Manages LP access control (NFT gate), per-pool loyalty timestamps, LP badge minting, and conservation pledge tracking.

beforeAddLiquidity as view: The NFT ownership check IDinoVaultVerify(vault).balanceOf(provider) is a read-only call, so beforeAddLiquidity is external view override. V4 hooks can declare view callbacks — V4 calls them via staticcall, which reverts if any state mutation is attempted. Using view here documents the intent and lets the compiler enforce it.

Per-pool loyalty mapping: mapping(address => mapping(bytes32 => uint256)) public poolLoyaltyTimestamps keys timestamps by both wallet and pool ID. The pool ID (bytes32) is derived from PoolIdLibrary.toId(key) — a deterministic hash of the pool's currency pair, fee, tick spacing, and hook address. This ensures that liquidity events in the DINO/ETH pool never affect the loyalty timestamp for the DINO/USDC pool, and vice versa. The earlier single-address mapping was a bug discovered in audit round two.

Conservation pledge via try/catch: The conservation pledge call IDinoPoolRegistry(poolRegistry).conservationPledge(provider) is wrapped in try/catch. If the registry is upgraded, paused, or temporarily unavailable, LP deposits should not fail. The conservation pledge is a cosmetic feature (leaf overlay) — it would be wrong for a registry outage to block liquidity provision.


F.7 DinoPoolRegistryHook.sol — The biome registry

What it is: Assigns biomes to pools on initialisation. Stores the canonical poolId → biome mapping that DinosaurVault reads during mint.

beforeInitialize signature: The V4 beforeInitialize callback includes uint160 sqrtPriceX96 — the initial price for the pool. In audit round one this was incorrectly typed as uint16, causing a function selector mismatch that meant the hook was never called by V4. The Solidity compiler does not check this automatically because it sees beforeInitialize as an override of an abstract function; the correct type must match the V4 interface exactly.

DAO pre-approval over address modulo: The biome is assigned from approvedBiomes[approvalKey] rather than computed from the pool address. The approvalKey is keccak256(abi.encode(currency0, currency1, fee, tickSpacing)) — a stable identifier that can be computed and approved by the DAO before the pool is deployed. This eliminates CREATE2 mining attacks where an adversary mines a pool address whose modulo maps to the most-favourable biome.

setAllowPermissionlessBiomes: A governance-controlled escape hatch. When true, pools without a pre-approved biome receive a deterministic hash-based biome assignment rather than reverting. This allows the protocol to support community-created pools (e.g. DINO/any-token) without requiring a DAO vote per pool. The default is false (permissioned only) for the five launch pools.


F.8 DinoGameModules.sol — Four contracts in one file

What it is: Four contracts deployed from a single source file: DinoBattleArena, DinoLeaderboard, DinoAchievements, and DinoExtinctionTracker. Grouped in one file for readability and deployment cohesion — they are logically part of the same "game layer" of the protocol.

DinoBattleArena — Chainlink VRF v2.5: The VRF request is made in initiateChallenge (synchronous) and fulfilled in rawFulfillRandomWords (asynchronous, called by Chainlink). The rawFulfillRandomWords entry point requires msg.sender == address(vrfCoordinator) — any other caller reverts with ArenaVRFOnly. The monotonic _nonce variable prevents two challenges in the same block for the same token pair from producing the same vrfRequestId (which would overwrite the first challenge in the challenges mapping).

The biome advantage calculation uses _biomeBeats(uint8 a, uint8 b):

return b == (a + 1) % 5;

This encodes the five-element cycle (FORGE→CRYSTAL→RUINS→STANDARD→WILDS→FORGE) in a single modular arithmetic expression. Each biome beats the next one in the sequence. The % 5 keeps the result in range without any branch.

DinoLeaderboardonlyOwner on setHook: The v5 audit found that setHook had no access control — any wallet could point the leaderboard at their own contract and emit false ActivityRecorded events, inflating their volume and making them eligible for Alpha badges. The onlyOwner modifier on setHook means only the DAO executor can change the authorised hook address.

DinoAchievements — Soulbound ERC-1155: ERC-1155 uses a single _update internal function for all token movements (mint, transfer, burn). Overriding _update with if (from != address(0)) revert AchievementsSoulbound() is the minimal, complete soulbound implementation — it catches all transfer and burn paths in one place with no case analysis. The from == address(0) check is the standard ERC-1155 mint condition.

On-chain badge art uses Base64.encode(bytes(svg)) — encoding the SVG bytes as base64 and embedding them in a data:image/svg+xml;base64,... URI. This is fully self-contained: no external URL, no IPFS CID, no server dependency. The badge SVG can be decoded and rendered by any browser from the URI alone.

DinoExtinctionTracker — Merkle proofs: The extinction system uses a Merkle tree rather than an on-chain loop because the collection has up to 10,000 tokens. An on-chain loop over all tokens would cost O(n × 20,000) gas — potentially 200,000,000 gas for a full settlement, exceeding the block gas limit (~30,000,000 gas) by nearly 7×. The Merkle approach settles each token independently with O(log₂ n) proof verification: for 10,000 tokens, that is log₂(10,000) ≈ 14 hashes per token, each costing ~30 gas, totalling ~420 gas per extinction claim regardless of collection size.

The Merkle root is built off-chain by the DAO: for each epoch, find all token IDs whose owners did not appear in epochSurvivalBitmap[epochId], build a Merkle tree of keccak256(abi.encodePacked(tokenId)) leaves, and publish the root via setExtinctionRoot. Anyone — the DAO, an NFT owner, or a keeper — can then call claimExtinction(epochId, tokenId, proof) to mark a specific token extinct.


F.9 WrappedDinero.sol — The ERC-20 bridge

What it is: An ERC-20 token that wraps the legacy 2-decimal Dinero 2015 contract, making it compatible with modern wallets, DEXes, and DeFi protocols.

SCALING_FACTOR = 10**16 — why this number: The legacy contract has 2 decimal places. Standard ERC-20 tokens have 18 decimal places. The gap is 18 - 2 = 16, so SCALING_FACTOR = 10**16. This value is a private constant — it is embedded directly in the bytecode at compile time. Every multiply and divide using this constant compiles to a PUSH32 10000000000000000 followed by MUL or DIV — no SLOAD, no runtime cost beyond the arithmetic itself.

The before/after balance snapshot in wrap():

uint256 balanceBefore = legacy.balanceOf(address(this));
if (!legacy.transferFrom(msg.sender, address(this), legacyAmount))
    revert WrapperDepositFailed();
uint256 actualDeposited = legacy.balanceOf(address(this)) - balanceBefore;

This handles fee-on-transfer tokens correctly — if the legacy contract somehow deducted a transfer fee (it does not currently, but the 2015 contract is opaque), the wrapped amount would reflect what was actually received, not what was specified. The balance snapshot pattern is the canonical defensive pattern for wrapping tokens with unknown transfer behaviour.

uint128 totalLegacyLocked: The maximum legacy supply is 1,000,000 DINO at 2 decimals = 100,000,000 raw units. A uint128 holds up to ~3.4 × 10³⁸ — more than sufficient. Using uint128 instead of uint256 allows totalLegacyLocked to share a storage slot with other fields, reducing SLOAD costs. A uint256 would occupy a full slot alone.

CEI in unwrap(): The sequence is deliberately:

  1. _burn(msg.sender, wrappedAmount) — state write
  2. totalLegacyLocked -= uint128(legacyAmount) — state write
  3. ILegacyDinero(legacyDineroAddress).transfer(msg.sender, legacyAmount) — external call

This is check-effects-interactions: all state is updated before the external call. Even though the legacy contract's transfer does not have a fallback mechanism that could re-enter WrappedDinero, the CEI ordering is applied defensively. If the legacy contract were ever upgraded (theoretically impossible given its immutability, but as a principle), the CEI ordering would remain safe.



Appendix G — The CC and DINO Synergy: How DinoHook Connects and Enhances Both Tokens

This addendum examines the relationship between Wrapped CurrencyCoin (CC) and Wrapped Dinero (DINO), how each token arrived at DinoHook, and how the platform creates compounding value for both communities and for the 10,000-NFT collection itself.


G.1 Two tokens. One developer. One origin story.

To understand why CC and DINO belong together inside DinoHook, it is necessary to understand where each token came from — and who made both of them.

Both Legacy CurrencyCoin and Legacy Dinero 2015 were deployed to the Ethereum mainnet by the same person: Rocky Fikki, known on-chain as rfikki — a prolific early Ethereum community member and pioneering blockchain developer who was building on the EVM within weeks of its existence. This is not a coincidence of shared era. These are two deliberate creations by the same technically adventurous builder, laid down three months apart in the founding year of the Ethereum token economy.

  • CurrencyCoin (CC) — deployed by rfikki on September 8, 2015, just 40 days after Ethereum's genesis block. Two days prior, Vitalik Buterin had committed currency.sol to the Standardized_Contract_APIs GitHub repository. rfikki took that code, hardened it, and deployed it as a live on-chain artefact — becoming one of the first people in history to deploy a token contract to the Ethereum mainnet.

  • Dinero 2015 (DINO) — deployed by rfikki on December 10, 2015, 93 days after CurrencyCoin. Styled after the same 2015 Mist Browser code philosophy — minimal, immutable, purposeful — Dinero extended rfikki's 2015 body of work and demonstrated the same pattern: a fixed supply, a single write function, a permanent on-chain record.

Between these two deployments, the entire ERC-20 standardisation process was unfolding. MistCoin was deployed on November 3, 2015 by Fabian Vogelsteller. EIP-20 was proposed sixteen days later. rfikki had already been building token contracts since September — before MistCoin, before the standard, before most of the world had heard of Ethereum tokens. By the time Dinero was deployed in December, rfikki had been a working Ethereum token developer for over three months.

Ethereum went live on July 30, 2015 with a barebone implementation codenamed Frontier. In the weeks that followed, a handful of developers began experimenting with the new EVM — not because there was an ecosystem to participate in, but because they were exploring what was possible. rfikki was emphatically one of those developers. The contracts he wrote were not polished products. They were experiments, commitments, and explorations. And that is precisely what makes them historically significant.

These are not two random old tokens that happen to be old. They are two deliberate creations by the same pioneering developer, from the same brief Frontier window in which Ethereum's entire token paradigm was being invented in real time. CurrencyCoin is a true historic blockchain collectible based on its use of the currency.sol Vitalik code and its early timestamp, and rfikki was a very active Ethereum community member. Dinero 2015 occupies the same category by the same criteria and the same hand. DinoHook is the first platform to treat both tokens as what they actually are: two chapters of the same pioneering story, both authored by rfikki in the founding year of the EVM token economy, and now reunited in a single living protocol.


G.2 The problem both tokens share — and why DinoHook solves it

Both CC and DINO face the same fundamental challenge: they were written before the ERC-20 standard existed, so they lack the approve and transferFrom functions that every modern DeFi protocol requires. Without these, neither token can:

  • Form a Uniswap liquidity pool
  • Be lent on Aave or Compound
  • Be used as collateral
  • Be integrated into any DEX aggregator
  • Display correctly in most modern wallets

Both tokens have been wrapped to meet ERC-20 standards, allowing each to be wrapped and unwrapped 1:1. Wrapping solves the technical incompatibility — but wrapping alone only makes a token usable. It does not make it desirable. It does not create demand, liquidity, or community. It does not give holders a reason to interact with the token beyond holding it as a curiosity.

DinoHook provides exactly what wrapping alone cannot: a living economic system that requires both tokens for full participation, rewards holding them, and creates ongoing reasons to acquire, hold, and use them.


G.3 How CC flows into DinoHook

The DINO/CC pool is one of the five launch pools. Every liquidity interaction with this pool is mediated by the DinoHook system:

The Frontier Ruins biome is CC's home territory. rfikki deployed CurrencyCoin on September 8, 2015 — a hardened version of Vitalik's currency.sol, making it a live, immutable artifact that predates the ERC-20 standard itself by years. The Frontier Ruins SVG background honours this directly — broken stone arches, the original CurrencyCoin bytecode etched into the walls, a deep purple sky that signals archaeological depth. Every DINO minted in this pool carries a permanent visual reference to rfikki's September 8, 2015 deployment — the oldest surviving token contract in the DinoHook ecosystem.

CC badge guaranteed at mint. Every DINO minted via the DINO/CC pool automatically receives the CC coin badge — a plasma-purple circle rendered in the badge panel below the dinosaur body. This badge cannot be purchased, traded, or manufactured. The only way to permanently attach the CC badge to a DINO NFT at mint without a rarity roll is to use the CC pool. This creates a direct, protocol-enforced demand signal: collectors who want a guaranteed CC badge must acquire and use CC tokens.

LP gate: must hold CC to provide liquidity. The DinoLiquidityManagerHook requires any wallet adding liquidity to the DINO/CC pool to hold at least 1 CC token at the time of deposit. This is not a soft recommendation — it is enforced at the contract level and reverts the transaction if not met. CC token holding is therefore a prerequisite for LP participation in what will become one of the most actively traded pools in the DinoHook ecosystem. Every DINO holder who wants to earn LP fees from their NFT's home pool must first hold CC.

Fossil acceleration. Extinct DINOs born in the Frontier Ruins fossilize at 90 days rather than 180 — accelerating the transition to the rarest state in the collection. Fossilized Frontier Ruins DINOs are dual collectibles: they carry the CC badge, the Ruins biome, and the fossilized status simultaneously. These will be among the scarcest tokens in the collection by every rarity dimension simultaneously.

Battle advantage: Ruins beats Standard Origin. In the inter-biome battle system, Frontier Ruins DINOs have a +5% VRF weight advantage over Standard Origin (WMC) DINOs. This is the only on-chain competitive mechanic that pits the two most historically proximate tokens — CC (September 8, 2015) and WMC (November 3, 2015) — against each other. The lore justification is exact: older code predates the prototype. CC came before WMC. The battle system encodes this fact permanently.


G.4 How DINO flows into DinoHook

The DINO badge is the only one of the four 2015 collectible badges that cannot be obtained through a pool choice or a rarity roll. It is awarded exclusively to wallets that hold at least 1 unit of legacy Dinero 2015 (0x374642afe485d1a181b01f5b028d169be58f3106) at the moment of minting. Legacy Dinero was deployed by rfikki on December 10, 2015 — 93 days after he deployed CurrencyCoin — applying the same minimal Mist Browser design philosophy to a second token in Ethereum's founding year. The DINO badge is therefore a credential that links two facts simultaneously: the holder acquired rfikki's second major 2015 token, and they held it before minting their DinoHook NFT.

The DINO ERC-20 is the trading token. Every swap in every DinoHook pool uses wrapped DINO (the ERC-20 produced by WrappedDinero.sol) as one side of the pair. DINO/ETH, DINO/USDC, DINO/CC, DINO/WMC, and DINO/WAAC — DINO is on every single trading pair. This means:

  • Every swap generates on-chain volume for the DINO token
  • Every new pool adds a new trading pair that requires DINO liquidity
  • Every new LP deposit requires holding a DINO NFT — which requires a prior mint — which required paying the incubation fee in ETH, which flowed to the DAO treasury

DINO is not a passive collectible in DinoHook. It is the engine. Every economic interaction in the protocol — minting, swapping, battling, grafting, LP deposits — involves DINO tokens in motion.

The DINO/ETH pool is the Primordial Forge. The flagship pool carries the genesis lore: ETH was born on July 30, 2015, and the Primordial Forge is the oldest and most powerful biome in the system. DINOs minted here receive a +5% rarity roll uplift — the best natural odds of any pool — and one free epoch survival per year. The Forge is the reward for using DINO at the protocol's most fundamental level: the DINO/ETH pair.

Maximum wrapped supply is capped by legacy supply. The legacy Dinero contract has a fixed supply of 1,000,000 tokens that can never increase. Therefore the maximum possible wrapped DINO supply is exactly 1,000,000 ERC-20 DINO. This hard cap, enforced by the legacy contract's own immutability, creates a natural scarcity ceiling for the trading token across all five pools. As DinoHook volume grows, the demand for DINO tokens to provide liquidity must compete against this fixed ceiling.


G.5 The compound flywheel: how both tokens benefit from DinoHook's growth

The relationship between CC, DINO, and DinoHook is not linear — it is circular and self-reinforcing. Growth in any one component creates pressure across all others.

Flywheel sequence:

  1. A new collector wants a DinoHook NFT. They need wrapped DINO to participate in the ecosystem. They wrap legacy Dinero — increasing DINO ERC-20 supply in circulation.

  2. To mint in the Frontier Ruins and guarantee the CC badge, they acquire CC tokens. CC demand increases. CC holders who see new demand are incentivised to provide CC/DINO liquidity.

  3. Providing DINO/CC liquidity requires holding a DINO NFT — which the new collector now has. The LP lock period keeps their liquidity in the pool for at least 3 days, increasing depth.

  4. Deeper liquidity reduces slippage on DINO/CC swaps. Lower slippage attracts more traders. More traders generate more swap volume. More volume means more swap-triggered DinoHook NFTs, more leaderboard activity, more achievement badges, more cybernetic grafts.

  5. More DinoHook NFTs in circulation increases demand for CC-badged DINOs specifically (they are the only DINOs with the Frontier Ruins biome and guaranteed CC badge). Scarcity of this configuration drives secondary market interest. Secondary market interest attracts new collectors back to step 1.

What CC gets from DinoHook:

  • A Uniswap V4 pool it has never had before — the DINO/CC pool gives CC a liquid, on-chain trading venue for the first time
  • A demand signal for holding CC specifically: the LP gate, the badge guarantee, and the biome all require CC
  • A community of DinoHook collectors who are simultaneously CC badge holders — every DINO minted in Frontier Ruins is a new CC community member by definition
  • An on-chain story: CurrencyCoin is a historic blockchain collectible based on the currency.sol code and its early timestamp — but a collectible without a living ecosystem is a museum piece. DinoHook gives CC an active role in 2025 DeFi without compromising its historical identity

What DINO gets from DinoHook:

  • Five Uniswap V4 liquidity pools simultaneously — DINO goes from an illiquid 2-decimal legacy curiosity to a multi-pool DeFi token in a single deployment
  • A reason to wrap: the badge, the biome, and the collection all require interacting with the wrapped DINO ERC-20 — every minter wraps some legacy Dinero
  • A price discovery mechanism: LP pools with real depth enable real price discovery for the first time since 2015
  • A community that holds DINO for protocol utility, not just nostalgia — every LP provider is simultaneously a DINO holder and a DinoHook collector
  • A branding narrative: the collection is named DinoHook because of DINO. The entire visual identity — the coral T-Rex, the amber-gold dorsal spines, the teal hook — all reference the DINO token directly

What DinoHook gets from both tokens:

  • Historical legitimacy: the two oldest surviving tokens from the Frontier era provide an authentic founding narrative that no other NFT collection can claim
  • Liquidity bootstrapping: CC and WMC holders who want guaranteed badges are motivated to provide early liquidity — before the collection is minted out — creating deep pools from day one
  • Collector overlap: the communities of CC, WMC, WAAC, and DINO holders are natural early adopters. Each community brings its own audience to DinoHook, and every community member who mints a DinoHook NFT is introduced to all four other 2015 token communities simultaneously
  • Token scarcity as a feature: the 1,000,000 DINO hard cap and the fixed supplies of CC, WMC, and WAAC mean that as DinoHook grows, demand for the underlying tokens must be distributed across shrinking supplies. In a growing protocol, fixed-supply tokens appreciate in utility value

G.6 The badge as a permanent cross-community credential

The CC badge and DINO badge are not marketing features. They are on-chain state variables stored in the StructuralAnatomy struct — bool hasCc and bool hasDino — that are indexed by every marketplace, rarity tool, and block explorer that indexes DinoHook NFTs.

This means that every rarity platform will display "CC Badge: true" and "DINO Badge: true" as searchable, filterable attributes for the 10,000 DinoHook NFTs. Collectors browsing for the rarest DinoHooks will specifically seek out CC-badged and DINO-badged tokens. The badge attributes are stored immutably in the NFT's anatomy — they cannot be removed, overridden, or transferred. They are permanent on-chain proof of the holder's relationship to the 2015 token communities.

The four-badge full set — WAAC + WMC + CC + DINO — is the statistically rarest natural configuration achievable in the collection. A DINO with all four badges displays a dashed gold outer ring around the badge panel and is recognisable to any collector as a cross-community ambassador: a single token that carries provenance from all four Frontier-era token communities simultaneously.


G.7 DinoHook as a preservation mechanism

There is a deeper argument for why CC and DINO belong inside DinoHook that goes beyond economics: preservation — and recognition of the person who built both.

rfikki is one of the most consequential early Ethereum token developers in history. He deployed CurrencyCoin on September 8, 2015, using Vitalik Buterin's currency.sol — before ERC-20 existed, before MistCoin, before anyone could be certain that Ethereum tokens would become anything at all. He returned to the EVM three months later and deployed Dinero on December 10, 2015, extending his body of 2015 Frontier-era work. Two deployments, three months apart, by the same prolific pioneer. Together they span the complete arc of Ethereum's first token year: from the first experimental currency contracts in September through to the post-MistCoin, post-EIP-20-proposal era of December.

Both tokens exist as immutable bytecode on the Ethereum blockchain. They cannot be deleted. But immutability is not the same as recognition. A token that nobody trades, nobody holds for utility, and nobody builds upon is preserved in the technical sense — it will always exist at its contract address — but rfikki's contributions are not preserved in any meaningful cultural or economic sense.

DinoHook changes this. By building an active, growing DeFi protocol on top of both tokens, DinoHook ensures that CC and DINO are used, not merely remembered. Every swap in the DINO/CC pool is a new transaction in the CC contract's history. Every LP deposit is a new holder interacting with CC. Every minted DinoHook NFT is a new permanent record that links rfikki's September 8, 2015 deployment to a collector who chose to mint in the Frontier Ruins because they understood what CC represents.

The EIP-20 proposal officially cites CurrencyCoin's source repository, documenting its role as a direct building block to the standard. The blockchain confirms its deployed bytecode is unique, establishing it as a unique on-chain artifact. DinoHook is the first system to give both of rfikki's 2015 artefacts an active economic role proportional to their historical importance.

Ethereum's history is written in blocks. DinoHook adds new blocks to that history — blocks that reference the pioneering work of rfikki in the founding year of the EVM token economy, that require holding his tokens to participate, and that generate permanent on-chain records connecting the founders' era to the present. That is not just value creation for two tokens. It is recognition of the developer who built them — and the ongoing construction of Ethereum's own institutional memory.


G.8 Summary: what each stakeholder gains

Stakeholder Direct benefit from DinoHook
rfikki (deployer of both tokens) Both 2015 creations receive active economic roles for the first time since deployment · CC (Sep 8, 2015) and DINO (Dec 10, 2015) are recognised as the paired founding work of one pioneering developer, not two unrelated old tokens · DinoHook's entire architecture publicly documents and honours both deployments
CC token holder First Uniswap V4 pool · LP fee revenue · Guaranteed CC badge in Frontier Ruins DINOs · New community of collectors who hold CC as a protocol requirement
DINO token holder Five simultaneous V4 pools · Price discovery · Reason to wrap · DINO badge credential reserved for legacy holders · Protocol named after DINO · 1M hard cap creates scarcity as demand grows
DinoHook NFT collector Historical legitimacy from rfikki's two 2015 deployments · Four distinct coin badge types as permanent rarity attributes · Biome bonuses from CC and DINO pool choices · Cross-community exposure
Liquidity providers Earn trading fees on the first-ever liquid markets for CC and DINO · LP lock creates pool stability · NFT gating reduces mercenary liquidity
Ethereum ecosystem Two of the oldest surviving tokens from the Frontier era — both by the same pioneering developer — preserved in active use · The story of ERC-20's origin encoded permanently into 10,000 on-chain SVG artefacts

Appendix G reflects the protocol as designed at v9. The synergies described are structural — encoded in the smart contracts themselves — not dependent on market conditions or external partnerships.

Document version: v10 — CollectibleTrust organisation name updated throughout. All 2015 legacy token deployment dates verified and made explicit. Appendix F added (per-contract code discussion). Appendix G added (CC and DINO synergy addendum). Full guide: 7 appendices, 26 sections, complete technical and collector reference.


⚠️ This is Unaudited Code — Use at your own risk.

The DinoHook smart contracts have not undergone a formal third-party security audit. All contract interactions are final and irreversible on the Ethereum blockchain. Do not interact with any contract unless you have independently verified its address through official governance channels. CollectibleTrust DAO LLC - A RMI Registered Not-For-Profit makes no warranties, express or implied, regarding the security, correctness, or fitness for purpose of this code. Always do your own research.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment