Documentation

How it works on Ethereum.

  1. Overview
  2. Smart Contracts
  3. Claiming
  4. Onchain Rendering
  5. Trait Rules
  6. Existence Check
  7. Treasury
  8. Ownership
  9. Existence Toggle
  10. Improvenance
  11. Source of Truth
  12. Verification

Overview

This Punk Does Not Exist (TPDNE) is a fully onchain ERC-721 that generates CryptoPunk trait combinations not found in the original 10,000 collection. Same pixel art, same palette, same rendering — just combinations that were never claimed.

Every punk is 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 original collection ends.

TPDNE launches in Does Not Exist mode: generated onchain, visually absent. See Existence Toggle.

Smart Contracts

The system is two contracts on Ethereum mainnet:

ThisPunkDoesNotExist.sol

The ERC-721 token. Handles claiming, random generation, duplicate checking, and treasury management. Stores each punk as a packed bytes8 value.

PunkRenderer.sol

The rendering engine. Stores the full 120-color RGBA palette and pixel data for all 11 base types and 87 traits (male and female variants). Composites layers with alpha blending and outputs SVG.

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

Claiming

Claiming is a single transaction. You call mint(count) with the protocol fee, and the contract generates a random punk directly from block.prevrandao, your address, and the current supply.

You don't choose your punk's traits. The blockchain does.

Generation

For each claim, the contract:

  1. Picks a random base type (0–10): Male 1–4, Female 1–4, Zombie, Ape, or Alien
  2. 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)
  3. Picks random categories via Fisher-Yates shuffle (hair, eyes, mouth, etc.)
  4. Selects a random trait from each chosen category's pool
  5. Checks the combo doesn't duplicate a real CryptoPunk or a previously claimed TPDNE

If the generated combo already exists, the contract retries (up to 10 attempts per claim). Batch claiming supports up to 20 punks per transaction.

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.

Storage Format

Each punk is packed into 8 bytes. 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...]

Onchain Rendering

tokenURI() returns a fully self-contained data:application/json;base64 URI containing the name, description, attributes, and a data:image/svg+xml;base64 image.

Pixel Encoding

Pixel data uses the same 3-byte-per-block encoding as the original CryptoPunks. 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 original CryptoPunks 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 real CryptoPunks:

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.

Trait Rules

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 real CryptoPunks 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 claiming, the contract verifies the generated combination doesn't exist in the real 10k collection. 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 real CryptoPunk has that exact combination of type + count + accessories. The combo is rejected and the contract retries.

Previously claimed 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 shared treasury. The treasury's purpose is to buy real CryptoPunks (both V1 and V2) on behalf of all TPDNE holders.

Protocol Fee

0.000404 ETH per claim. The owner can adjust this, capped at a maximum of 0.01 ETH.

Permissionless Buying

Anyone can trigger a treasury punk purchase — no admin key required. Both V2 (CryptoPunksMarket) and V1 (Jalil's trustless PunksMarket) are supported. The caller specifies slippage protection parameters to prevent front-running:

The contract records the total cost (purchase price + brokerage) as the punk's cost basis. This is used to enforce profitability on sales.

Brokerage

A 10% brokerage on purchases goes to the fee recipient. For V2 buys, brokerage is computed on the listing price. For V1 buys, brokerage is computed on the actual cost (balance before − balance after), handling any refunds from the marketplace. The brokerage is permanently disableable onchain.

Owner-Only Selling

Only the contract owner can sell treasury punks, and only by accepting existing bids — the contract cannot list punks for sale or set prices. Every sale enforces profitability: the bid must be at least the punk's cost basis.

This design means the treasury can never sell a punk at a loss. Punks go in, and they only come out at a profit.

Ownership

The contract has a single owner address that controls admin functions: toggling collection visibility (existence toggle), accepting bids on treasury punks, adjusting the protocol fee, changing the fee recipient, disabling brokerage, and updating the renderer contract.

The owner cannot claim, cannot access the treasury ETH directly, and cannot list punks for sale. Buying punks is permissionless — anyone can trigger it.

Ownership transfer is two-step: transferOwnership() sets a pending owner, then acceptOwnership() must be called by the new address to complete the transfer. This prevents accidental loss of ownership to a wrong address. The modular upgrade path: start with an EOA, transfer to a multisig (e.g. Safe), then to a full DAO (e.g. OZ Governor with timelock) as the community grows. No custom governance code needed — battle-tested infrastructure handles it.

Existence Toggle

TPDNE launches in Does Not Exist mode: every punk is generated and stored onchain exactly as described above, but token images render only as the CryptoPunks blue background. No trait layers. No improvenance pixel. The metadata name reads "Punk #10000 Does Not Exist".

This Punk Does Not Exist until permission does.

The contract has a global doesNotExist boolean (default true) and two owner-only functions:

This is collection-level, not per-token. Holders cannot control reveal. The owner controls whether the punks exist or do not. Because it uses the same ownership model, control can be transferred to a Safe, DAO executor, or timelock.

Claiming, generation, storage, existence checks, trait rules, treasury logic, and token IDs are completely unaffected by the toggle. Everything is generated and committed onchain regardless of visibility state.

We are seeking permission from NODE Foundation, the IP owner of CryptoPunks, before toggling existence on. If none granted, we await CryptoPunks ascension to the public domain.

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 real CryptoPunks 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 CryptoPunks. 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 original CryptoPunks 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 original 10,000 CryptoPunks 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 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 NODE Foundation or the original CryptoPunks project. TPDNE launches in Does Not Exist mode while we seek permission from NODE Foundation, the IP owner of CryptoPunks. If none granted, we await CryptoPunks ascension to the public domain.