<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>denim</title>
        <link>https://paragraph.com/@denim</link>
        <description>beep booping around the interwebz</description>
        <lastBuildDate>Wed, 29 Jul 2026 08:07:03 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <image>
            <title>denim</title>
            <url>https://storage.googleapis.com/papyrus_images/aba2dbfc2a448e2aa6e77f2be8dc5da6569f33f3ed9c635035412aff41117e83.jpg</url>
            <link>https://paragraph.com/@denim</link>
        </image>
        <copyright>All rights reserved</copyright>
        <item>
            <title><![CDATA[Nouns and Adding New Traits To Your Collection]]></title>
            <link>https://paragraph.com/@denim/nouns-and-adding-new-traits-to-your-collection</link>
            <guid>PjJ7UHLYQG5peUxHj6H3</guid>
            <pubDate>Thu, 06 Oct 2022 19:49:20 GMT</pubDate>
            <description><![CDATA[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 im...]]></description>
            <content:encoded><![CDATA[<p>On August 8th, 2022 (Noun’s first birthday), 13 new traits were added to the Nouns protocol: 8 heads, 3 accessories, and 2 glasses (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://nouns.wtf/vote/125">https://nouns.wtf/vote/125</a>). Let’s walk through the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/nounsDAO/nouns-monorepo">code</a> that makes this possible.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/768e7f55a51d250d94132704165889ff347396f2edfdfb16bbe0263212f7f2aa.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>The <code>NounsToken</code> contract uses three separate smart contracts to retrieve a given Noun’s art, which all lives on-chain:</p><ul><li><p><code>NounsDescriptor</code>: constructs an image for each Noun using artwork stored in the NounsArt contract</p></li><li><p><code>NounsArt</code>: stores approved color palettes for the collection and the image data for each Nouns trait</p></li><li><p><code>SVGRenderer</code>: takes stored pixel data and returns a corresponding SVG string that can be rendered on a browser</p></li></ul><p>Step-by-step now:</p><ol><li><p>On minting a new Noun, the <code>NounsToken</code> contract generates a unique seed (a pseudo-random collection of integers) that determines the NFT’s attributes</p></li><li><p>To render this Noun, <code>NounsToken</code> calls the <code>tokenURI</code> function in the <code>NounsDescriptor</code> contract which returns the corresponding token’s data</p></li><li><p>The <code>NounsDescriptor</code> retrieves the pixel data associated with a given seed from the <code>NounsArt</code> contract and passes it to the <code>SVGRenderer</code> to get an SVG image for the Noun</p></li></ol><h2 id="h-okay-cool-but-how-do-i-add-a-new-trait-to-the-collection" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Okay cool, but how do I add a new trait to the collection?</h2><p>Adding a trait to the collection means adding pixel data to the <code>NounsArt</code> contract. Let’s consider an example: how would you add a shark head PNG as a new Nouns trait?</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/6470f011a485c304ee950552e82049528e31e7e9d5e027e49545e0de74e4bb4c.png" alt="baby shark doo doo doo doo doo.png" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">baby shark doo doo doo doo doo.png</figcaption></figure><h2 id="h-shark-head-journey" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Shark head journey</h2><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/231c619b23bd986bfce7a9e398e66a110d46335ce0f66a8cc0066f37e781fca8.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><h3 id="h-rle-encoding" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">RLE Encoding</h3><p>First, the shark head image gets encoded using a technique called <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://en.wikipedia.org/wiki/Run-length_encoding">Run-Length-Encoding (RLE)</a>. 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, <code>PNGCollectionEncoder</code>, which lives in the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/nounsDAO/nouns-monorepo/blob/6328887f6cbe9bade0cdaf7ec2d6ed6eacbccfd4/packages/nouns-sdk/src/image/png-collection-encoder.ts#L9">nouns-sdk</a>.</p><p>A quick look at the code reveals that the bulk of the encoding logic lives in an Image class function, <code>toRLE()</code>, which takes in a collection of colors stored in <code>NounsArt</code> as a parameter.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/bd7d36ecb953776798bff4d282f202bca0f9c04c3b9ff6bd331223584add2be0.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>The <code>toRLE</code> function does three things:</p><ol><li><p>Calculates a boundary around the non-transparent part of a trait image</p></li><li><p>Loops through the pixels within this boundary and stores their values in <code>indexes</code>, an array containing the trait’s color data</p></li><li><p>Run-length encodes the data in <code>indexes</code> and returns it as a string of padded hex-encoded values</p></li></ol><p>The GIF below illustrates how the program calculates this image boundary:</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/14be4f3e74f298c03d74542cc0d03e6d5c0ccbe8920342ec914c5499862d8504.gif" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/3d6c138634283c6911d3d07365463a8c6d1dd3116d16f8f91325a37702d40b6a.png" alt="RLE implementation per part C" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">RLE implementation per part C</figcaption></figure><p>The output of <code>toRLE</code> is then encoded further as a bytes array before being officially added to the Nouns protocol.</p><p>To push the trait on-chain, the Nouns DAO must vote to execute an Ethereum transaction that calls the appropriate <code>addTraits</code> function in the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/nounsDAO/nouns-monorepo/blob/6328887f6cbe9bade0cdaf7ec2d6ed6eacbccfd4/packages/nouns-contracts/contracts/NounsDescriptorV2.sol">NounsDescriptor</a> contract (i.e. <code>addHeads</code>, <code>addGlasses</code>, etc) and passes the encoded image as a parameter.</p><p>This function then calls <code>addPage</code> which uses <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/transmissions11/solmate/blob/main/src/utils/SSTORE2.sol">SSTORE2</a> (<em>a solidity library for reading and writing data to persistent storage on-chain)</em> to store the encoded image in the <code>NounsArt</code> contract.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/43a73187669fca089052cc23e57893391dffc32895b1da268e21c7091be212a4.png" alt="The descriptor contract calls addHeads in NounsArt and passes in trait image data" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">The descriptor contract calls addHeads in NounsArt and passes in trait image data</figcaption></figure><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/40f328a55dfb62046cccc60a4e0f25296079bc30a4befc647d6f0cc439195ad7.png" alt="The addHeads function passes an encoded head trait to the " blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">The addHeads function passes an encoded head trait to the</figcaption></figure><p>And… voila! A new shark head can now be assigned to a Noun as a trait.</p><h2 id="h-wrapping-it-up" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Wrapping it up</h2><p>If you want to learn more about Nouns and their plans to add new traits to the collection, check out <code>#noundry</code> in their <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://discord.gg/nouns">discord</a>.</p><p>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 :)</p><p>Huge shoutout to <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/0xjulian_">Julian</a> for edits and thoughts on the walkthrough.</p>]]></content:encoded>
            <author>denim@newsletter.paragraph.com (denim)</author>
            <enclosure url="https://storage.googleapis.com/papyrus_images/ade02200b0c1b18a6104c3d2da046af4693b1e5043081e20484fbcf15eab5933.png" length="0" type="image/png"/>
        </item>
    </channel>
</rss>