BlockBaseエンジニアのぽんたです。
誰でもフルオンチェーンNFTを作れるサービスをリリースしたところ、反響がありました。
この記事では、そのコントラクトの一部を解説しようと思います。
githubはこちらです。
Nounsのピクセルアートの仕組み
このコントラクトはNounsDAOのコントラクトを参考に(ほぼコピーして)作っています。
NounsDAOについての解説はこちらの記事が詳しいのでオススメです!
https://ethereumnavi.com/2021/11/27/nouns-dao/
参考
RLE (Run Length Encoding) について
NounsではRun Length Encoding(連長圧縮)という手法を採用してオンチェーンに書き込むピクセルアートのデータ量を圧縮しています。Wikipediaからの引用ですが、
連長圧縮では、ある連続したデータを、そのデータ一つ分と連続した長さで表現することで圧縮している。例えば、「A A A A A B B B B B B B B B A A A」は「A 5 B 9 A 3」と表せる。
ということです。Nounsは32×32のピクセルアートで、左上から数えて1色目が何マス続いて、2色目が何マス続いて、3色目が何マス続いたあとにまた1色目が何マス続いて... というようなデータと、1色目は黄色、2色目は青色という情報のカラーパレットをブロックチェーン上に書き込み、プログラム上で画像を生成しています。
コントラクトを読み解く
まずNFTのコントラクトはこちらです。
NounsToken.sol
function tokenURI(uint256 tokenId) public view override returns (string memory) {
require(_exists(tokenId), 'NounsToken: URI query for nonexistent token');
return descriptor.tokenURI(tokenId, seeds[tokenId]);
}
seedsにはパーツごとにRLEエンコードしたピクセルアートの情報が入っています。
struct Seed {
uint48 background;
uint48 body;
uint48 accessory;
uint48 head;
uint48 glasses;
}
これをNounsDescriptor.solとNFTDescriptor.solでレンダリングしています。
https://github.com/nounsDAO/nouns-monorepo/blob/master/packages/nouns-contracts/contracts/libs/NFTDescriptor.sol
↑ここではフルオンチェーンあるあるのbase64にエンコードしてmetadataを返しています。
一番重要なのがこの
MultiPartRLEToSVG.sol というライブラリです。
_generateSVGRectsという関数にパーツの情報と背景色とカラーパレットを渡すと、SVGのを生成してstringで返してくれます。heightは10で固定、widthとx,y,fill(色)が可変になっています。
function _getChunk(uint256 cursor, string[16] memory buffer) private pure returns (string memory) {
string memory chunk;
for (uint256 i = 0; i < cursor; i += 4) {
chunk = string(
abi.encodePacked(
chunk,
'<rect width="', buffer[i], '" height="10" x="', buffer[i + 1], '" y="', buffer[i + 2], '" fill="#', buffer[i + 3], '" />'
)
);
}
return chunk;
}
Seedはどうやって作るのか
seedを作っているのはここです!
このテストのように32×32の透過pngを引数に入れて関数を実行すると、rleでエンコードされた値が返ってきます。
https://github.com/nounsDAO/nouns-monorepo/blob/04c96bd35e9dcebd983db37dfd59248b0c90284a/packages/nouns-sdk/test/png-collection-encoder.test.ts#L24
以上です!