On August 8th, 2022 (Noun’s first birthday), 13 new traits were added to the Nouns protocol: 8 heads, 3 accessories, and 2 glasses (https://nouns.wtf/vote/125). Let’s walk through the code that makes this possible.

The NounsToken contract uses three separate smart contracts to retrieve a given Noun’s art, which all lives on-chain:
NounsDescriptor: constructs an image for each Noun using artwork stored in the NounsArt contractNounsArt: stores approved color palettes for the collection and the image data for each Nouns traitSVGRenderer: takes stored pixel data and returns a corresponding SVG string that can be rendered on a browser
Step-by-step now:
On minting a new Noun, the
NounsTokencontract generates a unique seed (a pseudo-random collection of integers) that determines the NFT’s attributesTo render this Noun,
NounsTokencalls thetokenURIfunction in theNounsDescriptorcontract which returns the corresponding token’s dataThe
NounsDescriptorretrieves the pixel data associated with a given seed from theNounsArtcontract and passes it to theSVGRendererto get an SVG image for the Noun
Adding a trait to the collection means adding pixel data to the NounsArt contract. Let’s consider an example: how would you add a shark head PNG as a new Nouns trait?


First, the shark head image gets encoded using a technique called Run-Length-Encoding (RLE). This is done because on-chain data storage is expensive and encoding the image reduces the image’s size, and thus, the cost of storing it too. The trait image is encoded off-chain in a TypeScript class, PNGCollectionEncoder, which lives in the nouns-sdk.
A quick look at the code reveals that the bulk of the encoding logic lives in an Image class function, toRLE(), which takes in a collection of colors stored in NounsArt as a parameter.

The toRLE function does three things:
Calculates a boundary around the non-transparent part of a trait image
Loops through the pixels within this boundary and stores their values in
indexes, an array containing the trait’s color dataRun-length encodes the data in
indexesand returns it as a string of padded hex-encoded values
The GIF below illustrates how the program calculates this image boundary:


The output of toRLE is then encoded further as a bytes array before being officially added to the Nouns protocol.
To push the trait on-chain, the Nouns DAO must vote to execute an Ethereum transaction that calls the appropriate addTraits function in the NounsDescriptor contract (i.e. addHeads, addGlasses, etc) and passes the encoded image as a parameter.
This function then calls addPage which uses SSTORE2 (a solidity library for reading and writing data to persistent storage on-chain) to store the encoded image in the NounsArt contract.


And… voila! A new shark head can now be assigned to a Noun as a trait.
If you want to learn more about Nouns and their plans to add new traits to the collection, check out #noundry in their discord.
I wrote this walkthrough to highlight the affordances that come with storing data on-chain and to help others make sense of the Nouns smart contracts. If you’re working on a project with mechanics for showcasing collective group identities, I would love to chat :)
Huge shoutout to Julian for edits and thoughts on the walkthrough.

