how it actually works.
Slonks is an on-chain art experiment about neural reconstruction, not perfect copying. A tiny image model was trained on the canonical 24×24 CryptoPunks set and uploaded into bytecode. Every metadata read pulls the model's render and the original Punk live, then counts the pixels where they disagree. That count is your slop. The bigger the disagreement, the higher your slop.
one slonk, three views.
The render is the artwork — a tiny neural reconstruction of the underlying CryptoPunk. The original and the slop are shown alongside so you can see the source and the pixels where the model disagreed.
the model is a candidate-palette renderer.
Not a transformer. Per pixel, the model picks one of 18 candidate colors from the exact 222-color CryptoPunks palette. A learned token embedding multiplies into a per-pixel head; the argmax selects the slot; that slot indexes into a precomputed candidate table to give a palette index.
slot[token, pixel] = argmax_k dot( embedding[token], head[pixel, k] ) palette_index = candidates[pixel, slot] rgba = PALETTE_RGBA[palette_index]
Why so much error? The art concept is high recognizability with a persistent slop field. The team picked the candidate-palette checkpoint precisely because its mistakes read like learned reconstruction rather than random damage. Perfect was never the goal.
the slop is not stored.
Zero bytes of slop data are uploaded. Every metadata read recomputes the mask live by comparing the model's 576 palette indexes to the 2,304 RGBA bytes pulled from CryptoPunksData. That's why your slop can drift if the model is ever swapped (the model is lockable on chain).
for pixel in 0..576:
color = PALETTE_RGBA[ generated[pixel] ]
differs = color != originalRgba[pixel*4 : pixel*4+4]
if differs:
mask[pixel/8] |= 1 << (7 - pixel%8)
count += 1
slop = count
tier = slop / 50 // floormerging blends embeddings.
Two slonks of the same merge level fuse into one. The survivor keeps its original Punk anchor (the slop is still computed against that). The donor's embedding gets averaged into the survivor's; the model renders from that blended embedding from then on. The donor token is burned.
for i in 0..embedding_length:
a = (int8) survivor.embedding[i] // -128..127
b = (int8) donor.embedding[i]
blended[i] = (uint8)(int8)( (a + b) / 2 ) // truncates toward zero
survivor.mergeLevel += 1
survivor.embedding = blended
burn(donor)four contracts, four jobs.
The NFT shell, the model facade, the optional merge state, and the swappable renderer. Splitting them keeps tokenURI presentation independent from token ownership and keeps the model immutable once locked. Tap any address to open Etherscan.
Slonks
Owns existence, transfer, the immutable 10,000 max supply, mint-time base source assignment, and the blockhash reveal. tokenURI just forwards the id to the renderer.
- ›SeaDrop-compatible mint hook
- ›Fisher-Yates source pool
- ›permissionless finalizeReveal()
- ›ERC-4906 metadata events
SlonksImageModel
Holds the model config and SSTORE2 weight chunk pointers. Exposes renderSourcePixels(sourceId) and renderEmbeddingPixels(bytes) for the merged case. Lockable by owner once final.
- ›214 KB packed weights
- ›SSTORE2 chunk pointers
- ›lockModel() makes weights immutable
SlonksMergeManager
Records the mergeLevel and packed cumulative embedding for survivor tokens. Burns the donor through Slonks. Holders approve this contract before merging.
- ›level-match enforced on chain
- ›packs embedding into bytes32
- ›emits ERC-4906 on merge
SlonksRenderer
Builds the SVG, animation_url HTML, attributes, and live slop. Reads from Slonks, the model, the merge manager, and CryptoPunksData. Swappable so presentation can evolve.
- ›renders LLM / Original / Slop
- ›reads canonical Punk attrs
- ›computes slop at read time
CryptoPunksData
Larva Labs' on-chain Punks reference. Slonks pulls 2,304 raw RGBA bytes per Punk and the trait list from this contract — never mirrored, always live.
- ›external dependency
- ›punkImage(uint16) and punkAttributes(uint16)
Slonks.tokenURI(tokenId)
→ SlonksRenderer.tokenURI(tokenId)
sourceId = Slonks.sourceIdFor(tokenId)
mergeLevel = SlonksMergeManager.mergeLevel(tokenId)
mergeEmbedding = SlonksMergeManager.mergeEmbedding(tokenId)
generatedPixels = mergeLevel == 0
? imageModel.renderSourcePixels(sourceId)
: imageModel.renderEmbeddingPixels(mergeEmbedding)
originalRgba = CryptoPunksData.punkImage(uint16(sourceId))
slop = compare(generatedPixels, originalRgba)
assemble JSON { image, animation_url, attributes }the palette is exact.
222 RGBA colors, mirrored from the canonical CryptoPunks set. No nearest-color quantization is used. Index 0 is fully transparent — every Slonk shows the renderer's #28313d background through it.
The Merge Lab previews any blend live, no gas to look. Pick a survivor and tap around.