Nouns is a protocol for proliferating Nouns.

Introduction

Noun protocol is a suite of smart contracts that generates and auctions a Noun every day. The protocol serves as an open standard for generative art - Nouns. Because all of the art was released under the public domain, developers can build without restriction on the protocol at the smart contract or application layer.

This documentation describes the mechanisms that power the Nouns protocol and what each component entails. If you have any questions, please reach out at https://discord.gg/nouns

Smart contract overview

The Noun protocol consists of three primary components: Nouns, auctions and the DAO. The suite of smart contracts gives you full access to the auctions and generative art that powers the protocol.

Contracts

NameAddressEtherscan
NounsToken0x9C8fF314C9Bc7F6e59A9d9225Fb22946427eDC03https://etherscan.io/address/0x9C8fF314C9Bc7F6e59A9d9225Fb22946427eDC03
NounsSeeder0xCC8a0FB5ab3C7132c1b2A0109142Fb112c4Ce515https://etherscan.io/address/0xCC8a0FB5ab3C7132c1b2A0109142Fb112c4Ce515
NounsDescriptor0x0Cfdb3Ba1694c2bb2CFACB0339ad7b1Ae5932B63https://etherscan.io/address/0x0Cfdb3Ba1694c2bb2CFACB0339ad7b1Ae5932B63
NFTDescriptor0x0BBAd8c947210ab6284699605ce2a61780958264https://etherscan.io/address/0x0BBAd8c947210ab6284699605ce2a61780958264
NounsAuctionHouse0xF15a943787014461d94da08aD4040f79Cd7c124ehttps://etherscan.io/address/0xF15a943787014461d94da08aD4040f79Cd7c124e
NounsNounsAuctionHouseProxy0x830BD73E4184ceF73443C15111a1DF14e495C706https://etherscan.io/address/0x830bd73e4184cef73443c15111a1df14e495c706
NounsAuctionHouseProxyAdmin0xC1C119932d78aB9080862C5fcb964029f086401ehttps://etherscan.io/address/0xC1C119932d78aB9080862C5fcb964029f086401e
NounsDAOExecutor0x0BC3807Ec262cB779b38D65b38158acC3bfedE10https://etherscan.io/address/0x0BC3807Ec262cB779b38D65b38158acC3bfedE10
NounsDAOProxy0x6f3E6272A167e8AcCb32072d08E0957F9c79223dhttps://etherscan.io/address/0x0BC3807Ec262cB779b38D65b38158acC3bfedE10
NounsDAOLogicV10xa43aFE317985726E4e194eb061Af77fbCb43F944https://etherscan.io/address/0x0BC3807Ec262cB779b38D65b38158acC3bfedE10
➡️
To view the source code, visit the Github repository. You can also review the architecture here.

Auction lifecycle

Every auction begins where the last one ends. On settlement of an auction, a new Noun is minted and the next auction is created, all in the same transaction.

Timing Diagram

notion image
 

Settlement

To settle an auction, the NounsAuctionHouse contract is called. The function settleCurrentAndCreateNewAuction() both settles and creates a new auction:

function settleCurrentAndCreateNewAuction() external override nonReentrant whenNotPaused {
    _settleAuction();
    _createAuction();
}

The _settleAuction() function checks that the current auction has begun, that it has not been settled previously and that the auction time has ended:

function _settleAuction() internal {
    INounsAuctionHouse.Auction memory _auction = auction;
	
    require(_auction.startTime != 0, "Auction hasn't begun");
    require(!_auction.settled, 'Auction has already been settled');
    require(block.timestamp >= _auction.endTime, "Auction hasn't completed");

    auction.settled = true;
    ....
}

If settlement is valid, the Noun is transferred from the NounAuctionHouse to the winning bidder's address:

nouns.transferFrom(address(this), _auction.bidder, _auction.nounId);

In the case that there are no bids, the noun will be burned by transferring the token to the zero address:

_burn(nounId);

Creating a new auction

Creating a new auction happens on the same transaction as the settlement of the previous auction. From the NounsAuctionHouse contract, _createAuction() calls the NounsToken contract to mint a new Noun (which transfers it into NounsAuctionHouse for escrow) and then uses the newly minted nounId to create a new Auction .

function _createAuction() internal {
    try nouns.mint() returns (uint256 nounId) {
        uint256 startTime = block.timestamp;
	uint256 endTime = startTime + duration;
	
	auction = Auction({
		nounId: nounId,
		amount: 0,
		startTime: startTime,
		endTime: endTime,
		bidder: payable(0),
		settled: false
	});

	emit AuctionCreated(nounId, startTime, endTime);

    } catch Error(string memory) {
	_pause();
    }
}

The now active auction is stored in storage and available for anyone to view.

contract NounsAuctionHouse {
    ...
    // The active auction
    INounsAuctionHouse.Auction public auction;
    ...
}

The Auction has the following properties:

struct Auction {
    // ID for the Noun (ERC721 token ID)
    uint256 nounId;
    // The current highest bid amount
    uint256 amount;
    // The time that the auction started
    uint256 startTime;
    // The time that the auction is scheduled to end
    uint256 endTime;
    // The address of the current highest bid
    address payable bidder;
    // Whether or not the auction has been settled
    bool settled;
}
🚧
Try it out! Head over the the verified NounsAuctionHouseProxy contract on Etherscan and click on auction to see the current live auction!

Creating bids

A bid can be created by anyone so long as three requirements are met:

  • Valid auction: the Noun the bid is being created for is up for auction in the current auction.
  • Active auction: the current auction has not ended.
  • Minimum bid: the amount of ETH sent in the transaction is greater than the minimum bid.
function createBid(uint256 nounId) external payable override nonReentrant {
    INounsAuctionHouse.Auction memory _auction = auction;

    require(_auction.nounId == nounId, 'Noun not up for auction');
    require(block.timestamp < _auction.endTime, 'Auction expired');
    require(msg.value >= reservePrice, 'Must send at least reservePrice');
    require(
	msg.value >= _auction.amount + ((_auction.amount * minBidIncrementPercentage) / 100),
	'Must send more than last bid by minBidIncrementPercentage amount'
    );
    ...
}
💡
The minimum bid is determined by the last bid's amount plus a minimum bid increment percentage which is set by the NounsDAO. If the current bid is 10 ETH and the minBidIncrementPercentage is 2, the minimum bid will be 10 * 1.02 or 10.2 ETH.

If the bid is valid, the last bidder's bid amount will be returned:

    ...
    address payable lastBidder = _auction.bidder;

    // Refund the last bidder, if applicable
    if (lastBidder != address(0)) {
	_safeTransferETHWithFallback(lastBidder, _auction.amount);
    }
    ...

If the bid is placed within a certain time frame before the auction's end time, the auction's end time will be extended. This time frame is the timeBuffer property on the NounsAuctionHouse contract which can be modified by the NounsDAO.

// The minimum amount of time left in an auction after a new bid is created
uint256 public timeBuffer;

Generative on-chain art

All Nouns art work lives on chain - we do not utilize pointers to other networks such as IPFS. This is possible because Noun parts are compressed and stored on-chain using a custom run-length encoding (RLE), which is a form of lossless compression.

While the NounsToken contract handles the minting of Nouns, the NounsSeeder and NounsDescriptor contracts handle the determination of Noun attributes and rendering of the artwork, accordingly.

Noun attributes

All Noun's attributes are determined by a Noun Seed :

struct Seed {
    uint48 background;
    uint48 body;
    uint48 accessory;
    uint48 head;
    uint48 glasses;
}

The Seed is a collection of part indexes that can be randomly generated using the NounsSeeder contract by calling the generateSeed function below. The pseudo-random number used to determine noun parts is computed by hashing the previous blockhash and passed in nounId. Seed generation is not truly random - it can be predicted if you know the previous block's blockhash .

The second argument is the address of the INounsDescriptor or the contract in charge of storing and rendering the art work (more on that below).

function generateSeed(uint256 nounId, INounsDescriptor descriptor) external view override returns (Seed memory) {
    uint256 pseudorandomness = uint256(
        keccak256(abi.encodePacked(blockhash(block.number - 1), nounId))
    );

    uint256 backgroundCount = descriptor.backgroundCount();
    uint256 bodyCount = descriptor.bodyCount();
    uint256 accessoryCount = descriptor.accessoryCount();
    uint256 headCount = descriptor.headCount();
    uint256 glassesCount = descriptor.glassesCount();

    return Seed({
	background: uint48(
	    uint48(pseudorandomness) % backgroundCount
	),
	body: uint48(
	    uint48(pseudorandomness >> 48) % bodyCount
	),
	accessory: uint48(
	    uint48(pseudorandomness >> 96) % accessoryCount
	),
	head: uint48(
	    uint48(pseudorandomness >> 144) % headCount
	),
	glasses: uint48(
	    uint48(pseudorandomness >> 192) % glassesCount
	)
    });
}
💡
You can also create your own Seed directly if you know which parts you want!
🚧
Try it yourself! You can view the seed of any Noun through the NounsToken.seed property. Head over to the verified NounsToken contract on Etherscan and click on #26 (the seeds property). Input any noun id and you'll get the seed that determines its traits!

On-chain art

Encoding

Noun 'parts' are compressed using run-length encoding and stored in the NounsDescriptor contract.

💡
Run-length encoding (RLE) is a very simple form of data compression in which a stream of data is given as the input (i.e. "AAABBCCCC") and the output is a sequence of counts of consecutive data values in a row (i.e. "3A2B4C").

Consecutive, same-color pixels in each row of the image are grouped together. These compressed pixel representations are then drawn as SVG rectangles to form a Noun.

The algorithm used by Nouns is optimized for the creation of composite images, where the majority of the pixels in each input image are transparent. This is accomplished by defining a bounding box around the content in each image and only encoding the pixels inside the box.

Let's use this body as an example:

notion image

The majority of this 32x32 pixel grid is empty and therefore doesn't need to be drawn. Rather than include all transparent pixels in our runs, we'll define a bounding box that excludes all pixels outside the actual content:

notion image

For the above image, the bounding box consists of the following values:

  • 21 (top)
  • 23 (right)
  • 31 (bottom)
  • 9 (left)

When drawing Nouns, we start at the top-left of the bounding box and draw each row from top to bottom. We need two pieces of data to draw every SVG rectangle:

  1. The length in pixels
  1. The color

To save space, each color is represented as a number, which gives us enough information to look up the hex color value. In this example, assume the number representation of orange is 17 and transparent is 0.

With that assumption, the encoding is as follows:

First row:

14 (rectangle length) | 17 (color index)

Entire body:

14|17 14|17 14|17 14|17 02|17 01|00 11|17 02|17 01|00 11|17 02|17 01|00 11|17 02|17 01|00 11|17 02|17 01|00 11|17 02|17 01|00 11|17 02|17 01|00 11|17

As a final step, the bounds and pixel data are concatenated, and a 'color palette index' is prepended to the string. This index is a number that tells the renderer where to access the colors for the part. This allows us to support a large number of possible colors, while using as little storage as possible. The entire string is then hex-encoded.

That's it! The hex-encoded data for all Noun parts can be found in the image-data.json file inside the Nouns repository.

Rendering

In this context, 'rendering' means converting the run-length encoded Noun parts to an SVG image that can be viewed on any computer. This conversion is 100% on-chain and occurs in the MultiPartRLEToSVG library.

The process is simple - loop over selected parts, parse the image data, and draw all SVG rectangles.

To draw each part, we start at the top-left of the bounding box and loop through all rects, concatenating at several depths to reduce gas usage. It's easiest to explain this process by adding comments in the source code of the _generateSVGRects function:

// This is a lookup table that enables very cheap int to string
// conversions when operating on a set of predefined integers.
// This is used below to convert the integer length of each rectangle
// in a 32x32 pixel grid to the string representation of the length
// in a 320x320 pixel grid, which is the chosen dimension for all Nouns.
// For example: A length of 3 gets mapped to '30'.
string[33] memory lookup = [
    '0', '10', '20', '30', '40', '50', '60', '70', 
    '80', '90', '100', '110', '120', '130', '140', '150', 
    '160', '170', '180', '190', '200', '210', '220', '230', 
    '240', '250', '260', '270', '280', '290', '300', '310',
    '320' 
];

// The string of SVG rectangles
string memory rects;

// Loop through all Noun parts
for (uint8 p = 0; p < params.parts.length; p++) {
    // Convert the part data into a format that's easier to consume
    // than a byte array.
    DecodedImage memory image = _decodeRLEImage(params.parts[p]);

    // Get the color palette used by the current part (`params.parts[p]`)
    string[] storage palette = palettes[image.paletteIndex];

    // These are the x and y coordinates of the rect that's currently being drawn.
    // We start at the top-left of the pixel grid when drawing a new part.
    uint256 currentX = image.bounds.left;
    uint256 currentY = image.bounds.top;

    // The `cursor` and `buffer` are used here as a gas-saving technique.
    // We load enough data into a string array to draw four rectangles.
    // Once the string array is full, we call `_getChunk`, which writes the
    // four rectangles to a `chunk` variable before concatenating them with the
    // existing part string. If there is remaining, unwritten data inside the
    // `buffer` after we exit the rect loop, it will be written before the
    // part rectangles are merged with the existing part data.
    // This saves gas by reducing the size of the strings we're concatenating
    // during most loops.
    uint256 cursor;
    string[16] memory buffer;

    // The part rectangles
    string memory part;
    for (uint256 i = 0; i < image.rects.length; i++) {
        Rect memory rect = image.rects[i];

				// Skip fully transparent rectangles. Transparent rectangles
				// always have a color index of 0.
        if (rect.colorIndex != 0) {
				    // Load the rectangle data into the buffer
            buffer[cursor] = lookup[rect.length];          // width
            buffer[cursor + 1] = lookup[currentX];         // x
            buffer[cursor + 2] = lookup[currentY];         // y
            buffer[cursor + 3] = palette[rect.colorIndex]; // color
            cursor += 4;
            if (cursor >= 16) {
								// Write the rectangles from the buffer to a string
								// and concatenate with the existing part string. 
                part = string(abi.encodePacked(part, _getChunk(cursor, buffer)));
                cursor = 0;
            }
        }
				// Move the x coordinate `rect.length` pixels to the right
        currentX += rect.length;
				// If the right bound has been reached, reset the x coordinate
				// to the left bound and shift the y coordinate down one row.
        if (currentX == image.bounds.right) {
            currentX = image.bounds.left;
            currentY++;
        }
    }
    // If there are unwritten rectangles in the buffer, write them to a
    // `chunk` and concatenate with the existing part data.
    if (cursor != 0) {
        part = string(abi.encodePacked(part, _getChunk(cursor, buffer)));
    }
    // Concatenate the part with all previous parts
    rects = string(abi.encodePacked(rects, part));
}
return rects;
The rendering process, visualized.
The rendering process, visualized.

Accessing Image Data

Once the entire SVG has been generated, it is base64 encoded. The token URI, which contains the inlined token name, description, and image is also base64 encoded. This allows us to encode characters that aren't allowed as part of a URI.

The tokenURI function on the NounsDescriptor accepts two arguments: tokenId and seed. The NounsToken contract calls this function using seed information from its storage to get the corresponding token data:

function tokenURI(uint256 tokenId, INounsSeeder.Seed memory seed) external view returns (string memory);

A second function is exposed that allows you to generate the SVG image for any possible Noun using an arbitrary seed:

function generateSVGImage(INounsSeeder.Seed memory seed) external view returns (string memory);

A successful response will return a base64 encoded string representation of the SVG image, which looks like the following:

PHN2ZyB3aWR0aD0iMzIwIiBoZWlnaHQ9IjMyMCIgdmlld0JveD0iMCAwIDMyMCAzMjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgc2hhcGUtcmVuZGVyaW5nPSJjcmlzcEVkZ2VzIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZDVkN2UxIiAvPjxyZWN0IHdpZHRoPSIxNDAiIGhlaWdodD0iMTAiIHg9IjkwIiB5PSIyMTAiIGZpbGw9IiM2M2EwZjkiIC8+PHJlY3Qgd2lkdGg9IjE0MCIgaGVpZ2h0PSIxMCIgeD0iOTAiIHk9IjIyMCIgZmlsbD0iIzYzYTBmOSIgLz48cmVjdCB3aWR0aD0iMTQwIiBoZWlnaHQ9IjEwIiB4PSI5MCIgeT0iMjMwIiBmaWxsPSIjNjNhMGY5IiAvPjxyZWN0IHdpZHRoPSIxNDAiIGhlaWdodD0iMTAiIHg9IjkwIiB5PSIyNDAiIGZpbGw9IiM2M2EwZjkiIC8+PHJlY3Qgd2lkdGg9IjIwIiBoZWlnaHQ9IjEwIiB4PSI5MCIgeT0iMjUwIiBmaWxsPSIjNjNhMGY5IiAvPjxyZWN0IHdpZHRoPSIxMTAiIGhlaWdodD0iMTAiIHg9IjEyMCIgeT0iMjUwIiBmaWxsPSIjNjNhMGY5IiAvPjxyZWN0IHdpZHRoPSIyMCIgaGVpZ2h0PSIxMCIgeD0iOTAiIHk9IjI2MCIgZmlsbD0iIzYzYTBmOSIgLz48cmVjdCB3aWR0aD0iMTEwIiBoZWlnaHQ9IjEwIiB4PSIxMjAiIHk9IjI2MCIgZmlsbD0iIzYzYTBmOSIgLz48cmVjdCB3aWR0aD0iMjAiIGhlaWdodD0iMTAiIHg9IjkwIiB5PSIyNzAiIGZpbGw9IiM2M2EwZjkiIC8+PHJlY3Qgd2lkdGg9IjExMCIgaGVpZ2h0PSIxMCIgeD0iMTIwIiB5PSIyNzAiIGZpbGw9IiM2M2EwZjkiIC8+PHJlY3Qgd2lkdGg9IjIwIiBoZWlnaHQ9IjEwIiB4PSI5MCIgeT0iMjgwIiBmaWxsPSIjNjNhMGY5IiAvPjxyZWN0IHdpZHRoPSIxMTAiIGhlaWdodD0iMTAiIHg9IjEyMCIgeT0iMjgwIiBmaWxsPSIjNjNhMGY5IiAvPjxyZWN0IHdpZHRoPSIyMCIgaGVpZ2h0PSIxMCIgeD0iOTAiIHk9IjI5MCIgZmlsbD0iIzYzYTBmOSIgLz48cmVjdCB3aWR0aD0iMTEwIiBoZWlnaHQ9IjEwIiB4PSIxMjAiIHk9IjI5MCIgZmlsbD0iIzYzYTBmOSIgLz48cmVjdCB3aWR0aD0iMjAiIGhlaWdodD0iMTAiIHg9IjkwIiB5PSIzMDAiIGZpbGw9IiM2M2EwZjkiIC8+PHJlY3Qgd2lkdGg9IjExMCIgaGVpZ2h0PSIxMCIgeD0iMTIwIiB5PSIzMDAiIGZpbGw9IiM2M2EwZjkiIC8+PHJlY3Qgd2lkdGg9IjIwIiBoZWlnaHQ9IjEwIiB4PSI5MCIgeT0iMzEwIiBmaWxsPSIjNjNhMGY5IiAvPjxyZWN0IHdpZHRoPSIxMTAiIGhlaWdodD0iMTAiIHg9IjEyMCIgeT0iMzEwIiBmaWxsPSIjNjNhMGY5IiAvPjxyZWN0IHdpZHRoPSIyMCIgaGVpZ2h0PSIxMCIgeD0iMTQwIiB5PSIyNDAiIGZpbGw9IiMwMDAwMDAiIC8+PHJlY3Qgd2lkdGg9IjIwIiBoZWlnaHQ9IjEwIiB4PSIxODAiIHk9IjI0MCIgZmlsbD0iIzAwMDAwMCIgLz48cmVjdCB3aWR0aD0iMTAiIGhlaWdodD0iMTAiIHg9IjEzMCIgeT0iMjUwIiBmaWxsPSIjMDAwMDAwIiAvPjxyZWN0IHdpZHRoPSIyMCIgaGVpZ2h0PSIxMCIgeD0iMTYwIiB5PSIyNTAiIGZpbGw9IiMwMDAwMDAiIC8+PHJlY3Qgd2lkdGg9IjEwIiBoZWlnaHQ9IjEwIiB4PSIyMDAiIHk9IjI1MCIgZmlsbD0iIzAwMDAwMCIgLz48cmVjdCB3aWR0aD0iMjAiIGhlaWdodD0iMTAiIHg9IjE2MCIgeT0iMjYwIiBmaWxsPSIjMDAwMDAwIiAvPjxyZWN0IHdpZHRoPSIxMDAiIGhlaWdodD0iMTAiIHg9IjUwIiB5PSI2MCIgZmlsbD0iIzJiYjI2YiIgLz48cmVjdCB3aWR0aD0iMTEwIiBoZWlnaHQ9IjEwIiB4PSIxNjAiIHk9IjYwIiBmaWxsPSIjMmJiMjZiIiAvPjxyZWN0IHdpZHRoPSIxMCIgaGVpZ2h0PSIxMCIgeD0iNTAiIHk9IjcwIiBmaWxsPSIjMmJiMjZiIiAvPjxyZWN0IHdpZHRoPSIxMCIgaGVpZ2h0PSIxMCIgeD0iMTcwIiB5PSI3MCIgZmlsbD0iIzJiYjI2YiIgLz48cmVjdCB3aWR0aD0iMTAiIGhlaWdodD0iMTAiIHg9IjIyMCIgeT0iNzAiIGZpbGw9IiMyYmIyNmIiIC8+PHJlY3Qgd2lkdGg9IjEwIiBoZWlnaHQ9IjEwIiB4PSIyNjAiIHk9IjcwIiBmaWxsPSIjMmJiMjZiIiAvPjxyZWN0IHdpZHRoPSIxMCIgaGVpZ2h0PSIxMCIgeD0iNTAiIHk9IjgwIiBmaWxsPSIjMmJiMjZiIiAvPjxyZWN0IHdpZHRoPSIxMTAiIGhlaWdodD0iMTAiIHg9IjcwIiB5PSI4MCIgZmlsbD0iIzJiYjI2YiIgLz48cmVjdCB3aWR0aD0iMTAiIGhlaWdodD0iMTAiIHg9IjE5MCIgeT0iODAiIGZpbGw9IiMyYmIyNmIiIC8+PHJlY3Qgd2lkdGg9IjQwIiBoZWlnaHQ9IjEwIiB4PSIyMTAiIHk9IjgwIiBmaWxsPSIjMmJiMjZiIiAvPjxyZWN0IHdpZHRoPSIxMCIgaGVpZ2h0PSIxMCIgeD0iMjYwIiB5PSI4MCIgZmlsbD0iIzJiYjI2YiIgLz48cmVjdCB3aWR0aD0iMTAiIGhlaWdodD0iMTAiIHg9IjUwIiB5PSI5MCIgZmlsbD0iIzJiYjI2YiIgLz48cmVjdCB3aWR0aD0iMTAiIGhlaWdodD0iMTAiIHg9IjcwIiB5PSI5MCIgZmlsbD0iIzJiYjI2YiIgLz48cmVjdCB3aWR0aD0iMTAiIGhlaWdodD0iMTAiIHg9IjExMCIgeT0iOTAiIGZpbGw9IiMyYmIyNmIiIC8+PHJlY3Qgd2lkdGg9IjEwIiBoZWlnaHQ9IjEwIiB4PSIxOTAiIHk9IjkwIiBmaWxsPSIjMmJiMjZiIiAvPjxyZWN0IHdpZHRoPSIxMCIgaGVpZ2h0PSIxMCIgeD0iMjQwIiB5PSI5MCIgZmlsbD0iIzJiYjI2YiIgLz48cmVjdCB3aWR0aD0iMTAiIGhlaWdodD0iMTAiIHg9IjI2MCIgeT0iOTAiIGZpbGw9IiMyYmIyNmIiIC8+PHJlY3Qgd2lkdGg9IjEwIiBoZWlnaHQ9IjEwIiB4PSI1MCIgeT0iMTAwIiBmaWxsPSIjMmJiMjZiIiAvPjxyZWN0IHdpZHRoPSIxMCIgaGVpZ2h0PSIxMCIgeD0iOTAiIHk9IjEwMCIgZmlsbD0iIzJiYjI2YiIgLz48cmVjdCB3aWR0aD0iNzAiIGhlaWdodD0iMTAiIHg9IjEzMCIgeT0iMTAwIiBmaWxsPSIjMmJiMjZiIiAvPjxyZWN0IHdpZHRoPSI0MCIgaGVpZ2h0PSIxMCIgeD0iMjEwIiB5PSIxMDAiIGZpbGw9IiMyYmIyNmIiIC8+PHJlY3Qgd2lkdGg9IjEwIiBoZWlnaHQ9IjEwIiB4PSIyNjAiIHk9IjEwMCIgZmlsbD0iIzJiYjI2YiIgLz48cmVjdCB3aWR0aD0iNjAiIGhlaWdodD0iMTAiIHg9IjUwIiB5PSIxMTAiIGZpbGw9IiMyYmIyNmIiIC8+PHJlY3Qgd2lkdGg9IjIwIiBoZWlnaHQ9IjEwIiB4PSIxMjAiIHk9IjExMCIgZmlsbD0iIzJiYjI2YiIgLz48cmVjdCB3aWR0aD0iMTAiIGhlaWdodD0iMTAiIHg9IjE3MCIgeT0iMTEwIiBmaWxsPSIjMmJiMjZiIiAvPjxyZWN0IHdpZHRoPSIxMCIgaGVpZ2h0PSIxMCIgeD0iMjYwIiB5PSIxMTAiIGZpbGw9IiMyYmIyNmIiIC8+PHJlY3Qgd2lkdGg9IjEwIiBoZWlnaHQ9IjEwIiB4PSI1MCIgeT0iMTIwIiBmaWxsPSIjMmJiMjZiIiAvPjxyZWN0IHdpZHRoPSIxMCIgaGVpZ2h0PSIxMCIgeD0iNzAiIHk9IjEyMCIgZmlsbD0iIzJiYjI2YiIgLz48cmVjdCB3aWR0aD0iMTAiIGhlaWdodD0iMTAiIHg9IjEzMCIgeT0iMTIwIiBmaWxsPSIjMmJiMjZiIiAvPjxyZWN0IHdpZHRoPSIxMCIgaGVpZ2h0PSIxMCIgeD0iMTUwIiB5PSIxMjAiIGZpbGw9IiMyYmIyNmIiIC8+PHJlY3Qgd2lkdGg9IjEwIiBoZWlnaHQ9IjEwIiB4PSIxNzAiIHk9IjEyMCIgZmlsbD0iIzJiYjI2YiIgLz48cmVjdCB3aWR0aD0iNjAiIGhlaWdodD0iMTAiIHg9IjE5MCIgeT0iMTIwIiBmaWxsPSIjMmJiMjZiIiAvPjxyZWN0IHdpZHRoPSIxMCIgaGVpZ2h0PSIxMCIgeD0iMjYwIiB5PSIxMjAiIGZpbGw9IiMyYmIyNmIiIC8+PHJlY3Qgd2lkdGg9IjEwIiBoZWlnaHQ9IjEwIiB4PSI1MCIgeT0iMTMwIiBmaWxsPSIjMmJiMjZiIiAvPjxyZWN0IHdpZHRoPSI1MCIgaGVpZ2h0PSIxMCIgeD0iNzAiIHk9IjEzMCIgZmlsbD0iIzJiYjI2YiIgLz48cmVjdCB3aWR0aD0iMTAiIGhlaWdodD0iMTAiIHg9IjEzMCIgeT0iMTMwIiBmaWxsPSIjMmJiMjZiIiAvPjxyZWN0IHdpZHRoPSIzMCIgaGVpZ2h0PSIxMCIgeD0iMTUwIiB5PSIxMzAiIGZpbGw9IiMyYmIyNmIiIC8+PHJlY3Qgd2lkdGg9IjEwIiBoZWlnaHQ9IjEwIiB4PSIyNjAiIHk9IjEzMCIgZmlsbD0iIzJiYjI2YiIgLz48cmVjdCB3aWR0aD0iMTAiIGhlaWdodD0iMTAiIHg9IjUwIiB5PSIxNDAiIGZpbGw9IiMyYmIyNmIiIC8+PHJlY3Qgd2lkdGg9IjEwIiBoZWlnaHQ9IjEwIiB4PSIxMTAiIHk9IjE0MCIgZmlsbD0iIzJiYjI2YiIgLz48cmVjdCB3aWR0aD0iMTAiIGhlaWdodD0iMTAiIHg9IjE3MCIgeT0iMTQwIiBmaWxsPSIjMmJiMjZiIiAvPjxyZWN0IHdpZHRoPSI4MCIgaGVpZ2h0PSIxMCIgeD0iMTkwIiB5PSIxNDAiIGZpbGw9IiMyYmIyNmIiIC8+PHJlY3Qgd2lkdGg9IjEwIiBoZWlnaHQ9IjEwIiB4PSI1MCIgeT0iMTUwIiBmaWxsPSIjMmJiMjZiIiAvPjxyZWN0IHdpZHRoPSIzMCIgaGVpZ2h0PSIxMCIgeD0iNzAiIHk9IjE1MCIgZmlsbD0iIzJiYjI2YiIgLz48cmVjdCB3aWR0aD0iMzAiIGhlaWdodD0iMTAiIHg9IjExMCIgeT0iMTUwIiBmaWxsPSIjMmJiMjZiIiAvPjxyZWN0IHdpZHRoPSIxMCIgaGVpZ2h0PSIxMCIgeD0iMTUwIiB5PSIxNTAiIGZpbGw9IiMyYmIyNmIiIC8+PHJlY3Qgd2lkdGg9IjEwIiBoZWlnaHQ9IjEwIiB4PSIyNDAiIHk9IjE1MCIgZmlsbD0iIzJiYjI2YiIgLz48cmVjdCB3aWR0aD0iMTAiIGhlaWdodD0iMTAiIHg9IjI2MCIgeT0iMTUwIiBmaWxsPSIjMmJiMjZiIiAvPjxyZWN0IHdpZHRoPSIxMCIgaGVpZ2h0PSIxMCIgeD0iNTAiIHk9IjE2MCIgZmlsbD0iIzJiYjI2YiIgLz48cmVjdCB3aWR0aD0iMTAiIGhlaWdodD0iMTAiIHg9IjkwIiB5PSIxNjAiIGZpbGw9IiMyYmIyNmIiIC8+PHJlY3Qgd2lkdGg9IjEwIiBoZWlnaHQ9IjEwIiB4PSIxMTAiIHk9IjE2MCIgZmlsbD0iIzJiYjI2YiIgLz48cmVjdCB3aWR0aD0iMTAiIGhlaWdodD0iMTAiIHg9IjEzMCIgeT0iMTYwIiBmaWxsPSIjMmJiMjZiIiAvPjxyZWN0IHdpZHRoPSI4MCIgaGVpZ2h0PSIxMCIgeD0iMTUwIiB5PSIxNjAiIGZpbGw9IiMyYmIyNmIiIC8+PHJlY3Qgd2lkdGg9IjEwIiBoZWlnaHQ9IjEwIiB4PSIyNDAiIHk9IjE2MCIgZmlsbD0iIzJiYjI2YiIgLz48cmVjdCB3aWR0aD0iMTAiIGhlaWdodD0iMTAiIHg9IjI2MCIgeT0iMTYwIiBmaWxsPSIjMmJiMjZiIiAvPjxyZWN0IHdpZHRoPSIzMCIgaGVpZ2h0PSIxMCIgeD0iNTAiIHk9IjE3MCIgZmlsbD0iIzJiYjI2YiIgLz48cmVjdCB3aWR0aD0iMTAiIGhlaWdodD0iMTAiIHg9IjkwIiB5PSIxNzAiIGZpbGw9IiMyYmIyNmIiIC8+PHJlY3Qgd2lkdGg9IjEwIiBoZWlnaHQ9IjEwIiB4PSIyNjAiIHk9IjE3MCIgZmlsbD0iIzJiYjI2YiIgLz48cmVjdCB3aWR0aD0iMTAiIGhlaWdodD0iMTAiIHg9IjUwIiB5PSIxODAiIGZpbGw9IiMyYmIyNmIiIC8+PHJlY3Qgd2lkdGg9IjEwIiBoZWlnaHQ9IjEwIiB4PSI3MCIgeT0iMTgwIiBmaWxsPSIjMmJiMjZiIiAvPjxyZWN0IHdpZHRoPSI0MCIgaGVpZ2h0PSIxMCIgeD0iOTAiIHk9IjE4MCIgZmlsbD0iIzJiYjI2YiIgLz48cmVjdCB3aWR0aD0iNzAiIGhlaWdodD0iMTAiIHg9IjE0MCIgeT0iMTgwIiBmaWxsPSIjMmJiMjZiIiAvPjxyZWN0IHdpZHRoPSI1MCIgaGVpZ2h0PSIxMCIgeD0iMjIwIiB5PSIxODAiIGZpbGw9IiMyYmIyNmIiIC8+PHJlY3Qgd2lkdGg9IjEwIiBoZWlnaHQ9IjEwIiB4PSI1MCIgeT0iMTkwIiBmaWxsPSIjMmJiMjZiIiAvPjxyZWN0IHdpZHRoPSIxMCIgaGVpZ2h0PSIxMCIgeD0iOTAiIHk9IjE5MCIgZmlsbD0iIzJiYjI2YiIgLz48cmVjdCB3aWR0aD0iMTAiIGhlaWdodD0iMTAiIHg9IjE4MCIgeT0iMTkwIiBmaWxsPSIjMmJiMjZiIiAvPjxyZWN0IHdpZHRoPSIxMCIgaGVpZ2h0PSIxMCIgeD0iMjYwIiB5PSIxOTAiIGZpbGw9IiMyYmIyNmIiIC8+PHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxMCIgeD0iNTAiIHk9IjIwMCIgZmlsbD0iIzJiYjI2YiIgLz48cmVjdCB3aWR0aD0iMTAiIGhlaWdodD0iMTAiIHg9IjI2MCIgeT0iMjAwIiBmaWxsPSIjMmJiMjZiIiAvPjxyZWN0IHdpZHRoPSI2MCIgaGVpZ2h0PSIxMCIgeD0iMTAwIiB5PSIxMTAiIGZpbGw9IiMwMDAwMDAiIC8+PHJlY3Qgd2lkdGg9IjYwIiBoZWlnaHQ9IjEwIiB4PSIxNzAiIHk9IjExMCIgZmlsbD0iIzAwMDAwMCIgLz48cmVjdCB3aWR0aD0iMzAiIGhlaWdodD0iMTAiIHg9IjEwMCIgeT0iMTIwIiBmaWxsPSIjMDAwMDAwIiAvPjxyZWN0IHdpZHRoPSIxMCIgaGVpZ2h0PSIxMCIgeD0iMTMwIiB5PSIxMjAiIGZpbGw9IiNmZjBlMGUiIC8+PHJlY3Qgd2lkdGg9IjIwIiBoZWlnaHQ9IjEwIiB4PSIxNDAiIHk9IjEyMCIgZmlsbD0iIzAwMDAwMCIgLz48cmVjdCB3aWR0aD0iMzAiIGhlaWdodD0iMTAiIHg9IjE3MCIgeT0iMTIwIiBmaWxsPSIjMDAwMDAwIiAvPjxyZWN0IHdpZHRoPSIxMCIgaGVpZ2h0PSIxMCIgeD0iMjAwIiB5PSIxMjAiIGZpbGw9IiNmZjBlMGUiIC8+PHJlY3Qgd2lkdGg9IjIwIiBoZWlnaHQ9IjEwIiB4PSIyMTAiIHk9IjEyMCIgZmlsbD0iIzAwMDAwMCIgLz48cmVjdCB3aWR0aD0iMTYwIiBoZWlnaHQ9IjEwIiB4PSI3MCIgeT0iMTMwIiBmaWxsPSIjMDAwMDAwIiAvPjxyZWN0IHdpZHRoPSIxMCIgaGVpZ2h0PSIxMCIgeD0iNzAiIHk9IjE0MCIgZmlsbD0iIzAwMDAwMCIgLz48cmVjdCB3aWR0aD0iMTAiIGhlaWdodD0iMTAiIHg9IjEwMCIgeT0iMTQwIiBmaWxsPSIjMDAwMDAwIiAvPjxyZWN0IHdpZHRoPSIxMCIgaGVpZ2h0PSIxMCIgeD0iMTEwIiB5PSIxNDAiIGZpbGw9IiMwYWRjNGQiIC8+PHJlY3Qgd2lkdGg9IjIwIiBoZWlnaHQ9IjEwIiB4PSIxMjAiIHk9IjE0MCIgZmlsbD0iIzAwMDAwMCIgLz48cmVjdCB3aWR0aD0iMTAiIGhlaWdodD0iMTAiIHg9IjE0MCIgeT0iMTQwIiBmaWxsPSIjMTkyOWY0IiAvPjxyZWN0IHdpZHRoPSIxMCIgaGVpZ2h0PSIxMCIgeD0iMTUwIiB5PSIxNDAiIGZpbGw9IiMwMDAwMDAiIC8+PHJlY3Qgd2lkdGg9IjEwIiBoZWlnaHQ9IjEwIiB4PSIxNzAiIHk9IjE0MCIgZmlsbD0iIzAwMDAwMCIgLz48cmVjdCB3aWR0aD0iMTAiIGhlaWdodD0iMTAiIHg9IjE4MCIgeT0iMTQwIiBmaWxsPSIjMGFkYzRkIiAvPjxyZWN0IHdpZHRoPSIyMCIgaGVpZ2h0PSIxMCIgeD0iMTkwIiB5PSIxNDAiIGZpbGw9IiMwMDAwMDAiIC8+PHJlY3Qgd2lkdGg9IjEwIiBoZWlnaHQ9IjEwIiB4PSIyMTAiIHk9IjE0MCIgZmlsbD0iIzE5MjlmNCIgLz48cmVjdCB3aWR0aD0iMTAiIGhlaWdodD0iMTAiIHg9IjIyMCIgeT0iMTQwIiBmaWxsPSIjMDAwMDAwIiAvPjxyZWN0IHdpZHRoPSIxMCIgaGVpZ2h0PSIxMCIgeD0iNzAiIHk9IjE1MCIgZmlsbD0iIzAwMDAwMCIgLz48cmVjdCB3aWR0aD0iNjAiIGhlaWdodD0iMTAiIHg9IjEwMCIgeT0iMTUwIiBmaWxsPSIjMDAwMDAwIiAvPjxyZWN0IHdpZHRoPSI2MCIgaGVpZ2h0PSIxMCIgeD0iMTcwIiB5PSIxNTAiIGZpbGw9IiMwMDAwMDAiIC8+PHJlY3Qgd2lkdGg9IjYwIiBoZWlnaHQ9IjEwIiB4PSIxMDAiIHk9IjE2MCIgZmlsbD0iIzAwMDAwMCIgLz48cmVjdCB3aWR0aD0iNjAiIGhlaWdodD0iMTAiIHg9IjE3MCIgeT0iMTYwIiBmaWxsPSIjMDAwMDAwIiAvPjwvc3ZnPg==

Decode the base64, and voila:

notion image
🚧
Try it yourself! You can view the base64 encoded SVG image by calling the NounsDescriptor contract directly. Head over to the verified NounsDescriptor contract on Etherscan and click on #10 (the generateSVGImage function). Input any noun seed (e.g. ([0,2,4,123,2])) to get your encoded SVG image!
🏗️
Want to generate your own Noun on-chain? Follow our Generate a random Noun on-chain guide below:

Governance

coming soon.

 

➡️
To view the source code, visit the Github repository.
🏗️
Learn more about our smart contract architecture:
🤝
Join the community - we'd love to hear what you are building! discord.gg/nouns
⌐◨-◨

Sign up for the Nounsletter