独自コントラクトのNFT発行してみた

こんにちは、次世代デジタル基盤開発事業部の近藤です。

今回の内容

独自コントラクトのNFTを発行してみようと思います。

OpenZeppelinというスマートコントラクト用のライブラリとHardhatという開発環境を使ってスマートコントラクトの開発を行います。

そのスマートコントラクト使ってNFTを発行してOpenSeaテストネットで確認するところまでやっていきます。

OpenSeaテストネットはRinkebyネットワークに対応しているので今回はそちらで説明します。

OpenZeppelinとは

スマートコントラクト⽤のライブラリです。

今回はその中でERC721トークン用のものを使用します。 openzeppelin.com

Hardhatとは

Ethereumソフトウェアをコンパイル、デプロイ、テスト、およびデバッグするための開発環境です。 hardhat.org

事前準備

Node.jsとyarnのインストールが必要です。

インストールしていない方は事前にインストールしてください。

またMetaMask等のウォレットもセットアップしていない場合も事前にセットアップしてください。

使用バージョン

node 14.17.0
yarn 1.22.4
solidity 0.8.7
OpenZeppelin 4.5.0
Hardhat 2.9.3

環境構築

まず以下のコマンドでyarnのプロジェクトを作成します。

package.jsonが作成されます。

yarn init

続いて以下のコマンドでOpenZeppelinとHardhatをインストールしましょう。

OpenZeppelinのインストール

yarn add @openzeppelin/contracts

Hardhatのインストール

yarn add hardhat

Hardhatのインストールまで完了したらHardhatのプロジェクトを作成します。

yarn -s run hardhat

「Create a basic sample project」を選択すれば問題ないでしょう。

Project createdとなればOKです。

メタデータの作成

スマートコントラクトを実装する前にメタデータを作成します。

NFTはメタデータ自体はブロックチェーンで持たずに外部にjsonファイルを保存しておき、それを参照するのが一般的です。

メタデータの形式はOpenSeaのドキュメントにも例があがっているのでこちらを参考に作成しましょう。

この形式に則ったメタデータを作っておけばOpenSea上で良い感じにNFTが表示されます。

以下のような形式になると思います。attributesは何も設定しなくても問題ありません。

{
 "description": "this is test token",
 "image": "https://imageexample.com",
 "name": "test NFT",
 "attributes": []
}

provider URLの入手

チェーンに対してデプロイする際にはprovider URLが必要となります。

今回はAlchemyというサービスから入手します。

以下のサイトにアクセスします。 www.alchemy.com

アクセスしたら、新規登録し任意のプロジェクトを作成します。

この時CHAINはEthereum、NETWORKはRinkebyを指定してプロジェクトを作成してください。

作成したら、VIEW KEYの中のHTTPのURLをメモっておきましょう。

hardhat.config.jsの設定

今回はRinkebyネットワーク上にデプロイするので、hardhat.config.jsをそれにあわせて設定していきます。

hardhat.config.jsを修正し、以下のようにRinkebyネットワークの設定を追加してください。

require("@nomiclabs/hardhat-waffle");

// This is a sample Hardhat task. To learn how to create your own go to
// https://hardhat.org/guides/create-task.html
task("accounts", "Prints the list of accounts", async (taskArgs, hre) => {
  const accounts = await hre.ethers.getSigners();

  for (const account of accounts) {
    console.log(account.address);
  }
});

// You need to export an object to set up your config
// Go to https://hardhat.org/config/ to learn more

/**
 * @type import('hardhat/config').HardhatUserConfig
 */
module.exports = {
  solidity: "0.8.7",
  networks: {
    hardhat: {},
    rinkeby: {
      url: Alchemyで入手したURL,
      chainId: 4,
      accounts: [ 秘密鍵 ]
    },
  },
};

スマートコントラクトを作成する

では、実際にスマートコントラクトを作成していきましょう。

contractsフォルダの下に、sampleNft.solを作成してスマートコントラクトを実装していきます。

OpenZeppelinのライブラリを利用すれば簡単に実装可能です。

pragma solidity ^0.8.7;
 
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
 
contract sampleNFT is ERC721, Ownable {
    constructor() ERC721("my test token", "MTT") {}
 
    function _baseURI() internal view virtual override returns (string memory) {
        return "https://example.com";
    }
 
    function mint(address to, uint256 tokenId) public onlyOwner {
      _mint(to, tokenId);
    }
}

トークン名(ここではmy test tokenとしてます)とトークンシンボル(ここではMTTとしてます)は好きなものを入力してください。

_baseURIの返り値には先程作成したメタデータの保存先を設定します。

また、今回はtokenIdを指定してNFTを発行出来るようにmintメソッドにはtokenIdも追加しています。

faucetsでRinkeby用のETHを受け取る

デプロイやミントを行うためにはガス代が必要なので、テストネット用のETHを用意する必要があります。

以下のサイトにアクセスして、Rinkeby用のETHを貰いましょう。 faucets.chain.link

NetworkはEthereum Rinkebyを選択します。

右上のConnect walletで自分のウォレットを接続します。

今回はLINKは不要なのでRequest typeのリンクの方のチェックは外しましょう。

Send requestを押せば数分でウォレットにETHが送られてきます。

デプロイ用スクリプトの作成

先程作成したスマートコントラクトを取得してデプロイするシンプルなスクリプトになってます。

以下のスクリプトをdeploy.jsとして、scriptsフォルダの下に作成してください。

デプロイが完了したら、コントラクトアドレスがログ出力されます。

const hardhat = require("hardhat");
 
async function main() {
  const contract = await hardhat.ethers.getContractFactory("testNFT");
  const nft = await contract.deploy();
  await nft.deployed();
  console.log("Deploy to:", nft.address);
}
 
main()
  .then(() => process.exit(0))
  .catch((error) => {
    console.error(error);
    process.exit(1);
  });

Hardhatの機能を使ってデプロイします。

以下のコマンドでデプロイしましょう。

今回はRinkebyネットワーク上にデプロイしたいので、networkはRinkebyを指定しています。

yarn -s run hardhat run --network rinkeby scripts/deploy.js

コントラクトアドレスがログ出力されれば、無事デプロイ完了です。

コントラクトアドレスは後ほど使用するのでメモっておきましょう。

NFT発行用のスクリプトの作成

続いて、NFTを発行するスクリプトを作成していきます。

スクリプトのパラメータで発行するトークンIDを指定したいので、Hardhatではなくnodeでスクリプトを実行します。(Hardhatだとスクリプト実行時にパラメータを渡すのが面倒なため)

以下のスクリプトをmint.jsとして、scriptsフォルダの下に作成してください。

const Web3 = require("web3");
 
async function mintNFT() {
  const web3 = new Web3("Alchemyで入手したURL");
  const contract = require("../artifacts/contracts/sampleNft.sol/NFT.json");
 
  const nftContract = new web3.eth.Contract(contract.abi, contractAddress);
 
  const tx = {
    from: "ウォレットアドレス",
    to: "コントラクトアドレス",
    gas: 500000,
    data: nftContract.methods.mintMtt("ウォレットアドレス", Number(process.argv[2])).encodeABI(),
  };
  const signPromise = web3.eth.accounts.signTransaction(tx, "秘密鍵");
  signPromise
    .then((signedTx) => {
      const tx = signedTx.rawTransaction;
      if (tx !== undefined) {
        web3.eth.sendSignedTransaction(tx, function (err, hash) {
          if (!err) {
            console.log("Transaction hash is: ", hash);
          } else {
            console.log(
              "Something went wrong when submitting your transaction:",
              err
            );
          }
        });
      }
    })
    .catch((err) => {
      console.log("Promise failed:", err);
    });
}
 
mintNFT();

作成したら以下のコマンドでNFTを発行してみましょう。トークンIDは0以上の整数を指定すればOKです。

node scripts/mint.js <トークンID>

トランザクションハッシュがログ出力されるので、トランザクションの状態をRinkebyのEtherscanでも確認できます。

無事発行が成功したら、OpenSeaテストネット上で確認へ進みましょう。

発行したNFTをOpenSeaテストネット上で確認

発行したNFTをOpenSeaテストネットで確認してみます。

今回はRinkebyネットワーク上で発行したので、OpenSeaテストネットで確認することができます。

以下のサイトにアクセスします。 testnets.opensea.io

アクセスしたら、NFTを発行したウォレットを接続してログインしましょう。

右上のプロフィールアイコンのメニューからProfileページにアクセスします。

Collectedに先程発行したNFTが表示されていれば成功です!お疲れ様でした。

おわりに

予想以上に簡単に独自コントラクトのNFTを発行できて驚きました。

慣れてきたらもうすこし複雑なNFTなども作ってみたいと思います。

最後まで読んでいただきありがとうございました!

www.tecotec.co.jp

参考にしたサイト

note.com cam-inc.co.jp zenn.dev