はじめに

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

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

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

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

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

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

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

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

概要

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を正確に識別し、ブロックチェーン上で見つけることができるようにします。

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

関連リンク

使い方

コントラクトの目標

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

solidity
bytes32 constant COPYRIGHT_CC0_1_0_UNIVERSAL_LICENSE = 0xa2010f343487d3f7618affe54f789f5487602331c0a8d03f49e9a7c547cf0499;

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

詳細 0xa2010f343487d3f7618affe54f789f5487602331c0a8d03f49e9a7c547cf0499という値は、Creative Commons CC0 1.0 パブリックドメイン宣言 のテキストのハッシュです。

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データを格納するための配列です。 異なるボディのデザインが格納されます。

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

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

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

戻り値

  • uint256
    • 利用可能なNoun背景の数。

bodyCount

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

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

戻り値

  • uint256
    • 利用可能なNoun体の数。

accessoryCount

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

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

戻り値

  • uint256
    • 利用可能なNounアクセサリの数。

headCount

詳細
solidity
function headCount() external view override returns (uint256) {
        return heads.length;
}

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

戻り値

  • uint256
    • 利用可能なNoun頭部の数。

glassesCount

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

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

戻り値

  • uint256
    • 利用可能なNoun眼鏡の数。

addManyColorsToPalette

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

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

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

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

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

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

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

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

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

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

引数

  • _background
    • 追加するNoun背景の文字列。

戻り値 なし


addBody

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

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

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

引数

  • _body
    • 追加するNounのボディーのバイト列。

戻り値 なし


addAccessory

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

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

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

引数

  • _accessory
    • 追加するNounアクセサリのバイト列。

戻り値 なし


addHead

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

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

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

引数

  • _head
    • 追加するNoun頭部のバイト列。

戻り値 なし


addGlasses

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

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

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

引数

  • _glasses
    • 追加するNoun眼鏡のバイト列。

戻り値 なし


lockParts

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

        emit PartsLocked();
}

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

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

戻り値 なし


toggleDataURIEnabled

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

        isDataURIEnabled = enabled;
        emit DataURIToggled(enabled);
}

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

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

戻り値 なし


setBaseURI

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

        emit BaseURIUpdated(_baseURI);
}

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

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

引数

  • _baseURI
    • ベースURI。

戻り値 なし


tokenURI

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

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

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

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

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

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

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

引数

  • _paletteIndex
    • パレットのインデックス。
  • _color
    • 追加する色。

戻り値 なし


_addBackground

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

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

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

引数

  • _background
    • 追加する背景。

戻り値 なし


_addBody

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

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

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

引数

  • _body
    • 追加するボディーのバイト列。

戻り値 なし


_addAccessory

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

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

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

引数

  • _accessory
    • 追加するアクセサリのバイト列。

戻り値 なし


_addHead

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

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

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

引数

  • _head
    • 追加する頭部のバイト列。

戻り値 なし


_addGlasses

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

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

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

引数

  • _glasses
    • 追加する眼鏡のバイト列。

戻り値 なし


_getPartsForSeed

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

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

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

パラメータ

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

constructTokenURI

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

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

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

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

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

パラメータ

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

ContentBounds

ContentBounds
solidity
struct ContentBounds {
        uint8 top;
        uint8 right;
        uint8 bottom;
        uint8 left;
}

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

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

パラメータ

  • top
    • コンテンツの上端位置。
  • right
    • コンテンツの右端位置。
  • bottom
    • コンテンツの下端位置。
  • left
    • コンテンツの左端位置。

Rect

Rect
solidity
struct Rect {
        uint8 length;
        uint8 colorIndex;
}

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

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

パラメータ

  • length
    • 矩形の長さ。
  • colorIndex
    • 矩形のカラーインデックス。

DecodedImage

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

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

パラメータ

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

generateSVG

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

_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

_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

_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

PartsLocked
solidity
event PartsLocked();

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


DataURIToggled

DataURIToggled
solidity
event DataURIToggled(bool enabled);

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

パラメータ

  • enabled
    • Data URIが有効になった場合はtrue、無効になった場合はfalse。

BaseURIUpdated

BaseURIUpdated
solidity
event BaseURIUpdated(string baseURI);

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

パラメータ

  • baseURI
    • 更新されたベースURIを表す文字列。

コード

インターフェース

  • /contracts/interfaces/

ライブラリ

  • /contracts/libs/

最後に

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

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