EvolvingPunks × $EVOLVE
A self-mutating on-chain NFT collection bonded to an information-theoretic deflationary token. Time is the engine. Shannon surprisal is the reward. Burn is the only finality.
Abstract
EvolvingPunks is the first on-chain NFT collection where each token's visual state is a deterministic function of time + immutable seed + holder-controlled finality, with no off-chain dependency. Every minted punk is born with a 256-bit gene that decides what it is, when each of its 8 traits wakes up, and in what order. Once awake, each trait rerolls every epoch until the holder calls freeze() — an atomic on-chain action that locks the current pick and re-mixes future entropy.
Bonded to the NFT is $EVOLVE, a hard-capped ERC-20 whose only mint path is burning a punk. The reward is not arbitrary tiers or 1/p rarity scores — it is the Shannon surprisal −log₂(p_OG) of each frozen trait, denominated in bits and converted 1:1 into $EVOLVE. A counter-current Dutch auction burns $EVOLVE to mint fresh punks at a linearly decaying price. A buyback treasury fed by a Uniswap V4 hook absorbs 0.5% of every ETH-side swap and atomically dual-burns NFT+token, draining both supplies whenever floor punks list cheap enough.
This document is the technical and economic specification. It does not skip edge cases. Where a critic can poke a hole, we point at the hole first.
I The problem with static NFTs
Why the JPEG paradigm exhausted itself.
Five years into the NFT cycle, three structural failures persist:
- Static art, fake dynamism. Most "evolving" projects either lock state at reveal or depend on a centralized server to flip metadata. The chain stores a hash; the experience is JSON over HTTP.
- Rarity decided once, never paid for. A 1-in-10,000 trait at mint is exactly as rare a year later. There is no mechanic for the holder to do anything to earn a rare claim — no commitment, no risk, no skill.
- Token economies bolted on as afterthoughts. Project tokens typically arrive via airdrop, governance theater, or staking emissions. None of them have an intrinsic pricing signal tied to the NFT itself. The token's value floats on speculation; the NFT's value floats on vibes.
EvolvingPunks attacks all three. The art is alive on-chain, not flagged dynamic by metadata. Rarity is a verb (freeze()), not an adjective. And the token's supply is fundamentally bounded by the information content of the NFTs that birthed it.
We do not claim to be the first NFT that ever changes. Async Art holders edit layers; Pak time-released visuals; Bright Moments staged reveals. We claim to be the first where (a) the change is autonomous (no holder, oracle, or team triggers it), (b) the change is metered as information, and (c) the information is the unit of account for a paired token. All three at once is new.
II Design principles
Five non-negotiables that shaped every other decision.
- Permanence of identity. The base type (Male 1–4, Female 1–4, Zombie, Ape, Alien) is sampled at mint and never overwritten. A Zombie is a Zombie forever; the chain commits at block 1.
- Playability through time, not interaction. The holder does not do anything to make their punk evolve. The chain does it. Holder-initiated actions (
freeze,burn) are about committing to or realizing states, never about generating them. - Information-theoretic rewards. Reward per slot is
−log₂(p_OG), denominated in bits. This is the only natural unit that is additive across slots and compresses extremes without arbitrariness. - Closed economic loop. Every minted $EVOLVE must originate from an NFT burn. Every NFT burned permanently shrinks the 10,000-cap supply. The two markets feed each other; neither is a faucet.
- Composable, single-responsibility contracts. NFT, ERC-20, Converter, BuybackTreasury, and the V4 hook are five distinct deployments. Each stays under EIP-170's 24 KB ceiling. Each can be audited and replaced independently.
III NFT mechanic
The Phase 1 contract, live on Ethereum, no token dependency.
3.1 The gene · dual-seed model
At mint, the contract derives a 256-bit seed from keccak256(prevrandao, block.timestamp, msg.sender, tokenId) and stores it in two distinct fields:
mintSeed— immutable. Drives (a) the base type via an OG-weighted draw across 11 outcomes, (b) the permutation of slot unlock positions via Fisher-Yates shuffle, and (c) each slot's individual unlock delay sampled from[UNLOCK_MIN, UNLOCK_MAX]. The first slot in the random order always gets delay 0, guaranteeing every fresh punk has one trait awake from block 1.entropy— initially equal tomintSeed, but rehashed withblock.prevrandaoevery time the holder callsfreeze(). Drives the per-epoch trait pick inside each currently-unlocked slot.
The split is load-bearing: identity is fixed at mint and verifiable forever, but the trajectory mutates on each commit. This prevents the most obvious attack — observe future state cheaply, then burn at peak — because the holder cannot freeze without re-rolling the future.
Given r = keccak(mintSeed, "base") mod 10000, base i is selected iff Σⱼ<ᵢ wⱼ ≤ r < Σⱼ≤ᵢ wⱼ where wⱼ are the OG punks occurrence counts [1723, 1857, 1861, 598, 1101, 1174, 1145, 420, 88, 24, 9] for the 11 base types respectively. Sum = 10000 exactly. The distribution is faithful to the original CryptoPunks corpus.
3.2 Slot system · 8 mutex layers
Each punk has eight mutually-exclusive trait slots: Head, Eyes, LowerFace, Mouth, Expression, Face, Earring, Neck. The 87 traits in the OG CryptoPunks corpus are partitioned across these slots such that any single punk can occupy at most one trait per slot. Mutex constraints were derived from on-chain co-occurrence analysis of the canonical 10,000 collection.
Each slot maintains three pieces of state: an unlock timestamp (when it becomes eligible to roll), an empty threshold (the probability of rolling no trait at all in that slot, calibrated to OG distribution), and a weighted trait pool. Selection per epoch is r = keccak(entropy, slot_id, epoch) mod 10000; if r < emptyThreshold the slot renders empty; otherwise the lower 240 bits of r index into the slot's weighted trait table.
| Slot | Empty % | Traits | Rarest · surprisal |
|---|---|---|---|
| Head | 2.46% | 41 | Beanie · 7.83 bits |
| Eyes | 39.28% | 16 | Welding Goggles · 6.86 bits |
| LowerFace | 45.29% | 15 | Big Beard · 6.10 bits |
| Mouth | 82.75% | 4 | Medical Mask · 5.84 bits |
| Expression | 94.23% | 3 | Buck Teeth · 7.00 bits |
| Face | 89.00% | 4 | Spots · 6.33 bits |
| Earring | 75.41% | 1 | Earring · 2.02 bits |
| Neck | 96.27% | 3 | Choker · 7.70 bits |
Slot 5 (Face) has weights that sum to 10,008 rather than 10,000 — an 8-out-of-10,000 overflow inherited from a small number of OG punks that ship with multiple Face-adjacent traits. The mutex semantic is violated by 0.08%. Surprisal calculations treat the slot as exact and absorb the rounding into the most-common trait. The error is below the precision threshold of any downstream economic computation; we disclose it rather than paper over it.
3.3 Freeze · the only finality
Calling freeze(tokenId) is payable and charges a flat freezeFee
(currently 0.001 ETH, mutable by the protocol owner) routed to the
freezeFeeRecipient. The fee is purely an anti-spam guard — see the
anti-grinding paragraph at the end of this section — not a value-extraction lever.
On success the call performs three operations atomically:
- For each slot that is currently unlocked-and-not-frozen, the contract reads the present epoch's trait pick and writes it into the token's frozen mask.
- The entropy field is rehashed:
entropy = keccak(entropy, prevrandao, block.timestamp). From this moment onward, every still-rolling slot draws from a different deterministic future. - A
Freeze(tokenId, newFrozenMask, newEntropy)event is emitted.
There is no unfreeze. There is no batch freeze with selective slots — every eligible slot gets committed together. There is no way to peek at the next epoch's roll without paying for it.
Anti-grinding, two layers: (1) the fee is charged even when the call freezes nothing new, so attempting to repeatedly poke freeze() to refresh randomness costs real ETH per attempt. (2) If no slot transitioned to frozen, the entropy is not rewritten at all — so even after paying the fee, you don't get a fresh trajectory. Both gates close the "freeze-watch-burn" exploit independently.
3.4 On-chain rendering
The 11 base sprites and 87 trait sprites — 98 entries × 576 bytes each — are stored via SSTORE2 across three chunks pre-deployed at fixed addresses. Each sprite is a 24×24 palette-indexed image; the palette (124 colors × 3 bytes) lives in contract storage. tokenURI() reads the relevant chunk, composites the base + layered traits in z-order, encodes as SVG, and returns a base64 data URI. The function is pure-view and has no off-chain dependency.
Three storage slots per token (96 bytes) hold the entire state: mintSeed (32 B), entropy (32 B), and a packed uint144 carrying mintTime (64) | baseId (8) | frozenMask (8) | frozenIdxPacked (64).
IV Information-theoretic rewards
Why Shannon surprisal — and why nothing else works.
The reward per frozen slot is the slot trait's surprisal under the OG distribution:
surprisal(p) = −log2(p) [units: bits]
1 bit ⟷ 1 $EVOLVE [1e18 wei]
Three properties make this unique among NFT reward schemes:
- Additive across slots. Joint surprisal of independent picks equals the sum of individual surprisals:
−log₂(p₁ · p₂) = −log₂(p₁) − log₂(p₂). A maximally-frozen punk earns the sum of its parts, never an explosive product. - Logarithmically compressed. Naive
1/pwould weight an Alien (p=0.0009) at 1111× a Male 3 (p=0.1861). Surprisal weights them at 10.12 bits vs 2.43 bits — a 4.2× ratio. Rare is still rare, but the curve is liveable. - Has dimensional meaning. A bit is the capacity to distinguish one binary outcome. Burning a punk that resolves 30 bits of OG-distribution information is exactly the amount of new state your action injected into the world. The token unit is not arbitrary.
Computing −log₂(p) on-chain on every burn is unnecessary. The OG distribution is fixed; there are ~70 distinct count values across 11 bases + 87 traits + 8 empty thresholds. The Converter contract deploys a precomputed ogCount → surprisalBitsE18 table (≈3 KB, well within the 24 KB EIP-170 ceiling). Burns become a sequence of O(1) lookups and adds; no fixed-point logarithm needed.
Reference values: Alien base = 10.12 bits; Male 3 base = 2.43 bits; Head empty = 5.35 bits; Head occupied by Beanie = 7.83 bits. A maximally frozen Alien-Cap-Forward with rare traits in every slot yields approximately 60 EVOLVE; a zero-freeze burn of the same Alien still yields 10.12 EVOLVE (base is always counted).
V Token economics
Phase 2 — specification complete; deployment pending.
5.1 Supply structure
$EVOLVE is a standard ERC-20 with two non-standard properties: hard cap and minter whitelist.
- MAX_SUPPLY = 600,000 EVOLVE. The cap = 10,000 punks × ~60 EVOLVE per maximally-frozen burn. Enforced at the mint function; any mint that would push
totalSupplyover the cap reverts. - Launch LP inventory = 60,000 EVOLVE (10% of cap) seeded into the V4 pool at launch. This is the only EVOLVE that does not originate from a punk burn.
- Minter whitelist = { Converter }. Only the Converter contract may call
mint(). The whitelist is mutable by Evolve owner, allowing future Converter upgrades, but governance is the bottleneck — not a free faucet. - Burns are unrestricted by design: anyone holding EVOLVE can burn from their own balance.
The supply equation across the lifetime of the protocol:
where converterMintBurns are payments by Dutch-mint callers, and protocolBurns are dual-burns from the Treasury.
5.2 Converter · the NFT ⟷ EVOLVE bridge
The Converter contract is the only on-ramp/off-ramp between the two tokens. It exposes three entrypoints:
| Entrypoint | Caller | Effect |
|---|---|---|
burnNFT(id) | NFT holder | Punk destroyed; surprisal bits minted as EVOLVE to holder. |
mintNFT(maxPrice) | Anyone with EVOLVE | EVOLVE burned at current Dutch price; fresh punk minted with new mintSeed. |
protocolBurn(id) | Treasury only | Same as burnNFT but minted EVOLVE goes to Treasury, then immediately burned in the same transaction. |
Dutch-minted punks receive a fresh random mintSeed; there is no trait inheritance from any burned punk. This is intentional — the token-NFT bridge is an economic loop, not a reincarnation mechanic. The 10,000 supply cap is shared between primary mints (paid in ETH) and Dutch mints (paid in EVOLVE); the Converter pre-checks totalMinted < 10,000 before burning EVOLVE, so a hitting-the-cap caller never loses their tokens.
5.3 Dutch auction · linear 24h decay
The Dutch auction price function is:
elapsed = block.timestamp − auctionStart
Anchor points: t=0h → 60, t=12h → 30, t≥24h → 0 (free). Each successful reforge resets auctionStart to the current block, returning the price to 60 EVOLVE. Linear (not exponential) was chosen for three reasons: (1) trivial Solidity arithmetic, (2) auditable in 30 seconds, (3) easily reasoned about off-chain by bidders.
The 32 EVOLVE floor is approximately equal to one fully-frozen Alien-Cap-Forward burn yield. This is intentional — at the floor, you are effectively recombining one full rare punk's worth of bits into a fresh roll. If the floor were lower, mint would dominate burn; if higher, the loop would deflate to a stall.
5.4 Buyback treasury · dual-burn engine
The BuybackTreasury holds ETH (and only ETH). Its single externally-callable action is buyAndBurnNFT(tokenId, target, calldata, value):
- Call
targetwithcalldataandvalueETH. (Typically this is a marketplace fillOrder.) - Verify post-call that the Treasury now owns the NFT (
nft.ownerOf(id) == self). - Call
converter.protocolBurn(id), which burns the NFT and mints its surprisal-equivalent EVOLVE directly into the Treasury. - Call
evolve.burn(self, balanceOf(self))— destroying the EVOLVE just received.
All four steps happen in one transaction. The invariant is sharp: at every block boundary, evolve.balanceOf(treasury) == 0. The Treasury never holds EVOLVE across blocks; it cannot manipulate markets or be drained by other actors.
ETH budget for the Treasury accumulates over time via maxBuyPrice():
on every successful buyback, lastBuyBlock resets to block.number.
Each call must satisfy value ≤ maxBuyPrice(), preventing one-shot treasury drain by a single keeper. Combined with nonReentrant, post-call ownerOf verification, and a completely open target field (no marketplace whitelist), the design accepts an unrestricted external call but contains it behind invariants the contract can verify after the fact.
5.5 V4 hook · automated treasury feed
Once Uniswap V4 is live on the target chain, an EvolveFeeHook contract attaches to the EVOLVE/ETH pool and implements afterSwap. On every swap in either direction, the hook computes 0.5% of the ETH-side delta and routes it to the Treasury via poolManager.take(). The token leg is untouched; the EVOLVE side is unaffected.
The hook is an optimization, not a dependency. The Treasury also accepts direct ETH transfers; for the launch period (before the V4 hook is wired up), buyback funding can be a manual operation. The dual-burn loop functions regardless of how ETH arrives.
VI Architecture · five contracts, one loop
Why we did not merge.
| Contract | Responsibility | Approx size |
|---|---|---|
| EvolvingPunks | ERC-721, slot rolls, freeze, on-chain SVG | ~14 KB |
| Evolve | ERC-20, hard cap, minter whitelist | ~2 KB |
| Converter | Burn-to-mint, Dutch auction, surprisal table | ~5 KB |
| BuybackTreasury | ETH budget, dual-burn, marketplace call | ~3 KB |
| EvolveFeeHook | V4 afterSwap, 0.5% ETH-side fee capture | ~1 KB |
A monolithic version would exceed EIP-170's 24 KB deployed-code ceiling. More importantly, separation lets each contract evolve independently. The NFT contract is intended to be permanent; the Converter and Treasury are upgradeable through governance (Evolve owner can swap them by updating the minter whitelist and authorized burner). The V4 hook is plug-in: pool deployers attach it, but it has no power over the NFT or token contracts beyond its own fee-take.
Three protocol-wide invariants:
- EVOLVE supply equation (see §5.1) holds at all times; only the Converter mints, anyone may burn.
- NFT supply is monotonic-decreasing after secondary mints close. Primary mints (paid ETH) and Dutch mints (paid EVOLVE) both write to the same
totalMintedcounter, which can never decrease but is capped at 10,000. - Treasury holds no EVOLVE across block boundaries. Any EVOLVE the Treasury receives is burned in the same transaction.
VII Security & known trade-offs
If you can poke a hole, we want to point at it first.
| Risk | Surface | Mitigation |
|---|---|---|
| R1 | Proposer grinding on prevrandao can bias mint outcomes (~1-bit influence). | Bounded influence; if grinding becomes economically meaningful at scale, the upgrade path is Chainlink VRF for mintSeed generation. |
| R2 | Indexer / marketplace metadata cache lag — display can lag chain state. | No contract-level fix is possible. The chain is authoritative; tokenURI() is the source of truth. Disclosed. |
| R3 | buyAndBurnNFT accepts arbitrary target + calldata. | nonReentrant + post-call ownerOf verification + bounded budget. By design, no marketplace whitelist. |
| R4 | Dutch auction front-run between user's maxPrice and execution. | UI applies 1.5× slippage buffer; on revert, EVOLVE is returned to caller. |
| R5 | OG distribution table is hardcoded; spec changes require Converter redeploy. | Documented. Evolve's minter whitelist is mutable, supporting Converter swap without redeploying NFT or token. |
| R6 | Slot 5 (Face) weights sum to 10,008 (0.08% mutex violation). | See §3.2 callout. Disclosed; below economic precision threshold. |
Three classes of attack we explicitly do not defend against, because the design considers them out of scope:
- Off-chain front-end manipulation. A malicious clone of the website can show fake state. Users should verify with
tokenURIdirectly. The chain is the source of truth. - Permissioned governance disasters. If the Evolve owner sets a malicious minter, EVOLVE supply integrity breaks. Owner is initially the deployer multisig; governance hardening is a Phase 3 concern.
- MEV on the buyback path. A sophisticated searcher can sandwich the marketplace leg of
buyAndBurnNFT. This is fine — the Treasury pays at mostmaxBuyPrice(); downstream marketplace mechanics are not our problem.
Audit scope (Phase 2 prerequisite): all five contracts, focus on supply equation invariants and the dual-burn atomic guarantee.
VIII Prior art · what we are not
Direct comparisons, neither dismissive nor inflated.
| Project | Dynamic? | Token-bonded? | How we differ |
|---|---|---|---|
| CryptoPunks | No | No | We are a CC0-derivative inheriting the visual lineage, with an additive evolution mechanic and a separate token economy. |
| Bored Ape | No | Yes (ApeCoin) | ApeCoin's supply is unrelated to the NFTs and emitted via staking. EVOLVE is mintable only by burning an NFT. |
| Art Blocks | No (generative-once) | No | Art Blocks freezes state at mint. We freeze state per slot, per epoch, by holder commitment. |
| Async Art | Yes (holder-edited layers) | No | Async dynamism is holder-triggered. Ours is autonomous and time-driven. |
| Pak's generative drops | Yes (curated) | Sometimes | Pak's variants change on team-curated schedules. Ours is a pure function of block.timestamp and mintSeed. |
| Bright Moments | Reveal-staged | No | Staged reveals are one-time. Ours is unbounded. |
What is genuinely new in this protocol is the conjunction: autonomous on-chain evolution + information-theoretic reward unit + supply-bounded token tied to NFT burns + dual-burn buyback from V4 swap fees. We are not the first project to do any one of these. We believe we are the first to do all four together in a single non-custodial system.
IX Roadmap
Concrete milestones, no marketing fluff.
- Phase 1 · NFT live · Completed May 2026. EvolvingPunks deployed on Ethereum, 10,000-cap mint, on-chain rendering operational, freeze mechanic active. Per-slot unlock delay is drawn from each punk's
mintSeedat mint; awake slots reroll every 6 hours. The site you are reading runs against this contract. - Phase 1.5 · External audit · Q3 2026. Independent audit (TBD); proposer-grinding mitigation via Chainlink VRF integration if needed at scale.
- Phase 2 · Token economy goes live · Q4 2026 target. Evolve ERC-20, EvolveHook (Converter + Treasury merged), and EvolveFeeHook deployed. Launch LP of 60K EVOLVE seeded into Uniswap V4 pool. Public Dutch auction (60 → 0 over 24h) opens.
- Phase 3 · Governance + marketplace · 2027. Fixed-price NFT marketplace (priced in ETH initially, EVOLVE-denominated TBD). Owner controls (mint price, Converter swap rights) transition to multisig and eventually DAO.
Nothing in this document obligates the team to a schedule. We will ship when the contracts pass audit, the math is verified, and the loop is closed. If a Phase 2 prerequisite slips, Phase 1 continues to operate.
X Why it matters
The thesis, in one passage.
For most of NFTs' history, the chain has stored a hash and the experience has lived elsewhere. Pictures cached on CDNs, metadata served by team-controlled APIs, "dynamic" properties flipped manually when a roadmap milestone hits. The blockchain has been doing very little of the work. It has been a notary, not an engine.
EvolvingPunks treats the chain as the engine. The contract holds the state machine; the holder holds an immutable seed and a sequence of finality commits; the token's supply curve is determined by the information content of the work the chain has done. There is no off-chain dependency. There is no team flipping switches. There is no roadmap that needs to be executed for the art to evolve. The clock ticks. The hash chains. The punks change.
The token side closes the loop. $EVOLVE's supply is not a function of how many holders we attract or how much we farm — it is a function of how much OG-distribution surprise the network has resolved through committed burns. Every minted EVOLVE represents bits of information the chain has converted from possibility into permanence. Every burned NFT permanently shrinks the 10,000-cap supply. The two markets pull at each other; the V4 hook ensures every swap on either side feeds the dual-burn. There is no faucet. There is no free supply. There is no off-chain operator who has to choose to take action.
We do not claim this design is the end of NFT mechanics. We claim it is the first time the chain has had to do most of the work, and the first time the token's value comes from a quantity the chain can compute without trust. That is the paradigm shift we are pointing at.
Burn a punk. Mint a punk. The chain remembers. The chain commits. Everything else is just the holder watching their gene play out.