Bitmap encoding
A Normie is a monochrome bitmap at 1 bit per pixel, most significant bit first: bit 7 of the first byte is the top-left pixel. At mint every Normie is 40x40, 200 bytes for 1,600 pixels. After an enlargement a canvas is 50, 60, 70 or 80 per side (313, 450, 613 or 800 bytes), with the original art centred.
Pixel data is stored with SSTORE2, which writes data as contract bytecode rather than storage slots. Each token maps to a single SSTORE2 pointer.
SVG rendering
The renderer contract reads the bitmap and builds the SVG on chain. It scans each row and merges consecutive filled pixels into one run, which keeps the SVG small. The result is #48494b (off-black) on #e3e5e4 (off-white). Each Normie also has an animation_url that draws it on an HTML canvas, crisp at any size.
Minting
Encryption
Image data and traits were XOR-encrypted with a keccak256-derived keystream before being written to NormiesStorage, keyed by a secret revealHash and each token ID. Once the reveal hash was set, the storage contract decrypts on every read.
Signatures
Minting required a backend-signed message over (imageData, traits, minter, maxMints, deadline), an EIP-191 personal signature the minter contract verified on chain, so only pre-approved image data could be stored.
Cold wallets and phases
Both minters accepted delegate.xyz delegation from a cold wallet to a hot one (V2 reads both registries). Minting ran in three time-gated phases: Guaranteed, FCFS and Public, each with per-wallet limits.
Storage
Original bitmaps live in NormiesStorage, encrypted from the blind mint. Edit layers live separately, unencrypted, in NormiesCanvasStorageV2, which also holds wallet and per-Normie pixel balances. Keeping them apart preserves the original art untouched; writing plaintext layers into the encrypted storage would break its decryption.
Compositing
The image is the base bitmap XOR the edit layer:
- A set bit in the layer flips that pixel: filled becomes empty, empty becomes filled.
- Setting the same bit again restores the original pixel.
- For a Zombie the base is the Zombie art, not the human original. For a blank canvas there is no base: every set bit is a painted pixel.
The on-chain SVG is always monochrome. The green and grey in the editor are a frontend reading aid.
Commit and reveal
- Commit:
commitBurn(tokenIds, receiverTokenId)burns the tokens at once and records their pixel counts; the caller must own the receiver.commitBurnToWallet(tokenIds)does the same with the wallet as the receiver. - Wait: 5 blocks, about a minute, for a future block hash to serve as entropy.
- Reveal:
revealBurn(commitId)rolls a percentage for each burned token and credits the pixels to the receiver Normie or the wallet.
The EVM keeps only 256 block hashes, so after about 261 blocks (about 50 minutes) the roll is gone and the minimum tier is paid instead. The reward is never lost, only the roll.
Painting
setTransformBitmap(tokenId, bitmap) takes a full layer sized to the Normie’s canvas. The contract checks that the number of set bits does not exceed the pixels on the Normie and never deducts them: pixels are a ceiling, not a currency.
Delegation
An owner can set one Canvas delegate per token, an address allowed to call setTransformBitmap. A delegation is tied to the owner who set it (delegateSetBy): it lapses when the token is transferred and works again if the token returns. A delegate.xyz delegate gets the same painting right. Neither can move or spend pixels; that takes the owner or an ERC20-style allowance on the storage contract.
Renderer traits
The current renderer adds Canvas traits to every token: Customized, Pixel Count (from the original art, unchanged by edits), Action Points (the on-chain name for the pixels on the Normie), Level, Canvas Size and Blank Canvas.
API
api.normies.art serves everything on chain, read straight from Ethereum mainnet. Token IDs run from 0 to 9,999. The full reference is on the API page, and an llms.txt is there for LLM integrations.
Rate limit: 60 requests per minute per IP (sliding window).
Contract addresses
| Contract | Address |
|---|---|
| Normies | 0x9Eb6E2025B64f340691e424b7fe7022fFDE12438 |
| NormiesStorage | 0x1B976bAf51cF51F0e369C070d47FBc47A706e602 |
| NormiesRenderer | 0xBe57fC4D0c729b8e8d33b638Dd441F57365e4c25 |
| NormiesRendererV2 | 0x7818f24d3239c945510e0a1a523dd9971812c6c0 |
| NormiesRendererV3 | 0x1af01b902256d77cf9499a14ef4e494897380b05 |
| NormiesRendererV4 | 0x8eC46Cc1f306652868a4dfbAAae87CBa2715A0eB |
| NormiesRendererV5 | 0x7c726f02C5e840e1656b522A5C22caaf87C1C35C |
| NormiesRendererV6 | 0xd6747533697878a6a3a29B5cCf815740BA23998D |
| NormiesMinter | 0xC74994dD70FFb621CC514cE18a4F6F52124e296d |
| NormiesMinterV2 | 0xc513272597d3022D77b3d7EEBA92cea5D7fb2808 |
| NormiesCanvas | 0x64951d92e345C50381267380e2975f66810E869c |
| NormiesCanvasStorage | 0xC255BE0983776BAB027a156681b6925cde47B2D1 |
| NormiesCanvasV2 | 0xF14f2852e1fD6A4108156054AF49B3915dc40E2e |
| NormiesCanvasStorageV2 | 0x96F2DA32Bb9D429d59ac13dB469f4950cBe02084 |
| NormiesPixelMarket | 0x86156A8d6e4B9925F7fEca527ea5D71B0deeDB64 |
| NormiesRevenuePool | 0x481384812e79bf0d11FC0b1704af445D5F06813c |
| NormiesRoyaltySplitter | 0xA68A225f62772E6158f2B5f8afC184AdB69c3282 |
| NormiesZombie | 0x18533ad55a54c3847Da06A48b51aD7DcB2551202 |
| NormiesZombieStorage | 0xA331bD22C90D1DA096934Db8bc6b69F0e1491E26 |
| NormiesLegendaryCanvas | 0xfA55f6592522dA74224a67c7D3Fd1DF759c628e8 |
| Adapter8004 | 0xde152AfB7db5373F34876E1499fbD893A82dD336 |
| delegate.xyz V2 | 0x00000000000000447e69651d841bD8D104Bed493 |
| delegate.xyz V1 | 0x00000000000076A84feF008CDAbe6409d2FE638B |
