🗂

イーサリアム上で独自コントラクトでNFTを発行してみる

2022/08/06に公開

はじめに

イーサリアム上で独自コントラクトでNFT(Non-Fungible Token)を発行してみたいと思います。
NFTを発行すること自体はOpenSeaなどで簡単にできますが(いわゆる共有コントラクト)、共有コントラクトを使うと細かい挙動がカスタマイズできず、あまり面白くありません。そこで、今回はSolidityでスマートコントラクトを直接書いてNFTを発行してみたいと思います。

開発環境

  • Solidty
    イーサリアムのスマートコントラクト記述言語。
  • Remix
    Solidity(スマートコントラクトの記述言語)をコンパイル&デプロイするための開発環境。ブラウザベースなのでインストール不要。メタマスクとも接続できるため、コントラクトを直接デプロイすることができる。
  • OpenZeppelin
    Solidityで書かれたスマートコントラクトのライブラリを提供している。今回NFTの発行に使うERC721規格に必要なライブラリ群をインポートすることで簡単かつセキュアに使うことができる。逆に、独自コントラクトでも、OpenZeppelinなどのライブラリを使わずにERC721コントラクトを一から実装するのはセキュリティ的にやめたほうが良いと思います。
    特に、Contracts WizardというUIでコードの実装ができるツールを使います。

ERC721とは?

イーサリアムベースのNFTの標準規格。最大のNFTマーケットプレイスOpenSeaも準拠しています。これを拡張したERC1155などの新しい規格も出てきていますが、現時点(2022年6月)ではNFTの標準的な実装になっているようです。今回はERC721に準拠したコントラクトを実装することにします。

コントラクトの作成

OpenZeppelinのERC721実装を使用しますが、スクリプトの作成がGUIベースで簡単にできるContract Wizardというツールがあったので、こちらを使ってみます。
https://wizard.openzeppelin.com/#erc721
ERC721を選択し、各設定を指定していきます。

  • Name/Symbol
    NFTの名前になるので、好きな名前にします。
    ここでは、"Muku-tori"(=ムクドリ)というnameで、Symbolは"MUKU"としてみます。
  • Base URI
    NFTのメタデータ(画像など)を格納するベースのURIを指定します。後述しますが、今回はpinataという分散型ストレージにメタデータを保存するので、https://ipfs.io/ipfs/ を指定します。
  • FEATURES
    ERC721の必要な属性を指定します。ここでは以下の要素を指定しました。
    • Mintable
       ⇒コントラクトのデプロイ後にmintすることができる
    • Auto Increment Ids
      ⇒NFTを発行するたびにIDを+1する

できあがったスクリプトはこのようになります。

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Counters.sol";

contract MukuTori is ERC721, ERC721URIStorage, Ownable {
    using Counters for Counters.Counter;

    Counters.Counter private _tokenIdCounter;

    constructor() ERC721("Muku-tori", "MUKU") {}

    function _baseURI() internal pure override returns (string memory) {
        return "https://ipfs.io/ipfs/";
    }

    function safeMint(address to, string memory uri) public onlyOwner {
        uint256 tokenId = _tokenIdCounter.current();
        _tokenIdCounter.increment();
        _safeMint(to, tokenId);
        _setTokenURI(tokenId, uri);
    }

    // The following functions are overrides required by Solidity.

    function _burn(uint256 tokenId) internal override(ERC721, ERC721URIStorage) {
        super._burn(tokenId);
    }

    function tokenURI(uint256 tokenId)
        public
        view
        override(ERC721, ERC721URIStorage)
        returns (string memory)
    {
        return super.tokenURI(tokenId);
    }
}

OpenZeppelinが用意しているコントラクトを上書きする形になるので、最低限の記述になります。

コンパイル&デプロイ

Solidityコントラクトのコンパイルとデプロイは、Remixで行います。
Remixについては、以前書いたこちらの記事を参照ください。
https://zenn.dev/zzz/articles/368367ec20aa42#remixを使ったスマートコントラクトのコンパイル&デプロイ
今回は、Rinkebyテストネットにデプロイすることにします。Rinkebyは、OpenSeaもテストネットとして準拠しており、OpenSeaの表示も確認できます。

簡単に手順を記すと、
「Solidity Compiler」タブから、「Compile ERC721.sol」をクリックし、コンパイルします。次に、「DEPLOY & RUN TRANSACTIONS」タブから、デプロイを進めます。「ENVIRONMENT」から「Injected Provider - Metamask」を選択し、Metamask側からRinkebyテストネットを選択後、デプロイします。

メタデータのアップロード

NFTの構成要素に、URIや画像などのメタデータがあります。これらのメタデータはNFTのコンテンツそのものと認識されることが多いため、どこに置くかが重要な問題になります。画像などのメタデータは容量が大きいためethereumチェーン上に置くのは適していません(ものすごくコストがかかります)。そこで、チェーン外(=オフチェーン)に保存することが多いです。
とはいえ、AWSなどのサーバでホストすると管理者側が勝手に内容を変更できてしまうため、あまり良くないとされています。
そこでよく使われるのが、IPFS(InterPlanetary File System)です。IPFSはアドレスとコンテンツが結びついているため、内容を勝手に変更することができません。そのため、NFTの信憑性が増します。

今回は、Pinataというサービスを利用してIPFS上にNFTを保存します。
https://www.pinata.cloud/
登録してコンテンツをアップロードすれば、IPFS経由でアクセスできるようになります。
手順としては、以下のようになります。

手順1.画像をアップロード

試しにこの画像を使ってみます。

IPFSにアップロードすると、コンテンツのハッシュがCIDとして生成されます。そして、

https://ipfs.io/ipfs/[CID]

がアドレスとなります。例えば、この画像の場合、

CID : QmQCgWMbDC66fKJzotjmzh8ErWQtfTWRy6VTFixBMB7iGt
アドレス : https://ipfs.io/ipfs/QmQCgWMbDC66fKJzotjmzh8ErWQtfTWRy6VTFixBMB7iGt

となります。

手順2.JSONメタデータをアップロード

NFTには
下記の要領でJSONメタデータを作成し、同様にIPFSにアップロードします。
imageのURIとして先ほどアップロードした画像のアドレスを指定します。

0.json
{
  "name": "Muku-tori",
  "description": "Muku-tori NFT",
  "image": "https://ipfs.io/ipfs/QmQCgWMbDC66fKJzotjmzh8ErWQtfTWRy6VTFixBMB7iGt",
  "attributes": [
      {
          "value":10
      }
  ]
}

NFTの発行(ミント)

NFTの発行(ミント)は、デプロイしたコントラクトのsafeMintメソッドとやりとりすることで行います。
RemixのDeployed Contractsタブから、"safeMint"を選択し、"to"にオーナーのアドレス、"uriに"JsonファイルのCIDを指定します。そして、transactを押してトランザクションをネットワークに送付すれば、NFTが発行できます。

Contracts Wizardでスクリプトを作成したときに"Auto Increment Ids"を指定したので、発行するたびにtoken IDが0,1,2,...とインクリメントされます。

OpenSea

NFTがうまく発行されれば、OpenSeaから表示を確認することもできます。

まとめ

本記事では、独自コントラクトでNFTを発行し、OpenSeaで表示を確認するところまで行いました。上記のような単純なコントラクトであれば独自コントラクトで発行する必要もないのかもしれませんが、SolidityコントラクトをいろいろカスタマイズしてオリジナルのNFTを発行することができますので、その土台として独自コントラクト発行の方法を学んでおくと良いと思っています。

Discussion