Documentation

There are millions of trait combinations not in the original 10,000. So many grails that will never exist. But what if they could? With permission of course!

  1. Overview
  2. Settlement Trigger
  3. Smart Contracts
  4. Claiming
  5. Treasury
  6. Deferred Reveal
  7. Onchain Rendering
  8. Dormant Renderer
  9. Trait Rules
  10. Existence Check
  11. Trust Model
  12. Improvenance
  13. Source of Truth
  14. Verification

Overview

This Punk Does Not Exist (TPDNE) is a fully onchain ERC-721 and capital-formation campaign on Ethereum. The stated goal is to acquire the CryptoPunks IP and release it into the public domain. The consolation outcome is a license or grant of permission — the right to exist without full ownership of the IP.

Both outcomes run through the same settlement mechanism: a one-time lump-sum payment for a perpetual result. The payment buys the right to exist; deal terms decide whether that’s full acquisition or a license.

100% of claim fees pool into a shared treasury. Contributions are fully refundable until settlement closes. Holders can burn their tokens at any time pre-settlement to recover 100% of what they paid. Every proposed settlement has a 7-day timelock — that window is the last chance to exit before funds are committed. If enough holders burn during the window, the deal reverts. If the campaign deadline passes without settlement, holders can still burn-for-refund.

Tokens launch as identical blue squares with no traits, no rendered art, and no per-token trait metadata. Trait generation and rendering are deferred unless and until settlement is accepted. Until then, the artwork is absence itself: a punk-shaped void on Ethereum.

If/when settlement succeeds, traits are generated and rendered entirely onchain. No IPFS, no external hosting, no off-chain metadata. The SVG image and JSON attributes are constructed in Solidity from stored pixel data and returned as base64 data URIs. Token IDs start at 10000, continuing where the originals end.

See Settlement Trigger, Treasury, Deferred Reveal, and Dormant Renderer.

Settlement Trigger

TPDNE launches in Does Not Exist mode. All tokens render as blue squares. No trait layers, no art, no per-token metadata until settlement.

Settlement is a two-step process with a 7-day timelock:

  1. nominateSettlement(recipient, amount) — owner-only. Proposes sending amount ETH to recipient in exchange for the right to exist — whether that’s full IP acquisition for the public domain or a license/permission grant. Starts a 7-day countdown. Can be overwritten with a new nomination at any time before acceptance (the overwrite resets the 7-day clock).
  2. acceptSettlement() — callable only by the nominated recipient after the 7-day delay. Transfers the settlement funds, sets doesNotExist = false and settlementAccepted = true. One-time, irreversible, lump-sum. No recurring payments.

The 7-Day Exit Window

Burn-for-refund is open at any time before settlement is accepted. But the 7-day timelock after a nomination is the critical window — the last opportunity to evaluate a proposed deal and exit with 100% of your contribution before funds are committed.

Any nomination must survive 7 days of holders being free to leave. If enough holders burn during the window, the treasury balance drops below the settlement amount and acceptSettlement() reverts — mass exit is a veto on the deal.

Headroom fragility: a settlement amount set close to the full treasury balance is fragile, because normal burns during the window can tip the balance below it and block an otherwise-supported deal. Nominators should leave headroom.

After settlement, doesNotExist flips to false permanently, enabling the reveal pipeline. Minting continues but is free (no fee charged post-settlement). Burn-for-refund closes permanently — refunds are only available pre-settlement.

Campaign Deadline

The campaign has a deadline set at deploy time (bounded between 180 days and 5 years). After the deadline, no new mints or settlement nominations can occur. The owner can extend the deadline (but never beyond the absolute maximum set at deploy). Burn-for-refund remains available after the deadline — holders can always exit.

Existence Control

Visual existence requires both doesNotExist == false (set by settlement) and a renderer contract. The metadata name always reads “Punk #[id] Does Not Exist” regardless of state.

A token can be revealed internally but still not visually exist if the renderer is unset. The owner can set and unset the renderer at any time, providing a second layer of control if the legal basis for rendering changes.

TPDNE waits on resolution of the underlying IP question — whether through acquisition for the public domain or a license/permission grant. No art from the originals is displayed. This Punk Does Not Exist until then, if ever.

Smart Contracts

The system launches as a single contract on Ethereum mainnet, with the renderer deployed separately if/when needed:

ThisPunkDoesNotExist.sol

The ERC-721 token. Handles claiming, burn-for-refund, settlement trigger, deferred reveal via blockhash commit-reveal, and campaign deadline enforcement. Trait generation and duplicate checking happen at reveal time, not at claim. Stores each punk as a packed bytes8 value. While the renderer is unset (address(0)), tokenURI() returns the blue-square state from an inline constant. Uses OpenZeppelin Ownable2Step (two-step ownership transfer) and ReentrancyGuard.

PunkRenderer.sol

Not deployed at launch. The renderer is dormant infrastructure for a future reveal. It stores the full 120-color RGBA palette and pixel data for all 11 base types and 87 traits (male and female variants). If/when deployed and set, it composites layers with alpha blending and outputs SVG. The owner can set and unset the renderer address at any time.

The renderer is sealable. Once sealed, no pixel data, palette, or trait metadata can be changed. The art is permanently frozen onchain.

Claiming

Your token starts as a blue square — no traits, no image, no punk. Traits are generated onchain if/when settlement succeeds.

Claiming is a single transaction. You call mint(count) with the protocol fee. Batch claiming supports up to 100 tokens per transaction. All claimed tokens are identical: same blue square, same metadata, differing only by token ID.

0.00404 ETH per claim. 100% goes to the shared treasury. Zero extraction — no operator fee, no brokerage. Every wei is either refundable to holders or allocated to settlement.

Your contribution is fully refundable until settlement closes. Burn your tokens anytime pre-settlement to recover 100% of what you paid. When a settlement is nominated, a 7-day window opens — your last chance to exit before funds are committed.

After settlement, claiming is free (no fee). Minting continues indefinitely post-settlement, expanding the collection with zero cost to new claimers. Post-settlement tokens have no contribution recorded and are not eligible for refund (since refunds close at settlement).

Storage Format

Each punk is packed into 8 bytes at reveal time. Byte 0 encodes both the base type (bits 0–4) and trait count (bits 5–7). Bytes 1–7 store sorted trait indices.

bytes8: [(traitCount << 5) | baseType] [trait0] [trait1] ... [traitN] [0x00...]

Deferred Reveal

Traits are not generated at claim time. If/when settlement succeeds, they are generated onchain using a random seed derived from a blockhash commit-reveal. No third-party dependencies. This is a three-phase process:

Phase 1: Claim

You pay the protocol fee and receive a token ID. No punkData is written. Your token renders as a blue square with the name “Punk #[id] Does Not Exist”.

Phase 2: Seed Commitment

After settlement is accepted, the owner calls commitSeed() which records block.number + 1 as the seed block. Once that block is produced, anyone can call finalizeSeed() to store blockhash(seedBlock) as the immutable reveal seed. The owner cannot predict a future block's hash at commit time. The seed can never be changed once finalized. Finalization must happen within 256 blocks (~50 minutes). If the window is missed, the owner can recommitSeed() to try again.

Phase 3: Reveal

Once the seed is committed, anyone can call reveal(tokenIds) to generate traits for a batch of up to 10 tokens. This is permissionless — the caller pays gas, but any address can reveal any token. For each token, the contract:

  1. Derives per-token randomness: keccak256(revealSeed, tokenId, nonce, attempt)
  2. Picks a weighted random base type (0–10): Male 1–4, Female 1–4, Zombie, Ape, or Alien — probabilities match the original 10k distribution
  3. Rolls a trait count using a cumulative distribution that mirrors the original collection (most punks have 2–3 traits, very few have 0 or 7)
  4. Picks random categories via Fisher-Yates shuffle (hair, eyes, mouth, etc.)
  5. Selects a random trait from each chosen category's pool
  6. Checks the combo doesn't duplicate any of the originals or a previously revealed TPDNE

If the generated combo already exists, the contract retries (up to 10 attempts per token). If all attempts collide, the token’s nonce is incremented and it can be retried later — the next attempt explores an entirely fresh set of combinations.

Trait Count Distribution

The CDF used for trait count selection (roll is a random uint8, 0–255):

Traits  CDF     Approx. probability
0       1/256   0.4%
1       9/256   3.1%
2       100/256 35.5%
3       215/256 44.9%
4       251/256 14.1%
5       254/256 1.2%
6       255/256 0.4%
7       256/256 0.4%  (roll = 255 only)

This is a byte-quantized approximation of the observed original distribution. Counts 1–4 match closely. At the tails, the smallest non-zero slot in a single-byte CDF is 1/256, so 0-trait and 7-trait punks each get ~0.4% despite different observed rates in the original 10k (8 zero-trait punks vs one 7-trait punk, #8348). The original generation process is undocumented; this CDF is reverse-engineered from the observed trait-count histogram, not from the original Larva Labs code.

Base Type Distribution

Base types are selected via a weighted CDF matching the original 10k distribution:

Type        CDF     Approx. probability
Male 1      44/256  17.2%  (1723 in originals)
Male 2      91/256  18.4%  (1857)
Male 3      139/256 18.8%  (1861)
Male 4      154/256 5.9%   (598)
Female 1    182/256 10.9%  (1101)
Female 2    212/256 11.7%  (1174)
Female 3    241/256 11.3%  (1145)
Female 4    252/256 4.3%   (420)
Zombie      254/256 0.8%   (88)
Ape         255/256 0.4%   (24)
Alien       256/256 0.4%   (9, fallthrough)

Alien and Ape have ~0.4% each (1/256, the minimum non-zero slot in a byte CDF), which slightly overrepresents them vs the originals (0.09% and 0.24%). All other types match within 0.2%.

Why Blockhash

No external dependencies. No oracles. No LINK tokens. No subscriptions that can expire. The seed is derived from Ethereum’s own block production — the owner commits to a block, and the blockhash of that block becomes the seed. The owner cannot predict future blockhashes, and validators cannot predict which hash benefits any particular token without running the full trait generation. Once the seed is finalized, every token’s traits are deterministic: the same seed + token ID always produces the same punk.

Onchain Rendering

If/when existence is enabled and a token is revealed, tokenURI() returns a fully self-contained data:application/json;base64 URI containing the name, description, attributes, and a data:image/svg+xml;base64 image. While Does Not Exist mode is active, tokenURI() returns only the blue-square state — see Dormant Renderer.

Pixel Encoding

Pixel data uses the same 3-byte-per-block encoding as the originals. Each block covers a 2×2 pixel region:

Each bit in the mask maps to a sub-pixel: bit 0 = (0,0), bit 1 = (0,1), bit 2 = (1,0), bit 3 = (1,1).

Compositing

Layers are composited bottom-to-top using the standard alpha "over" operator. The base type is drawn first, then each trait layer is composited on top in the canonical order defined by punksdata.eth.

Layer Order

Every trait has an asset ID. Traits are sorted by asset ID ascending (lower ID = rendered first = further back). Categories are interleaved across the ID range, so the effective render order from back to front is:

Every trait shown as pixel art, grouped by category in render order (back to front). Hover for names. Render order verified pixel-by-pixel against all 10,000 originals via indexedPixelsOf() on punksdata.eth. See methodology below.

Where layers overlap

Most trait pairs have no pixel overlap at all. Where they do, the rendering order is verified against the originals:

FrontBackReal punk
Beard Chain (neck) Big Beard covers Gold Chain on #2239, #4644
Mouth Beard Cigarette shows over Luxurious Beard on #2886
Mouth Lips Medical Mask covers Purple Lipstick on #2187
Hoodie (hair) Beard Hoodie covers Big Beard on #1644, #8515
Hoodie (hair) Earring (ears) Hoodie covers Earring on #87
Glasses / Shades (eyes) Medical Mask (mouth) 3D Glasses over Medical Mask on #1123, VR on #9636
Glasses / Shades (eyes) Hoodie (hair) Regular Shades over Hoodie on #87
Clown Nose Eyes / Mouth Clown Nose over VR on #2766, over Medical Mask on #4173
Vampire Hair Beard Vampire Hair covers Muttonchops on #236, Chinstrap on #1425
Purple Hair Beard Purple Hair covers Big Beard on #1127, #9054
Beard Expressions (Buck Teeth, Frown, Smile) Normal Beard hides Buck Teeth on #998, Handlebar hides Frown on #1303
Mouth Mole / Rosy Cheeks / Spots Medical Mask covers Rosy Cheeks on #3871, Spots on #7360

Palette

TPDNE’s renderer stores a 120-color RGBA palette (480 bytes): 105 fully opaque and 15 semi-transparent. These are the colors referenced by individual trait pixel layers (accessories, hair, facial features, etc.).

punksdata.eth exposes a larger 222-color palette via paletteRgbaBytes(). The additional 102 colors are used in punksdata.eth’s pre-composited indexedPixelsOf() output, which flattens all layers (base type skin, accessories, alpha-blended results) into a single indexed image. TPDNE doesn’t need those extra colors because it composites from individual trait layers at render time, not from pre-composited pixel data.

Dormant Renderer

The renderer is not deployed at launch. The main contract references address(0) for the renderer until it is set. While unset, tokenURI() returns the blue-square Does Not Exist state built from an inline SVG constant — no external call needed.

Visual rendering requires both doesNotExist == false (set by settlement) and a non-zero renderer address. Specifically, while either condition is unmet:

The owner can set and unset the renderer at any time. Setting it to address(0) returns all tokens to the dormant state regardless of doesNotExist. Settlement does not require a renderer — minting, committing, and revealing all work without one. The renderer only matters for visual output in tokenURI().

PunkRenderer.sol, when deployed, stores the full palette and pixel data for all base types and traits onchain. The renderer is sealable: once sealed, no pixel data, palette, or trait metadata can be changed, whether or not the collection visually exists.

Trait Rules

If/when revealed, TPDNE generates only trait combinations that could have existed in the original collection. Each base type has specific trait pools based on what appears on real punks of that type:

TypeCategoriesCount
Male (1–4)Hair, Eyes, Mouth, Face, Expression, Beard, Neck, Ears8
Female (1–4)Hair, Eyes, Mouth, Face, Lips, Neck, Ears7
ZombieHair, Eyes, Mouth, Face, Expression, Beard, Neck, Ears8
ApeHair, Eyes, Mouth, Neck, Ears5
AlienHair, Eyes, Mouth, Ears4

Category Exclusivity

Within each category, a punk can have at most one trait. This mirrors the original collection: you can't have two hairstyles, or two pairs of eyewear.

Expression vs. Mouth: In the original collection, expression traits (Buck Teeth, Frown, Smile) and mouth accessories (Cigarette, Medical Mask, Pipe, Vape) are separate categories. A punk can have both a Frown and a Cigarette — 83 of the originals do. TPDNE preserves this distinction.

Type-Specific Pools

Not every trait can appear on every type. For example, Aliens only have 7 hair options, 2 eye options, and 2 mouth options. Apes can't have beards. These restrictions are hardcoded in the contract and verified against punksdata.eth bitmaps.

Existence Check

Before a punk is revealed, the contract verifies the generated combination doesn't exist in the original 10k. This uses punksdata.eth trait bitmaps directly onchain.

The check ANDs together bitmap words for:

  1. The head variant (which base type the punk is)
  2. The attribute count (how many traits)
  3. Each individual accessory

If any word in the AND result is non-zero, at least one original punk has that exact combination of type + count + accessories. The combo is rejected and the contract retries with a different attempt seed.

Previously revealed TPDNE combos are tracked separately via a usedCombos mapping to prevent duplicates within the collection.

Treasury

100% of claim fees go to the contract itself, forming a capital-formation fund. The treasury exists for one purpose: to fund a settlement that gives the collection the right to exist — ideally through IP acquisition for the public domain, or through a license or permission grant.

Protocol Fee

0.00404 ETH per claim. The owner can adjust this, capped at a maximum of 0.01 ETH. After settlement, claiming is free.

Burn-for-Refund

Holders can burn their tokens at any time pre-settlement to recover 100% of their contribution. Each token tracks the fee paid at mint time in tokenContribution[tokenId]. Burning returns exactly that amount.

If the fee changes between mints, each token refunds exactly what was paid for it, not the current fee. A token minted at 0.00404 ETH refunds 0.00404 ETH even if the fee later changes.

Settlement Flow

The owner nominates a settlement: a recipient address and an ETH amount. After a 7-day timelock, the recipient can accept. This transfers the funds as a one-time lump-sum, flips doesNotExist to false, and enables the reveal pipeline. No recurring payments, no subscriptions. See Settlement Trigger.

The 7-day window is the holders’ exit period. Burn-for-refund is open anytime pre-settlement, but the 7 days after a nomination are the last chance to leave. If enough holders burn, the balance drops below the settlement amount and acceptance reverts — mass exit is a veto on the deal.

No Operator Fee

There is no brokerage, no operator fee, no extraction of any kind. Every wei in the treasury is either refundable to holders or allocated to settlement. The owner cannot withdraw ETH directly.

Trust Model

TPDNE launches with a single owner (EOA). This is an honest disclosure, not a feature. The owner has real power, and holders should understand exactly what that power is and how it is constrained.

What the Owner Can Do

What the Owner Cannot Do

Guardrails

The 7-day settlement timelock is the primary guardrail. It gives holders time to evaluate any proposed deal and exit if they disagree. The burn-for-refund mechanism is always-on pre-settlement and cannot be disabled by the owner.

The owner could nominate a bad-faith settlement (e.g., sending funds to themselves). The defense: if holders disagree, they burn-for-refund during the 7-day window. If enough holders exit, the contract balance drops below the settlement amount and acceptance reverts. This is economic rather than cryptographic protection.

What Could Go Wrong

The owner could grief the campaign by repeatedly overwriting nominations or setting unreasonable fees. This is mitigable but not eliminable at launch. The owner's reputation is the primary defense in the early stage.

Progressive Custody

The plan is to transition ownership from a single EOA to progressively more decentralized control as the campaign grows:

  1. Launch: single EOA (current)
  2. Phase 2: Safe multisig (target: TBD)
  3. Phase 3: DAO executor / timelock / onchain governance (target: TBD)

Ownership transfer is two-step (Ownable2Step): transferOwnership() sets a pending owner, then acceptOwnership() must be called by the new address. No custom governance code needed — battle-tested infrastructure handles it. The renderer can be sealed independently of ownership.

These thresholds are placeholders. They will be defined as the campaign develops and the community forms.

Improvenance

Every TPDNE punk carries a single-pixel marker at position (23, 23) — the bottom-right corner of the 24×24 grid. The pixel is rgba(160, 80, 80), a muted red that's invisible when cropped as a PFP but visible in the raw image.

This is intentional. It’s a provenance marker that distinguishes generated punks from the originals at the pixel level, while being practically invisible in everyday use.

Source of Truth

All trait rules, pixel data, and existence checks are verified against punksdata.eth (0x9cF9C8eA737A7d5157d3F4282aCe30880a7A117C), the sealed onchain data surface for the originals. It is the single source of truth for:

punksdata.eth is sealed and immutable. No one can modify the data it serves. TPDNE's trait pools, rendering order, and existence checks are all verified against it — see Verification below.

Verification

The originals have no documentation for their compositing rules. Trait layering order, category groupings, and special-case behaviors were reverse-engineered by comparing our renderer's output against the canonical pixel data stored onchain in punksdata.eth.

10,000 / 10,000 pixel-perfect

A verification script renders every one of the 10,000 originals through our renderer and compares the result pixel-by-pixel against indexedPixelsOf() on punksdata.eth. The canonical data is fetched via Multicall3 in batches of 50, converted from palette indices to RGBA, and compared against our composited output for all 576 pixels per punk.

The result is a 100% match — zero pixel discrepancies across all 10,000 punks. This confirms that our layer compositing order, alpha blending, and pixel data are identical to the originals.

How the rules were determined

The layering rules were discovered iteratively. The initial approach (sort by asset ID only) produced 139 mismatched punks. Each batch of mismatches revealed a pattern — for example, "all 53 punks with beard + expression are wrong" pointed to expressions needing to render under beards. After each fix, the full 10k sweep was re-run to confirm the fix didn't introduce regressions. The final rule set:

PriorityCategoryNotes
0FaceMole, Rosy Cheeks, Spots (skin-level, under everything)
1Ears
2HairMost hair traits
3NeckChains
4Lips
5ExpressionsBuck Teeth, Frown, Smile (hidden by beard)
6Beard
7Hair (special)Hoodie, Purple Hair, Vampire Hair (cover beard area)
10MouthCigarette, Medical Mask, Pipe, Vape
12EyesGlasses and shades render on top of mask and hoodie
13Clown NoseRenders over everything including eyes

This priority table is implemented identically in three places: the JavaScript renderer (renderer.js), the Solidity renderer (PunkRenderer.sol), and the verification script. A A consistency check confirms all three return the same priority for every trait.

Running the verification

# Full 10k pixel comparison (requires Ethereum RPC)
node scripts/verify-layering.js

# Single punk
node scripts/verify-layering.js --punk 4173

# Offline consistency checks (no RPC needed)
node scripts/verify-consistency.js

# Solidity unit tests
forge test

# Solidity fork tests against mainnet punksdata.eth
forge test --match-contract Verification --fork-url $RPC_URL
This project is not affiliated with the CryptoPunks project or its IP holders. No CryptoPunk art is displayed. TPDNE launches in Does Not Exist mode — tokens are blue squares with no traits, no image, no punk. The project is a capital-formation campaign. The ideal outcome is acquiring the IP for the public domain; the consolation is a license or permission grant. Both use the same settlement mechanism — a one-time payment for a perpetual right to exist. Contributions are fully refundable until settlement closes. This Punk Does Not Exist until settlement, if ever.