# [Bunzz Decipher] Nounsの『NounsDescriptor』コントラクトを理解しよう！

> NounsDescriptorコントラクトは、Ethereumブロックチェーン上の大規模なシステムの重要なコントラクトです。

- URL: https://cardene.dev/articles/87a238ea31f0ba
- 公開日: 2023-08-31
- 更新日: 2023-08-31
- トピック: Blockchain

# はじめに

初めまして。
**CryptoGames**というブロックチェーンゲーム企業でエンジニアをしている **cardene（かるでね）** です！
スマートコントラクトを書いたり、フロントエンド・バックエンド・インフラと幅広く触れています。

https://cryptogames.co.jp/

代表的なゲームは**クリプトスペルズ**というブロックチェーンゲームです。

https://cryptospells.jp/

今回はBunzzの新機能『**DeCipher**』を使用して、Nounsの「**NounsDescriptor**」コントラクトを見てみようと思います。

『**DeCipher**』はAIを使用してコントラクトのドキュメントを自動生成してくれるサービスです。

https://www.bunzz.dev/decipher

詳しい使い方に関しては以下の記事を参考にしてください！

https://zenn.dev/heku/articles/33266f0c19d523

:::message
この記事はあくまで個人が「**DeCipher**」を使用してみての記事になります。
間違った部分などもあるかと思いますが、その際はコメントなどで教えてください。
:::

今回使用する『**DeCipher**』のリンクは以下になります。

https://app.bunzz.dev/decipher/chains/1/addresses/0x0Cfdb3Ba1694c2bb2CFACB0339ad7b1Ae5932B63

Etherscanのリンクは以下になります。

https://etherscan.io/address/0x0Cfdb3Ba1694c2bb2CFACB0339ad7b1Ae5932B63

# 概要

**NounsDescriptor**コントラクトは、Ethereumブロックチェーン上の大規模なシステムの重要なコントラクトです。
このシステムは非代替トークン（NFT）とやり取りするために設計されており、物理的なアイテムのように購入、販売、所有できる一意なデジタルアセットです。

## NounsDescriptorコントラクトの目的
各NFTの詳細な説明を提供することです。
これには、NFTの外見に関する情報が含まれます。
たとえば、カラーパレット、背景、ボディ、アクセサリー、頭部などが詳細に記述されます。
また、新しいパーツの追加の可否や、トークンのURI（Uniform Resource Identifier）がデータURIとして返されるかどうかについての情報も管理されます。

さらに、**NounsDescriptor**コントラクトは、NFTを識別しアクセスするための基盤であるベースURIを管理します。
ベースURIは各NFTの識別子の基本部分を構成する文字列で、これによりNFTがブロックチェーン上で特定されます。

## NounsDescriptorコントラクトの仕組み
**NounsDescriptor**コントラクトは、変数やマッピングといったデータ構造を使用して、各NFTに関連する情報を保存・管理します。
たとえば、カラーパレットに関する情報はマッピングに、背景、ボディ、アクセサリー、頭部に関する情報は配列に格納されます。
また、関数は特定のタスクを実行するためのコードの断片で、例えばベースURIや特定NFTのカラーパレットを取得するための関数があります。

## NounsDescriptorコントラクトの重要性
**NounsDescriptor**コントラクトは、NFTシステムの運用において重要な役割を果たします。
各NFTの詳細な説明を提供することで、ユーザーはNFTのユニークな特徴を理解し、魅力を感じることができます。
また、ベースURIや他の重要なデータを管理することで、**NounsDescriptor**コントラクトはNFTを正確に識別し、ブロックチェーン上で見つけることができるようにします。

:::message
まとめると、**NounsDescriptor**コントラクトはEthereumブロックチェーン上のNFTシステムにおいて重要な役割を果たしています。
各NFTの詳細な説明を提供し、重要なデータを管理し、NFTを正確に識別し、見つけることを可能にすることで、NFTの購入、販売、所有に関心を持つ人々にとって不可欠なツールとなっています。
:::

## 関連リンク

- [NounsAuctionHouse on Etherscan](https://etherscan.io/address/0x0Cfdb3Ba1694c2bb2CFACB0339ad7b1Ae5932B63)
- [NounsDAOExecutor on Etherscan](https://etherscan.io/address/0xF15a943787014461d94da08aD4040f79Cd7c124e)
- [NounsDAOProxy on Etherscan](https://etherscan.io/address/0x0BC3807Ec262cB779b38D65b38158acC3bfedE10)
- [GovernorBravoDelegator on Etherscan](https://etherscan.io/address/0x6f3E6272A167e8AcCb32072d08E0957F9c79223d)
- [Official website for Nouns](https://nouns.wtf/)

# 使い方

## コントラクトの目標
NounsDescriptorコントラクトは、Nounsトークンに関する詳細な説明を提供することを目的としています。Nounsプロジェクトは、1日に1つのNoun（NFT）を生成する分散型自治組織（DAO）の一部です。

## コントラクトの使用手順

### 1. コントラクトのデプロイ
まず、**NounsDescriptor**コントラクトをEthereumネットワークにデプロイします。
これは、コントラクトをブロックチェーン上に展開するプロセスです。
**Truffle**や**Hardhat**などの開発ツールを使用して、このデプロイを行います。

### 2. tokenURI関数の呼び出し
コントラクトがデプロイされたら、`tokenURI`関数を呼び出すことで、特定の**Noun**トークンのURIを取得できます。
トークンIDを引数として指定し、そのトークンに関連するURI（Uniform Resource Identifier）を取得します。
これにより、そのNFTの詳細情報を取得する際に利用できます。

### 3. dataURI関数の呼び出し
特定の**Noun**トークンのデータURIを取得するためには、`dataURI`関数を使用します。
同様に、トークンIDを引数として渡すことで、そのNFTのデータURIを取得します。
データURIは、NFTの詳細データを表すもので、画像やメタデータなどが含まれます。

### 4. tokenSVG関数の呼び出し
もし特定の**Noun**トークンのSVG表現を取得したい場合は、`tokenSVG`関数を呼び出します。
この関数もトークンIDを引数として渡すことで、そのNFTのSVG表現を取得します。
SVGはベクトル形式の画像表現であり、NFTの外観を表示するのに役立ちます。

これらの手順に従うことで、**NounsDescriptor**コントラクトを活用することができます。
各**Noun**トークンに関する詳細情報や、URI、データURI、SVG表現を取得するための基本的なステップです。
これにより、開発者はNFTのデータや表示に関する情報を効果的に利用できます。

**Note**
このガイドは、Ethereumやスマートコントラクト、Solidityプログラミング言語に基本的な理解があることを前提としています。
上記が初めての場合は、進む前にそれらについて学習しておくことをおすすめします。

また、**NounsDescriptor**コントラクトは広範なコントラクトのエコシステムの一部です。
その機能を完全に理解するためには、**Nouns**プロジェクト内の他のコントラクトも調べることが重要です。

# コントラクト

## NounsDescriptor

### COPYRIGHT_CC0_1_0_UNIVERSAL_LICENSE

```solidity
bytes32 constant COPYRIGHT_CC0_1_0_UNIVERSAL_LICENSE = 0xa2010f343487d3f7618affe54f789f5487602331c0a8d03f49e9a7c547cf0499;
```

**概要**
Creative Commons CC0 1.0 パブリックドメイン宣言のテキストのハッシュを表します。

**詳細**
`0xa2010f343487d3f7618affe54f789f5487602331c0a8d03f49e9a7c547cf0499`という値は、[Creative Commons CC0 1.0 パブリックドメイン宣言](https://creativecommons.org/publicdomain/zero/1.0/legalcode.txt) のテキストのハッシュです。

:::message
`COPYRIGHT_CC0_1_0_UNIVERSAL_LICENSE`変数は、**Creative Commons CC0 1.0**パブリックドメイン宣言のテキストのハッシュを格納するものです。

```solidity
// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.0;

contract LicenseExample {
    // COPYRIGHT_CC0_1_0_UNIVERSAL_LICENSE constant
    bytes32 constant COPYRIGHT_CC0_1_0_UNIVERSAL_LICENSE = 0xa2010f343487d3f7618affe54f789f5487602331c0a8d03f49e9a7c547cf0499;

    // Check if a given hash matches the CC0 1.0 license hash
    function isCC0LicenseHash(bytes32 hash) external view returns (bool) {
        return hash == COPYRIGHT_CC0_1_0_UNIVERSAL_LICENSE;
    }
}
```

この例では、`LicenseExample`コントラクト内で`COPYRIGHT_CC0_1_0_UNIVERSAL_LICENSE` を定義し、その値を使って関数`isCC0LicenseHash`を定義しています。
この関数は、与えられたハッシュが`COPYRIGHT_CC0_1_0_UNIVERSAL_LICENSE`と一致するかどうかを確認します。
実際には、他のコントラクトや関数内でライセンスを確認するための基準として使用されることがあります。

このように、`COPYRIGHT_CC0_1_0_UNIVERSAL_LICENSE`変数は、ライセンスのハッシュをコード内で参照するためのものであり、特定のライセンスが適用されていることを示すために使用されます。
:::

---

### arePartsLocked

```solidity
bool public override arePartsLocked;
```

**概要**
新しい**Noun**パーツが追加可能かどうかを示すブール値。

**詳細**
`true`の場合、新しいパーツの追加はできません。
`false`の場合、新しいパーツを追加できます。

---

### isDataURIEnabled

```solidity
bool public override isDataURIEnabled = true;
```

**概要**
`tokenURI`がデータURIとして返されるかどうかを示すブール値。

**詳細**
`true`の場合、データURIとして返されます。
`false`の場合、通常のURLとして返されます。

---

### baseURI

```solidity
string public override baseURI;
```

**概要**
トークンのメタデータのベースURIを示す文字列。

**詳細**
トークンのメタデータ（画像など）のベースとなるURIを示します。
トークンごとの識別子などが後に続き、完全なメタデータのURLを構築します。

---

### palettes

```solidity
mapping(uint8 => string[]) public override palettes;
```

**概要**
**Noun**カラーパレットを格納するマッピング配列。

**詳細**
この変数は、**Noun**の異なるパーツに対するカラーパレットを格納するためのマッピングです。`uint8`型のインデックスに対して、対応するカラーコードの文字列配列が格納されます。

---

### backgrounds

```solidity
string[] public override backgrounds;
```

**概要**
**Noun**の背景色（ヘックスカラーコード）を格納する配列。

**詳細**
**Noun**の背景色に関する情報を格納するための配列です。
背景色は`hex`カラーコードで表されます。

---

### bodies

```solidity
bytes[] public override bodies;
```

**概要**
**Noun**のボディ部分のカスタム**Run-Length Encoding（RLE）** データを格納する配列。

**詳細**
**Noun**のボディ部分のカスタム**RLE**データを格納するための配列です。
異なるボディのデザインが格納されます。

:::message
**Run-Length Encoding（RLE）**
データ圧縮技術の一種であり、連続する同じデータの塊を繰り返し回数とデータ値の組み合わせで表現する手法です。
**RLE**は、データ内でパターンが頻繁に繰り返される場合に非常に効果的です。

基本的なアイデアは、データ内で同じ値が連続している箇所を検出し、その連続したデータの数とその値を格納することです。
これにより、同じデータが繰り返し現れる場合、そのデータの個数分だけの情報で済むため、データを圧縮することができます。

例えば、文字列 **"AAAABBBCCDAA"** を**RLE**で圧縮すると **"4A3B2C1D2A"** となります。
ここでは、**A**が`4`回繰り返され、**B**が`3`回繰り返され、**C**が`2`回繰り返され、**D**が`1`回、そして最後に**A**が再び`2`回繰り返されています。

Solidityのコード内で**RLE**を使用する場合、カスタムのデータ構造を定義して、それに**RLE**形式でデータを格納することが一般的です。
これにより、特に繰り返しのパターンが多いデータを効果的に保存できます。

例えば、以下は Solidity コード内で**RLE**形式のデータを表現する単純な例です。

```solidity
contract RLEExample {
    struct RLEData {
        bytes data; // Compressed RLE data
        uint256[] counts; // Number of times each value is repeated
    }

    function compressData(bytes memory originalData) internal pure returns (RLEData memory) {
        RLEData memory compressed;

        // Compress the data using RLE
        bytes memory compressedData;
        uint256[] memory dataCounts;

        uint256 count = 1;
        for (uint256 i = 1; i < originalData.length; i++) {
            if (originalData[i] == originalData[i - 1]) {
                count++;
            } else {
                compressedData.push(originalData[i - 1]);
                dataCounts.push(count);
                count = 1;
            }
        }
        compressedData.push(originalData[originalData.length - 1]);
        dataCounts.push(count);

        compressed.data = compressedData;
        compressed.counts = dataCounts;

        return compressed;
    }
}
```

この例では、`RLEData`構造体を定義して、**RLE**形式のデータとその出現回数を格納します。
`compressData`関数は、元のデータを受け取り、**RLE**で圧縮したデータと出現回数を返します。

**RLE**は特にデータ内でパターンが繰り返される場合に有効であり、データを効果的に圧縮する手法の一つです。
:::

---

### accessories

```solidity
bytes[] public override accessories;
```

**概要**
**Noun**のアクセサリ部分のカスタム**Run-Length Encoding（RLE）** データを格納する配列。

**詳細**
この変数は、**Noun**のアクセサリ部分のカスタム**RLE**データを格納するための配列。
異なるアクセサリのデザインが格納されます。

---

### heads

```solidity
bytes[] public override heads;
```

**概要**
**Noun**のヘッド部分のカスタム**Run-Length Encoding（RLE）** データを格納する配列。

**詳細**
**Noun**のヘッド部分のカスタム**RLE**データを格納するための配列です。
異なるヘッドのデザインが格納されます。

---

### glasses

```solidity
bytes[] public override glasses;
```

**概要**
**Noun**のメガネ部分のカスタム**Run-Length Encoding（RLE）** データを格納する配列。

**詳細**
**Noun**のメガネ部分のカスタム**RLE**データを格納するための配列です。異なるメガネのデザインが格納されます。

---

### whenPartsNotLocked

```solidity
modifier whenPartsNotLocked() {
        require(!arePartsLocked, 'Parts are locked');
        _;
}
```

**概要**
パーツがロックされていないことを確認する修飾子。

**詳細**
この修飾子は、関数の実行前に呼び出され、`arePartsLocked`変数が`false`（ロックされていない）ことを確認します。
もし変数が`true`の場合、**'Parts are locked'** というエラーメッセージとともに関数の実行が中断されます。


### backgroundCount

:::details backgroundCount
```solidity
function backgroundCount() external view override returns (uint256) {
        return backgrounds.length;
}
```
:::

**概要**
**Noun**の「`backgrounds`」の利用可能な数を取得する関数。


**戻り値**
- `uint256`
	- 利用可能な**Noun**背景の数。

---

### bodyCount

:::details bodyCount
```solidity
function bodyCount() external view override returns (uint256) {
        return bodies.length;
}
```
:::


**概要**
**Noun**の「`bodies`」の利用可能な数を取得する関数。


**戻り値**
- `uint256`
	- 利用可能な**Noun**体の数。

---

### accessoryCount

:::details accessoryCount
```solidity
function accessoryCount() external view override returns (uint256) {
        return accessories.length;
}
```
:::

**概要**
**Noun**の「`accessories`」の利用可能な数を取得する関数。

**戻り値**
- `uint256`
	- 利用可能な**Noun**アクセサリの数。

---

### headCount

:::details 
```solidity
function headCount() external view override returns (uint256) {
        return heads.length;
}
```
:::

**概要**
**Noun**の「`heads`」の利用可能な数を取得する関数。

**戻り値**
- `uint256`
	- 利用可能な**Noun**頭部の数。

---

### glassesCount

:::details glassesCount
```solidity
function glassesCount() external view override returns (uint256) {
        return glasses.length;
}
```
:::

**概要**
**Noun**の「`glasses`」の利用可能な数を取得する関数。

**戻り値**
- `uint256`
	- 利用可能な**Noun**眼鏡の数。

---

### addManyColorsToPalette

:::details addManyColorsToPalette
```solidity
function addManyColorsToPalette(uint8 paletteIndex, string[] calldata newColors) external override onlyOwner {
        require(palettes[paletteIndex].length + newColors.length <= 256, 'Palettes can only hold 256 colors');
        for (uint256 i = 0; i < newColors.length; i++) {
            _addColorToPalette(paletteIndex, newColors[i]);
        }
}
```
:::

**概要**
カラーパレットに色を追加する関数。

**詳細**
この関数はオーナーによって呼び出されることができ、指定したカラーパレットに複数の色を追加します。
パレットのインデックスと新しい色の配列を引数として受け取り、追加される色の合計が`256`色以下であることを確認します。

**引数**
- `paletteIndex`
	- カラーパレットのインデックス。
- `newColors`
	- 追加する色の配列。

**戻り値**
なし

---

### addManyBackgrounds

:::details addManyBackgrounds
```solidity
function addManyBackgrounds(string[] calldata _backgrounds) external override onlyOwner whenPartsNotLocked {
        for (uint256 i = 0; i < _backgrounds.length; i++) {
            _addBackground(_backgrounds[i]);
        }
}
```
:::

**概要**
複数の**Noun**背景を一括追加する関数。

**詳細**
この関数はオーナーによって、ロックされていない状態で呼び出されることができます。
与えられた**Noun**背景の配列を順番に追加します。

**引数**
- `_backgrounds`
	- 追加する**Noun**背景の配列。

**戻り値**
なし

---

### addManyBodies

:::details addManyBodies
```solidity
function addManyBodies(bytes[] calldata _bodies) external override onlyOwner whenPartsNotLocked {
        for (uint256 i = 0; i < _bodies.length; i++) {
            _addBody(_bodies[i]);
        }
}
```
:::

**概要**
複数の**Noun**のボディーを一括追加する関数。

**引数**
- `_bodies`
	- 追加する**Noun**のボディーのバイト列の配列。

**戻り値**
なし

---

### addManyAccessories

:::details addManyAccessories
```solidity
function addManyAccessories(bytes[] calldata _accessories) external override onlyOwner whenPartsNotLocked {
        for (uint256 i = 0; i < _accessories.length; i++) {
            _addAccessory(_accessories[i]);
        }
}
```
:::

**概要**
複数の**Noun**アクセサリを一括追加する関数。

**引数**
- `_accessories`
	- 追加する**Noun**アクセサリのバイト列の配列。

**戻り値**
なし

---

### addManyHeads

:::details addManyHeads
```solidity
function addManyHeads(bytes[] calldata _heads) external override onlyOwner whenPartsNotLocked {
        for (uint256 i = 0; i < _heads.length; i++) {
            _addHead(_heads[i]);
        }
}
```
:::

**概要**
複数の**Noun**頭部を一括追加する関数。

**引数**
- `_heads`
	- 追加する**Noun**頭部のバイト列の配列。

**戻り値**
なし

---

### addManyGlasses

:::details addManyGlasses
```solidity
function addManyGlasses(bytes[] calldata _glasses) external override onlyOwner whenPartsNotLocked {
        for (uint256 i = 0; i < _glasses.length; i++) {
            _addGlasses(_glasses[i]);
        }
}
```
:::

**概要**
複数の**Noun**眼鏡を一括追加する関数。

**引数**
- `_glasses`
	- 追加する**Noun**眼鏡のバイト列の配列。

**戻り値**
なし

---

### addColorToPalette

:::details addColorToPalette
```solidity
function addColorToPalette(uint8 _paletteIndex, string calldata _color) external override onlyOwner {
        require(palettes[_paletteIndex].length <= 255, 'Palettes can only hold 256 colors');
        _addColorToPalette(_paletteIndex, _color);
}
```
:::

**概要**
カラーパレットに単一の色を追加する関数。

**詳細**
この関数はオーナーによって呼び出され、指定したカラーパレットに単一の色を追加します。
パレットのインデックスと追加する色の文字列を引数として受け取り、パレットが`256`色以下の色を保持することを確認します。

**引数**
- `_paletteIndex`
	- カラーパレットのインデックス。
- `_color`
	- 追加する色の文字列。

**戻り値**
なし

---

### addBackground

:::details addBackground
```solidity
function addBackground(string calldata _background) external override onlyOwner whenPartsNotLocked {
        _addBackground(_background);
}
```
:::

**概要**
**Noun**の背景を追加する関数。

**詳細**
オーナーによって呼び出され、ロックされていない状態で**Noun**の背景を追加します。

**引数**
- `_background`
	- 追加する**Noun**背景の文字列。

**戻り値**
なし

---

### addBody

:::details addBody
```solidity
function addBody(bytes calldata _body) external override onlyOwner whenPartsNotLocked {
        _addBody(_body);
}
```
:::

**概要**
**Noun**のボディーを追加する関数。

**詳細**
この関数はオーナーによって呼び出され、ロックされていない状態で**Noun**のボディーを追加します。

**引数**
- `_body`
	- 追加する**Noun**のボディーのバイト列。

**戻り値**
なし

---

### addAccessory

:::details addAccessory
```solidity
function addAccessory(bytes calldata _accessory) external override onlyOwner whenPartsNotLocked {
        _addAccessory(_accessory);
}
```
:::

**概要**
**Noun**のアクセサリを追加する関数。

**詳細**
この関数はオーナーによって呼び出され、ロックされていない状態で**Noun**のアクセサリを追加します。

**引数**
- `_accessory`
	- 追加する**Noun**アクセサリのバイト列。

**戻り値**
なし

---

### addHead

:::details addHead
```solidity
function addHead(bytes calldata _head) external override onlyOwner whenPartsNotLocked {
        _addHead(_head);
}
```
:::

**概要**
**Noun**の頭部を追加する関数。

**詳細**
この関数はオーナーによって呼び出され、ロックされていない状態で**Noun**の頭部を追加します。

**引数**
- `_head`
	- 追加する**Noun**頭部のバイト列。

**戻り値**
なし

---

### addGlasses

:::details addGlasses
```solidity
function addGlasses(bytes calldata _glasses) external override onlyOwner whenPartsNotLocked {
        _addGlasses(_glasses);
}
```
:::

**概要**
**Noun**の眼鏡を追加する関数。

**詳細**
この関数はオーナーによって呼び出され、ロックされていない状態で**Noun**の眼鏡を追加します。

**引数**
- `_glasses`
	- 追加する**Noun**眼鏡のバイト列。

**戻り値**
なし

---

### lockParts

:::details lockParts
```solidity
function lockParts() external override onlyOwner whenPartsNotLocked {
        arePartsLocked = true;

        emit PartsLocked();
}
```
:::

**概要**
すべての**Noun**パーツをロックする関数。

**詳細**
この関数はオーナーによって呼び出され、ロックされていない状態ですべての**Noun**パーツをロックします。
一度ロックすると元に戻すことはできません。

**戻り値**
なし

---

### toggleDataURIEnabled

:::details toggleDataURIEnabled
```solidity
function toggleDataURIEnabled() external override onlyOwner {
        bool enabled = !isDataURIEnabled;

        isDataURIEnabled = enabled;
        emit DataURIToggled(enabled);
}
```
:::

**概要**
`tokenURI`がデータURIかHTTP URLを返すかを切り替える関数。

**詳細**
この関数はオーナーによって呼び出され、`tokenURI`が返す値がデータURIかHTTP URLかを切り替えます。

**戻り値**
なし

---

### setBaseURI

:::details setBaseURI
```solidity
function setBaseURI(string calldata _baseURI) external override onlyOwner {
        baseURI = _baseURI;

        emit BaseURIUpdated(_baseURI);
}
```
:::

**概要**
すべてのトークンIDのベースURIを設定する関数。

**詳細**
この関数はオーナーによって呼び出され、`tokenURI`で返される値に自動的に接頭辞が付加されます。

**引数**
- `_baseURI`
	- ベースURI。

**戻り値**
なし

---

### tokenURI

:::details tokenURI
```solidity
function tokenURI(uint256 tokenId, INounsSeeder.Seed memory seed) external view override returns (string memory) {
        if (isDataURIEnabled) {
            return dataURI(tokenId, seed);
        }
        return string(abi.encodePacked(baseURI, tokenId.toString()));
}
```
:::

**概要**
公式の**Nouns DAO**の**Nouns**のためのトークンURIを生成する関数。

**詳細**
与えられたトークンIDとシードを使用して、トークンURIを生成します。
`isDataURIEnabled`が有効な場合、データURIを生成し、それ以外の場合はHTTP URLを生成します。

**引数**
- `tokenId`
	- トークンID。
- `INounsSeeder.Seed memory seed`
	- シード情報。

**戻り値**
- `memory`
	- 生成されたトークンURI。

---

### dataURI

:::details dataURI
```solidity
function dataURI(uint256 tokenId, INounsSeeder.Seed memory seed) public view override returns (string memory) {
        string memory nounId = tokenId.toString();
        string memory name = string(abi.encodePacked('Noun ', nounId));
        string memory description = string(abi.encodePacked('Noun ', nounId, ' is a member of the Nouns DAO'));

        return genericDataURI(name, description, seed);
}
```
:::

**概要**
与えられたトークンIDとシード(`seed`)に基づいて、公式の**Nouns DAO**の**Noun**のbase64エンコードされたデータURIを構築する関数。

**詳細**
与えられたトークンIDとシード(`seed`)から名前と説明を生成し、`genericDataURI`関数を呼び出してデータURIを構築します。

**引数**
- `tokenId`
	- トークンのID。
- `INounsSeeder.Seed memory seed`
	- シード情報。

**戻り値**
- `string memory`
	- base64エンコードされたデータURI。

---

### genericDataURI

:::details genericDataURI
```solidity
function genericDataURI(
        string memory name,
        string memory description,
        INounsSeeder.Seed memory seed
    ) public view override returns (string memory) {
        NFTDescriptor.TokenURIParams memory params = NFTDescriptor.TokenURIParams({
            name: name,
            description: description,
            parts: _getPartsForSeed(seed),
            background: backgrounds[seed.background]
        });
        return NFTDescriptor.constructTokenURI(params, palettes);
}
```
:::

**概要**
与えられた名前、説明、およびシード(`seed`)に基づいて、base64エンコードされたデータURIを構築する関数。

**詳細**
名前、説明、およびシード(`seed`)を受け取り、それらの情報を使って`NFTDescriptor.TokenURIParams`を生成し、`constructTokenURI`関数を呼び出してデータURIを構築します。

**引数**
- `name`
	- 名前。
- `description`
	- 説明。
- `INounsSeeder.Seed memory seed`
	- シード情報。

**戻り値**
- `string memory`
	- base64エンコードされたデータURI。

---

### generateSVGImage

:::details generateSVGImage
```solidity
function generateSVGImage(INounsSeeder.Seed memory seed) external view override returns (string memory) {
        MultiPartRLEToSVG.SVGParams memory params = MultiPartRLEToSVG.SVGParams({
            parts: _getPartsForSeed(seed),
            background: backgrounds[seed.background]
        });
        return NFTDescriptor.generateSVGImage(params, palettes);
}
```
:::

**概要**
与えられたシード(`seed`)に基づいて、base64エンコードされたSVG画像を構築する関数。

**詳細**
この関数は、シード(`seed`)を受け取り、それを使って`MultiPartRLEToSVG.SVGParams`を生成し、`generateSVGImage`関数を呼び出してSVG画像を構築します。

**引数**
- `INounsSeeder.Seed memory seed`
	- シード情報。

**戻り値**
- `string memory`
	- base64エンコードされたSVG画像。

---

### _addColorToPalette

:::details _addColorToPalette
```solidity
function _addColorToPalette(uint8 _paletteIndex, string calldata _color) internal {
        palettes[_paletteIndex].push(_color);
}
```
:::

**概要**
カラーパレットに単一の色を追加する関数。

**詳細**
与えられたパレットインデックスと色を使って、カラーパレットに色を追加します。

**引数**
- `_paletteIndex`
	- パレットのインデックス。
- `_color`
	- 追加する色。

**戻り値**
なし

---

### _addBackground

:::details _addBackground
```solidity
function _addBackground(string calldata _background) internal {
        backgrounds.push(_background);
}
```
:::

**概要**
**Noun**の背景を追加する関数。

**詳細**
与えられた背景を背景リストに追加します。

**引数**
- `_background`
	- 追加する背景。

**戻り値**
なし

---

### _addBody

:::details _addBody
```solidity
function _addBody(bytes calldata _body) internal {
        bodies.push(_body);
}
```
:::

**概要**
**Noun**のボディーを追加します。

**詳細**
与えられた**Noun**のボディーをボディーリストに追加します。

**引数**
- `_body`
	- 追加するボディーのバイト列。

**戻り値**
なし

---

### _addAccessory

:::details _addAccessory
```solidity
function _addAccessory(bytes calldata _accessory) internal {
        accessories.push(_accessory);
}
```
:::

**概要**
**Noun**のアクセサリを追加します。

**詳細**
与えられたアクセサリをアクセサリリストに追加します。

**引数**
- `_accessory`
	- 追加するアクセサリのバイト列。

**戻り値**
なし

---

### _addHead

:::details _addHead
```solidity
function _addHead(bytes calldata _head) internal {
        heads.push(_head);
}
```
:::

**概要**
**Noun**の頭部を追加する関数。

**詳細**
与えられた頭部を頭部リストに追加します。

**引数**
- `_head`
	- 追加する頭部のバイト列。

**戻り値**
なし

---

### _addGlasses

:::details tokenURI
```solidity
function _addGlasses(bytes calldata _glasses) internal {
        glasses.push(_glasses);
}
```
:::

**概要**
**Noun**の眼鏡を追加する関数。

**詳細**
与えられた眼鏡を眼鏡リストに追加します。

**引数**
- `_glasses`
	- 追加する眼鏡のバイト列。

**戻り値**
なし

---

### _getPartsForSeed

:::details tokenURI
```solidity
function _getPartsForSeed(INounsSeeder.Seed memory seed) internal view returns (bytes[] memory) {
        bytes[] memory _parts = new bytes[](4);
        _parts[0] = bodies[seed.body];
        _parts[1] = accessories[seed.accessory];
        _parts[2] = heads[seed.head];
        _parts[3] = glasses[seed.glasses];
        return _parts;
}
```
:::

**概要**
与えられたシード(`seed`)に対応するすべての**Noun**パーツを取得する関数。

**詳細**
与えられたシード(`seed`)を使って、対応する**Noun**のボディー、アクセサリ、頭部、眼鏡のパーツを取得します。

**引数**
- `INounsSeeder.Seed memory seed`
	- シード情報。

**戻り値**
- `bytes[] memory`
	- パーツのバイト列の配列。

## NFTDescriptor

### TokenURIParams

:::details TokenURIParams
```solidity
struct TokenURIParams {
        string name;
        string description;
        bytes[] parts;
        string background;
}
```
:::

**概要**
**ERC721**トークンのURIを構築するためのパラメータを保持する構造体。

**パラメータ**
- `name`
	- ークンの名前を示す文字列。
- `description`
	- トークンの説明を示す文字列。
- `parts`
	- トークンを構成するパーツのバイト列の配列。
- `background`
	- トークンの背景を示す文字列。

---

### constructTokenURI

:::details constructTokenURI
```solidity
function constructTokenURI(TokenURIParams memory params, mapping(uint8 => string[]) storage palettes)
        public
        view
        returns (string memory)
    {
        string memory image = generateSVGImage(
            MultiPartRLEToSVG.SVGParams({ parts: params.parts, background: params.background }),
            palettes
        );

        // prettier-ignore
        return string(
            abi.encodePacked(
                'data:application/json;base64,',
                Base64.encode(
                    bytes(
                        abi.encodePacked('{"name":"', params.name, '", "description":"', params.description, '", "image": "', 'data:image/svg+xml;base64,', image, '"}')
                    )
                )
            )
        );
}
```
:::

**ERC721**トークンのURIを構築する関数。

**詳細**
与えられた`TokenURIParams`およびパレット情報を基に、トークンのURIを構築します。
トークンの名前、説明、パーツ、および背景情報から、JSON形式のメタデータを生成し、それをBase64エンコードしてURIを完成させます。

**引数**
- `params`
	- **ERC721**トークンのメタデータパラメータを保持する`TokenURIParams`構造体。
- `palettes`
	- パーツのパレット情報を保持するマッピング配列。

**戻り値**
生成されたトークンのURIを文字列として返します。

---

### generateSVGImage

:::details generateSVGImage
```solidity
function generateSVGImage(MultiPartRLEToSVG.SVGParams memory params, mapping(uint8 => string[]) storage palettes)
        public
        view
        returns (string memory svg)
    {
        return Base64.encode(bytes(MultiPartRLEToSVG.generateSVG(params, palettes)));
}
```
:::

**概要**
**ERC721**トークンのURIに含まれるSVG画像を生成する関数。

**詳細**
与えられた`SVGParams`およびパレット情報を基に、複数のパーツを組み合わせたSVG画像を生成します。
具体的には、`MultiPartRLEToSVG.generateSVG`関数を呼び出してSVG画像を生成し、それをBase64エンコードして返します。

**引数**
- `params`
	- SVG画像の生成に必要なパラメータを保持する`SVGParams`構造体。
- `palettes`
	- パーツのパレット情報を保持するマッピング配列。

**戻り値**
生成されたSVG画像をBase64エンコードして文字列として返します。

## MultiPartRLEToSVG

### SVGParams

:::details SVGParams
```solidity
struct SVGParams {
        bytes[] parts;
        string background;
}
```
:::

**概要**
SVGイメージを生成する際のパラメータを格納する構造体。

**詳細**
SVGイメージの生成に必要な情報を保持します。
`parts`はRLE形式の画像パーツをバイト配列として格納し、`background`はSVGイメージの背景色を文字列として保持します。

**パラメータ**
- `parts`
	- RLE形式の画像パーツを格納したバイト配列。
- `background`
	- SVGイメージの背景色を表す文字列。

---

### ContentBounds

:::details ContentBounds
```solidity
struct ContentBounds {
        uint8 top;
        uint8 right;
        uint8 bottom;
        uint8 left;
}
```
:::

**概要**
コンテンツの境界情報を格納する構造体。

**詳細**
コンテンツ内の図形が配置される範囲を示す情報を保持します。

**パラメータ**
- `top`
	- コンテンツの上端位置。
- `right`
	- コンテンツの右端位置。
- `bottom`
	- コンテンツの下端位置。
- `left`
	- コンテンツの左端位置。

---

### Rect

:::details Rect
```solidity
struct Rect {
        uint8 length;
        uint8 colorIndex;
}
```
:::

**概要**
矩形の情報を格納する構造体。

**詳細**
矩形の長さとカラーインデックスを保持します。

**パラメータ**
- `length`
	- 矩形の長さ。
- `colorIndex`
	- 矩形のカラーインデックス。

---

### DecodedImage

:::details DecodedImage
```solidity
struct DecodedImage {
        uint8 paletteIndex;
        ContentBounds bounds;
        uint256 width;
        Rect[] rects;
}
```
:::

**概要**
**RLE**形式で圧縮された画像をデコードした情報を格納する構造体。

**パラメータ**
- `paletteIndex`
	- パレットのインデックス。
- `bounds`
	- コンテンツの範囲。
- `width`
	- 画像の幅。
- `rects`
	- 画像を構成する各矩形の情報。

---

### generateSVG

:::details generateSVG
```solidity
function generateSVG(SVGParams memory params, mapping(uint8 => string[]) storage palettes)
        internal
        view
        returns (string memory svg)
    {
        // prettier-ignore
        return string(
            abi.encodePacked(
                '<svg width="320" height="320" viewBox="0 0 320 320" xmlns="http://www.w3.org/2000/svg" shape-rendering="crispEdges">',
                '<rect width="100%" height="100%" fill="#', params.background, '" />',
                _generateSVGRects(params, palettes),
                '</svg>'
            )
        );
}
```
:::

**概要**
**RLE**画像パーツとカラーパレットを組み合わせて単一のSVGイメージを生成する関数。

**詳細**
与えられたRLE画像パーツとカラーパレットを用いて、指定された背景色を持つSVGイメージを生成します。
SVGの`<rect>`要素と`_generateSVGRects`関数から得られる**SVG rects**を結合して完成されたSVGイメージを生成します。

**引数**
- `params`
	- SVGイメージ生成に必要なパラメータを格納する`SVGParams`構造体。
- `palettes`
	- カラーパレットのマッピング配列。

**戻り値**
生成されたSVGイメージを表す文字列。

---

### _generateSVGRects

:::details _generateSVGRects
```solidity
function _generateSVGRects(SVGParams memory params, mapping(uint8 => string[]) storage palettes)
        private
        view
        returns (string memory svg)
    {
        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' 
        ];
        string memory rects;
        for (uint8 p = 0; p < params.parts.length; p++) {
            DecodedImage memory image = _decodeRLEImage(params.parts[p]);
            string[] storage palette = palettes[image.paletteIndex];
            uint256 currentX = image.bounds.left;
            uint256 currentY = image.bounds.top;
            uint256 cursor;
            string[16] memory buffer;

            string memory part;
            for (uint256 i = 0; i < image.rects.length; i++) {
                Rect memory rect = image.rects[i];
                if (rect.colorIndex != 0) {
                    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) {
                        part = string(abi.encodePacked(part, _getChunk(cursor, buffer)));
                        cursor = 0;
                    }
                }

                currentX += rect.length;
                if (currentX - image.bounds.left == image.width) {
                    currentX = image.bounds.left;
                    currentY++;
                }
            }

            if (cursor != 0) {
                part = string(abi.encodePacked(part, _getChunk(cursor, buffer)));
            }
            rects = string(abi.encodePacked(rects, part));
        }
        return rects;
}
```
:::

**概要**
**RLE**画像パーツとカラーパレットを用いて**SVG rects**を生成する関数。

**詳細**
与えられた**RLE**画像パーツとカラーパレットを使用して、SVGの`<rect>`要素を生成します。
各矩形の情報を元に**SVG rects**を生成し、それらを結合して返します。

**引数**
- `params`
	- SVGイメージ生成に必要なパラメータを格納する`SVGParams`構造体。
- `palettes`
	- カラーパレットのマッピング配列。

**戻り値**
生成された**SVG rects**を表す文字列。

---

### _getChunk

:::details _getChunk
```solidity
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;
}
```
:::

**概要**
提供された`buffer`内のすべての矩形から成る文字列を返す関数。

**詳細**
与えられた`buffer`内の矩形の情報を元に、各矩形をSVGの`<rect>`要素として生成し、それらを結合して文字列として返します。

**引数**
- `cursor`
	- 現在のカーソル位置を示す整数。
- `buffer`
	- 矩形情報を保持する文字列の配列。

**戻り値**
すべての矩形から成るSVGの文字列。

---

### _decodeRLEImage

:::details _decodeRLEImage
```solidity
function _decodeRLEImage(bytes memory image) private pure returns (DecodedImage memory) {
        uint8 paletteIndex = uint8(image[0]);
        ContentBounds memory bounds = ContentBounds({
            top: uint8(image[1]),
            right: uint8(image[2]),
            bottom: uint8(image[3]),
            left: uint8(image[4])
        });
        uint256 width = bounds.right - bounds.left;

        uint256 cursor;
        Rect[] memory rects = new Rect[]((image.length - 5) / 2);
        for (uint256 i = 5; i < image.length; i += 2) {
            rects[cursor] = Rect({ length: uint8(image[i]), colorIndex: uint8(image[i + 1]) });
            cursor++;
        }
        return DecodedImage({ paletteIndex: paletteIndex, bounds: bounds, width: width, rects: rects });
}
```
:::

**概要**
単一の**RLE**圧縮画像を`DecodedImage`としてデコードする関数。

**詳細**
与えられた**RLE**圧縮画像データを解読し、`DecodedImage`構造体として結果を返します。
画像のパレットインデックス、コンテンツの範囲、幅、および各矩形の情報を復元します。

**引数**
- `image`
	- **RLE**圧縮画像を表すバイト配列。

**戻り値**
`DecodedImage`構造体。

---

# イベント

### PartsLocked

:::details PartsLocked
```solidity
event PartsLocked();
```
:::

**概要**
パーツがロックされた時に発行されるイベント。

---

### DataURIToggled

:::details DataURIToggled
```solidity
event DataURIToggled(bool enabled);
```
:::

**概要**
**Data URI**が有効に切り替えられた時に発行されるイベント。

**パラメータ**
- `enabled`
	- **Data URI**が有効になった場合は`true`、無効になった場合は`false`。

---

### BaseURIUpdated

:::details BaseURIUpdated
```solidity
event BaseURIUpdated(string baseURI);
```
:::

**概要**
ベースURIが更新された時に発行されるイベント。

**パラメータ**
- `baseURI`
	- 更新されたベースURIを表す文字列。

---

# コード

## インターフェース

- `/contracts/interfaces/`

## ライブラリ

- `/contracts/libs/`

# 最後に

今回の記事では、Bunzzの新機能『**DeCipher**』を使用して、**Nouns**の「**NounsDescriptor**」のコントラクトを見てきました。
いかがだったでしょうか？
今後も特定のNFTやコントラクトをピックアップしてまとめて行きたいと思います。

普段はブログやQiitaでブロックチェーンやAIに関する記事を挙げているので、よければ見ていってください！

https://chaldene.net/

https://qiita.com/cardene
