👓

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

2023/08/31に公開

はじめに

初めまして。
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

今回使用する『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つ存在することを防ぎます。
このユニーク性は、ブロックチェーン上のデジタルアセットの価値を確保する重要な特徴です。

使い方

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

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

1. コントラクトのインポート

NounsSeederコントラクトをSolidityファイルにimportキーワードを使用してインポートします。

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

2. コントラクトのインスタンス化

NounsSeederコントラクトのインスタンスを作成します。
これには、デプロイされたコントラクトのアドレスが必要です。

NounsSeeder public seeder;

3. generateSeed関数の呼び出し

適切なパラメータでgenerateSeed関数を呼び出します。
この関数には2つのパラメータが必要です。

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

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

使用例

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

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);
    }
}

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

コントラクト

NounsSeeder

backgroundCount

uint256 backgroundCount = descriptor.backgroundCount();

概要
descriptorINounsDescriptorインターフェースのインスタンス)から取得される背景(background)の総数を表した変数。

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


bodyCount

uint256 bodyCount = descriptor.bodyCount();

概要
descriptorINounsDescriptorインターフェースのインスタンス)から取得されるボディ(body)の総数を表した変数。

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


accessoryCount

uint256 accessoryCount = descriptor.accessoryCount();

概要
descriptorINounsDescriptorインターフェースのインスタンス)から取得されるアクセサリー(accessory)の総数を表した変数。

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


headCount

uint256 headCount = descriptor.headCount();

概要
descriptorINounsDescriptorインターフェースのインスタンス)から取得されるヘッド(head)の総数を表した変数。

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


glassesCount

uint256 glassesCount = descriptor.glassesCount();

概要
descriptorINounsDescriptorインターフェースのインスタンス)から取得されるメガネ(glasses)の総数を表した変数。

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


generateSeed

generateSeed
// 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
    • 生成するNounsNFTのID。
  • descriptor
    • NFTの要素の数を提供するためのインターフェース。

戻り値

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

イベント

なし。

コード

NounsSeeder.sol

NounsSeeder.sol
// 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

INounsSeeder.sol
// 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

INounsDescriptor.sol
// 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

CryptoGames

Discussion