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

> NounsSeederコントラクトは、Ethereumブロックチェーン上に構築されたNounsプロジェクトの中で重要な役割を果たすコントラクトです。

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

# はじめに

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

https://cryptogames.co.jp/

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

https://cryptospells.jp/

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

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

https://www.bunzz.dev/decipher

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

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

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

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

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

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

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

# 概要

**NounsSeeder**コントラクトは、Ethereumブロックチェーン上に構築された**Nouns**プロジェクトの中で重要な役割を果たすコントラクトです。

## コントラクトとは？
ブロックチェーンとweb3の文脈では、コントラクトはブロックチェーン上に存在するコードの一部です。
これは、オブジェクト指向プログラミングにおけるクラスに類似しています。
コントラクトにはデータ変数と関数が含まれ、他のコントラクトとのやり取りが可能です。
これらのコントラクトは、分散型アプリケーション（dApps）の基本的な要素です。

## NounsSeederコントラクトとは？
**NounsSeeder**コントラクトは、Nounsプロジェクト内の特定のコントラクトです。
主な役割は、疑似乱数のシードを生成することです。
これらのシードは、**Nouns**プロジェクト内のデジタルアセットである「**Nouns**」を生成するために使用されます。

## シードとは？
コンピューティングの文脈では、シードは疑似乱数の数列を開始する出発点です。
同じシードから始めると、同じ疑似乱数の数列を生成できます。
この特性は、**Nouns**などのユニークなデジタルアセットを生成する際に利用されます。

## NounsSeederコントラクトの動作は？
**NounsSeeder**コントラクトには「`generateSeed`」という関数があります。
この関数は、**Nouns**のIDとディスクリプタという2つの入力を受け取ります。
**Nouns**のIDは、各**Nouns**を一意に識別するための番号であり、ディスクリプタは**Nouns**に関する情報を提供する別のコントラクトです。
ディスクリプタには、背景、ボディ、アクセサリ、ヘッド、メガネなどの選択肢が含まれます。

`generateSeed`関数は、前のブロックのハッシュ（ブロックチェーン内でブロックを一意に識別する値）と**Nouns**のIDを使用して、疑似乱数を生成します。
この数値は、ディスクリプタから提供される選択肢から、**Nouns**の背景、ボディ、アクセサリ、ヘッド、メガネを選ぶために使用されます。

## NounsSeederコントラクトの重要性は？
`NounsSeeder`コントラクトは、**Nouns**プロジェクトにおいて非常に重要です。
各**Nouns**がユニークであることを確保します。
疑似乱数のシードを使用して各**Nouns**の属性を選択することで、同じ**Nouns**が2つ存在することを防ぎます。
このユニーク性は、ブロックチェーン上のデジタルアセットの価値を確保する重要な特徴です。

:::message
要約すると、**NounsSeeder**コントラクトは**Nouns**プロジェクトの中で重要な役割を果たす要素です。
疑似乱数のシードを生成し、ユニークな**Nouns**を作成します。
技術的な詳細は複雑かもしれませんが、その目的と役割は明確で**Nouns**プロジェクト内の各**Nouns**のユニーク性を確保することです。
:::

# 使い方

このガイドは、**NounsSeeder**コントラクトを使用する手順をステップバイステップです。
**NounsSeeder**コントラクトは、**Nouns**プロジェクトのために疑似乱数のシードを生成するために使用され、これによって**Nouns**特性が決定されます。

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

### 1. コントラクトのインポート
**NounsSeeder**コントラクトをSolidityファイルに`import`キーワードを使用してインポートします。
```solidity
import { NounsSeeder } from './NounsSeeder.sol';
```

### 2. コントラクトのインスタンス化
**NounsSeeder**コントラクトのインスタンスを作成します。
これには、デプロイされたコントラクトのアドレスが必要です。
```solidity
NounsSeeder public seeder;
```

### 3. generateSeed関数の呼び出し
適切なパラメータで`generateSeed`関数を呼び出します。
この関数には2つのパラメータが必要です。
   - `nounId`
	   - **Nouns**のユニークな識別子。
	   - 特定の**Nouns**のシードを生成するために使用します。
   - `descriptor`: 
	   - **INounsDescriptor**コントラクトのインスタンス。
	   - このコントラクトは、**Nouns**が持つ異なる特性（背景、ボディ、アクセサリ、ヘッド、メガネ）のカウントを取得するために使用されます。
```solidity
function generateNounSeed(uint256 nounId) public view returns (NounsSeeder.Seed memory) {
return seeder.generateSeed(nounId, descriptor);
}
```

`generateSeed`関数は`Seed`構造体を返します。
この構造体には、**Nouns**のために疑似乱数で生成された特性が含まれています。
これらの特性は`uint48`の値として表されます。

### 使用例
以下は、**NounsSeeder**コントラクトを独自のコントラクト内で使用する方法の例です。

```solidity
pragma solidity ^0.8.6;

import { NounsSeeder } from './NounsSeeder.sol';
import { INounsDescriptor } from './interfaces/INounsDescriptor.sol';

contract MyContract {
    NounsSeeder public seeder;
    INounsDescriptor public descriptor;

    constructor(NounsSeeder _seeder, INounsDescriptor _descriptor) {
        seeder = _seeder;
        descriptor = _descriptor;
    }

    function generateNounSeed(uint256 nounId) public view returns (NounsSeeder.Seed memory) {
        return seeder.generateSeed(nounId, descriptor);
    }
}
```

この例では、**MyContract**が**NounsSeeder**コントラクトをインポートし、特定の**Noun ID**のシードを生成するために使用しています。
`generateNounSeed`関数を呼び出すことで、シードを生成して返すことができます。

# コントラクト

## NounsSeeder

### backgroundCount

```solidity
uint256 backgroundCount = descriptor.backgroundCount();
```

**概要**
`descriptor`（**INounsDescriptor**インターフェースのインスタンス）から取得される背景（background）の総数を表した変数。

**詳細**
`descriptor.backgroundCount()`を呼び出すことで、NFT（Non-Fungible Token）の背景として使用可能なアイテムの総数を取得します。
この数は環境によって異なる可能性があります。
背景アイテムは、NFTの生成時にランダムに選ばれる要素の一部です。

---

### bodyCount

```solidity
uint256 bodyCount = descriptor.bodyCount();
```

**概要**
`descriptor`（**INounsDescriptor**インターフェースのインスタンス）から取得されるボディ（body）の総数を表した変数。

**詳細**
`descriptor.bodyCount()`を呼び出すことで、NFTのボディとして使用可能なアイテムの総数を取得します。
ボディアイテムは、生成されるNFTの外見の一部を構成する要素です。

---

### accessoryCount

```solidity
uint256 accessoryCount = descriptor.accessoryCount();
```

**概要**
`descriptor`（**INounsDescriptor**インターフェースのインスタンス）から取得されるアクセサリー（accessory）の総数を表した変数。

**詳細**
`descriptor.accessoryCount()`を呼び出すことで、NFTのアクセサリーとして使用可能なアイテムの総数を取得します。
アクセサリーアイテムは、NFTの外見をカスタマイズするための要素です。

---

### headCount

```solidity
uint256 headCount = descriptor.headCount();
```

**概要**
`descriptor`（**INounsDescriptor**インターフェースのインスタンス）から取得されるヘッド（head）の総数を表した変数。

**詳細**
`descriptor.headCount()`を呼び出すことで、NFTのヘッドとして使用可能なアイテムの総数を取得します。
ヘッドアイテムは、NFTの外見を構成する要素の一部です。

---

### glassesCount

```solidity
uint256 glassesCount = descriptor.glassesCount();
```

**概要**
`descriptor`（**INounsDescriptor**インターフェースのインスタンス）から取得されるメガネ（glasses）の総数を表した変数。

**詳細**
`descriptor.glassesCount()`を呼び出すことで、NFTのメガネとして使用可能なアイテムの総数を取得します。
メガネアイテムは、NFTの外見をカスタマイズするための要素です。

---

### generateSeed

:::details generateSeed
```solidity
// SPDX-License-Identifier: GPL-3.0

/// @title The NounsToken pseudo-random seed generator

/*********************************
 * ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ *
 * ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ *
 * ░░░░░░█████████░░█████████░░░ *
 * ░░░░░░██░░░████░░██░░░████░░░ *
 * ░░██████░░░████████░░░████░░░ *
 * ░░██░░██░░░████░░██░░░████░░░ *
 * ░░██░░██░░░████░░██░░░████░░░ *
 * ░░░░░░█████████░░█████████░░░ *
 * ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ *
 * ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ *
 *********************************/

pragma solidity ^0.8.6;

import { INounsSeeder } from './interfaces/INounsSeeder.sol';
import { INounsDescriptor } from './interfaces/INounsDescriptor.sol';

contract NounsSeeder is INounsSeeder {
    /**
     * @notice Generate a pseudo-random Noun seed using the previous blockhash and noun ID.
     */
    // prettier-ignore
    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
            )
        });
}
```
:::

**概要**
前のブロックハッシュと**Nouns**のIDを使用して疑似乱数のシードを生成する関数。

**詳細**
NFTの生成プロセスで使用されます。
関数内部で、前のブロックのハッシュと**Nouns**のIDを元に疑似乱数のシードを生成します。
このシードは後続の処理でランダムな要素を選ぶための基準として使用されます。

**引数 or パラメータ**
- `nounId`
	- 生成する**Nouns**NFTのID。
- `descriptor`
	- NFTの要素の数を提供するためのインターフェース。

**戻り値**
- `Seed`
	- シード生成の結果として、各要素の選択に使用されるランダムな値を格納した構造体。
	- この構造体は、生成されるNFTの外見を決定する要素をランダムに選ぶための情報を持っています。

# イベント

なし。

# コード

## NounsSeeder.sol

:::details NounsSeeder.sol
```solidity
// SPDX-License-Identifier: GPL-3.0

/// @title The NounsToken pseudo-random seed generator

/*********************************
 * ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ *
 * ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ *
 * ░░░░░░█████████░░█████████░░░ *
 * ░░░░░░██░░░████░░██░░░████░░░ *
 * ░░██████░░░████████░░░████░░░ *
 * ░░██░░██░░░████░░██░░░████░░░ *
 * ░░██░░██░░░████░░██░░░████░░░ *
 * ░░░░░░█████████░░█████████░░░ *
 * ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ *
 * ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ *
 *********************************/

pragma solidity ^0.8.6;

import { INounsSeeder } from './interfaces/INounsSeeder.sol';
import { INounsDescriptor } from './interfaces/INounsDescriptor.sol';

contract NounsSeeder is INounsSeeder {
    /**
     * @notice Generate a pseudo-random Noun seed using the previous blockhash and noun ID.
     */
    // prettier-ignore
    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
            )
        });
    }
}
```
:::

## INounsSeeder.sol

:::details INounsSeeder.sol
```solidity
// SPDX-License-Identifier: GPL-3.0

/// @title Interface for NounsSeeder

/*********************************
 * ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ *
 * ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ *
 * ░░░░░░█████████░░█████████░░░ *
 * ░░░░░░██░░░████░░██░░░████░░░ *
 * ░░██████░░░████████░░░████░░░ *
 * ░░██░░██░░░████░░██░░░████░░░ *
 * ░░██░░██░░░████░░██░░░████░░░ *
 * ░░░░░░█████████░░█████████░░░ *
 * ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ *
 * ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ *
 *********************************/

pragma solidity ^0.8.6;

import { INounsDescriptor } from './INounsDescriptor.sol';

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

    function generateSeed(uint256 nounId, INounsDescriptor descriptor) external view returns (Seed memory);
}
```
:::

## INounsDescriptor.sol

:::details INounsDescriptor.sol
```solidity
// SPDX-License-Identifier: GPL-3.0

/// @title Interface for NounsDescriptor

/*********************************
 * ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ *
 * ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ *
 * ░░░░░░█████████░░█████████░░░ *
 * ░░░░░░██░░░████░░██░░░████░░░ *
 * ░░██████░░░████████░░░████░░░ *
 * ░░██░░██░░░████░░██░░░████░░░ *
 * ░░██░░██░░░████░░██░░░████░░░ *
 * ░░░░░░█████████░░█████████░░░ *
 * ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ *
 * ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ *
 *********************************/

pragma solidity ^0.8.6;

import { INounsSeeder } from './INounsSeeder.sol';

interface INounsDescriptor {
    event PartsLocked();

    event DataURIToggled(bool enabled);

    event BaseURIUpdated(string baseURI);

    function arePartsLocked() external returns (bool);

    function isDataURIEnabled() external returns (bool);

    function baseURI() external returns (string memory);

    function palettes(uint8 paletteIndex, uint256 colorIndex) external view returns (string memory);

    function backgrounds(uint256 index) external view returns (string memory);

    function bodies(uint256 index) external view returns (bytes memory);

    function accessories(uint256 index) external view returns (bytes memory);

    function heads(uint256 index) external view returns (bytes memory);

    function glasses(uint256 index) external view returns (bytes memory);

    function backgroundCount() external view returns (uint256);

    function bodyCount() external view returns (uint256);

    function accessoryCount() external view returns (uint256);

    function headCount() external view returns (uint256);

    function glassesCount() external view returns (uint256);

    function addManyColorsToPalette(uint8 paletteIndex, string[] calldata newColors) external;

    function addManyBackgrounds(string[] calldata backgrounds) external;

    function addManyBodies(bytes[] calldata bodies) external;

    function addManyAccessories(bytes[] calldata accessories) external;

    function addManyHeads(bytes[] calldata heads) external;

    function addManyGlasses(bytes[] calldata glasses) external;

    function addColorToPalette(uint8 paletteIndex, string calldata color) external;

    function addBackground(string calldata background) external;

    function addBody(bytes calldata body) external;

    function addAccessory(bytes calldata accessory) external;

    function addHead(bytes calldata head) external;

    function addGlasses(bytes calldata glasses) external;

    function lockParts() external;

    function toggleDataURIEnabled() external;

    function setBaseURI(string calldata baseURI) external;

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

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

    function genericDataURI(
        string calldata name,
        string calldata description,
        INounsSeeder.Seed memory seed
    ) external view returns (string memory);

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

# 最後に

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

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

https://chaldene.net/

https://qiita.com/cardene
