NFTを発行する独自コントラクトをInfuraのテストネット(Ropsten)にデプロイする

2022/01/27に公開

割と日本語のブロックチェーン関連の記事がないので、書いてみました。
この記事では、solidityで独自コントラクトを作成し、テストネットにデプロイまでを行います。

NFTとは

代替不可能なトークンのことです。
ERC721という規格で定められたinterfaceが実装されている=NFTと考えてもらって大丈夫です。
ちなみに代替可能トークンはFTって呼ばれます。

具体的には、以下のinterfaceで定義されているfunctionが実装されていればNFTです。

interface ERC721{
    event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);
    event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId);
    event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
    function balanceOf(address _owner) external view returns (uint256);
    function ownerOf(uint256 _tokenId) external view returns (address);
    function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) external payable;
    function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;
    function transferFrom(address _from, address _to, uint256 _tokenId) external payable;
    function approve(address _approved, uint256 _tokenId) external payable;
    function setApprovalForAll(address _operator, bool _approved) external;
    function getApproved(uint256 _tokenId) external view returns (address);
    function isApprovedForAll(address _owner, address _operator) external view returns (bool);
}

これ全部実装するのはキツいので、今回はopenzeppelinというツールを使って実装をしていきます。

必要なもの

  • Node
  • Infuraのアカウント
    • dappsの情報処理をイーサリアムブロックチェーン上で可能にするホスティングサービス。
    • https://infura.io/
      • 無料で使えます!

参考になるリンク

出来上がり

以下リポジトリにデプロイまで行ったコードを入れてあります。
https://github.com/tkyatg/hardhat-nft-example

プロジェクト作成

hardhatという、スマートコントラクを作るツールを使います。

有名なものだとtruffleなどがありますが
新しいバージョンのtruffleは不具合があったり、使いにくいなどが理由で
hardhatが流行っている印象があります。

以下コマンドを実行して空のプロジェクトを作成します。

npm init --yes
npm install --save-dev hardhat
npm install @openzeppelin/contracts web3
npx hardhat

hardhatのプロジェクト作成時、質問は基本yesで、プロジェクトタイプはCreate a basic sample projectを選びました。

スマートコントラクト実装

次にNFTを作成するスマコン実装をします。
contracts/Greeter.solを削除して、contracts/Nft.solを作成して、以下の内容を書きます。

//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";

contract Nft is ERC721, ERC721URIStorage, ERC721Enumerable, ReentrancyGuard {
    uint256 private latestTokenId = 1;

    constructor(string memory name, string memory symbol)
        ERC721(name, symbol)
    {}

    function mint(string memory _tokenURI) public {
        uint256 tokenId = latestTokenId;
        _mint(msg.sender, tokenId);
        _setTokenURI(tokenId, _tokenURI);
        latestTokenId++;
    }

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

    function _beforeTokenTransfer(
        address _from,
        address _to,
        uint256 _tokenId
    ) internal override(ERC721, ERC721Enumerable) {
        super._beforeTokenTransfer(_from, _to, _tokenId);
    }

    function supportsInterface(bytes4 _interfaceId)
        public
        view
        override(ERC721, ERC721Enumerable)
        returns (bool)
    {
        return super.supportsInterface(_interfaceId);
    }
}

openzeppelinERC721を継承することで、既にNFTと言っても過言ではないです。
(openzeppelinがNFTに必要なメソッドの実装までしてくれているためです。)
さらに便利なスマコンにするためにERC721URIStorageERC721Enumerable, ReentrancyGuardも継承します。

継承したERC721の内部実装が気になる方はGitで確認を。
https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol

今回はNFTを作成するファンクションmintを実装しています。
mint以外のfunctionは継承元を呼んでいるだけです。

本来であれば単体テストを書くべきですが、めんどくさいので今回はスキップ。

デプロイ用スクリプトを修正

scripts/sample-script.jsを削除して、scripts/deploy.jsを作成し、以下のように編集します。

const { ethers } = require("hardhat");

async function main() {
  const Nft = await ethers.getContractFactory("Nft");
  const nft = await Nft.deploy("name", "symbol");

  await nft.deployed();

  console.log("Nft deployed to:", nft.address);
}
main()
   .then(() => process.exit(0))
  .catch((error) => {
    console.error(error);
    process.exit(1);
  });

デプロイ用の環境作成

infuraアカウントを作成し、プロジェクト作成してください。
ethereumのテストネットであれば、上限はありますが無料で使えます。
https://infura.io/dashboard

プロジェクト作成すると、以下のような画面が出てきます。
image

ENDPOINTROPSTENに変えて、https://ropsten.infura.io/v3/...のリンクをコピーしておいてください。後で使います。

hardhat.config.jsの修正をして、デプロイ先をinfuraに向ける

metamaskprivate keyを取得して、hardhat.config.jsを以下のように書き換えます。

import { HardhatUserConfig } from "hardhat/config";

const config: HardhatUserConfig = {
  solidity: "0.8.4",
  networks: {
    ropsten: {
      url: "{先ほどinfuraで取得したURL}",
      accounts: ["{metamaskのプライベートキー}"],
    },
  },
};

export default config;

ちなみにmatamaskのプライベートキーは流失したら終わりです。
絶対にgitにはpushしないこと。

あと、infuraのurlはropstenに切り替えてから取得すること。
メインネットにするとメチェクチャ高くつきます。

テストネットにデプロイ

以下コマンドを実行するだけです。

$ npx hardhat run --network ropsten scripts/deploy.js
Nft deployed to: {デプロイ先のコントラクトアドレス}

少し地味ですが、infuraのコンソールのアクセスボリュームが増えます。
下のようになっていたらデプロイできている(多分)

image

最後に

次回はデプロイしたコントラクトにフロントアプリから接続してみたいと思います。

twitterのフォローお願いします!
https://twitter.com/takuya_develop

あと定期的に渋谷でブロックチェーンもくもく会やるので、ぜひ参加してください〜。
ブロックチェーンについて語り合いましょう!
https://block-chain.connpass.com/event/236396/

GitHubで編集を提案

Discussion