LoginSignup
33
28

More than 1 year has passed since last update.

シンプルなERC721のコントラクトをデプロイする手順

Last updated at Posted at 2021-04-22

BlockBaseのエンジニアの@suhara_pontaです

2021年に入ってからの流行でNFTを知っている人も増えましたが、実際EthereumでNFTなどのコントラクトを開発するときに、どのような手順が必要なのでしょうか。調べてみるとかなり古い情報に当たることが多いので、現時点でのシンプルなやり方を開発初心者の方向けに共有したいと思います。

前提

  • MacOS Big Sur (Intel)
  • Node.jsが入っている

コントラクト開発の環境構築

プロジェクトのディレクトリを作成して、packagesの中にfrontendとcontractsフォルダを作成する(monorepoにするため)
スクリーンショット 2021-04-20 21.01.42.png

  • contractsに移動してnpm init -y
  • hardhatの初期化

    • npx hardhat init スクリーンショット 2021-04-20 21.08.30.png
    • create a sample project (yesで他のnpm modulesも追加しておく)
    • インストールが終わるとフォルダの中に色々生えている スクリーンショット 2021-04-20 21.12.37.png
    • hardhatのテストを動かして正常にインストールできたか確認する yarn hardhat test
  • package.jsonのscripts部分を以下に変更

    • yarn test:テスト
    • yarn localchain:ローカル環境にブロックチェーンを立ち上げる
    • yarn deploy:ローカルチェーンにコントラクトをデプロイする
package.json
"scripts": {
    "test": "hardhat test",
    "localchain": "hardhat node",
    "deploy": "hardhat run scripts/sample-script.js --network localhost"
 },

※yarn localchainでチェーンを立ち上げている状態で、別のターミナルのタブを開き、yarn deployして試してみる

NFTコントラクトの開発

  • yarn add @openzeppelin/contracts でOpenZeppelinのコントラクトを使えるようになる
  • contract/contarcts内のGreeter.solをNFT.solにして内容を変更していく
NFT.sol
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;

import "hardhat/console.sol";
import "@openzeppelin/contracts/token/ERC721/presets/ERC721PresetMinterPauserAutoId.sol";

contract NFT is ERC721PresetMinterPauserAutoId {
  constructor() ERC721PresetMinterPauserAutoId("name", "symbol", "http://localhost:3000") {}
}

ERC721PresetMinterPauserAutoIdというpresetを継承して、name, symbol, tokenURIを固定で引数に渡しているコード

  • hardhat.config.tsのsolidityのバージョンをコントラクトのバージョンに合わせて変更する
hardhat.config.ts
module.exports = {
  solidity: "0.8.0",
};
  • testとscriptsも修正する
    • コントラクトがデプロイできているか(nameが一致しているか)
    • NFTをmintできるか
    • minter role以外がmintできないかをテストで確認している
sample-test.js
const { expect } = require("chai");

describe("NFT", function() {
  it("NFT basic test", async function() {
    const [signer, badSigner] = await ethers.getSigners();
    const NFT = await ethers.getContractFactory("NFT");
    const nft = await NFT.deploy();
    expect(await nft.name()).to.equal("name");
    await nft.mint(signer.address);
    expect(await nft.balanceOf(signer.address)).to.equal(1);
    await expect(nft.connect(badSigner).mint(signer.address)).to.revertedWith("ERC721PresetMinterPauserAutoId: must have minter role to mint")
  });
});
sample-script.js
const hre = require("hardhat");
async function main() {
  const NFT = await hre.ethers.getContractFactory("NFT");
  const nft = await NFT.deploy();
  await nft.deployed();
  console.log("Nft deployed to:", nft.address);
}
main()
  .then(() => process.exit(0))
  .catch(error => {
    console.error(error);
    process.exit(1);
  });

フロントエンド(本題ではないのでスキップしてもOK)

frontendディレクトリに移動してnpm init -y
yarn add -D serve

packages/frontend/package.json
"scripts": {
  "dev": "serve"
},

index.htmlを追加

index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>title</title>
    <link rel="stylesheet" href="style.css">
    <script src="https://cdn.ethers.io/lib/ethers-5.0.umd.min.js" type="text/javascript"></script>
    <script src="script.js"></script>
  </head>
  <body>
    <h1>workshop</h1>
    <p>test</p>
    <button id="test">Button</button>
  </body>
</html>

script.jsを追加

script.js
window.onload = async function() {
  const abi = [] // packages/contracts/artifacts/NFT.sol/NFT.jsonからコピペする
}
const provider = new ethers.providers.Web3Provider(window.ethereum);
const address = "0x5FbDB2315678afecb367f032d93F642f64180aa3"
const contract = new ethers.Contract(address, abi, provider);
document.getElementById("test").onclick = async () => {
  console.log(await contract.name())
}

abiはcontractをデプロイして生成されるもの(abiの配列全て)を利用する。
スクリーンショット 2021-05-17 9.49.02.png

yarn devでフロントを立ち上げて、テストボタンを押してcontractのnameがconsoleに表示されればフロントとの接続が成功したことがわかる

monorepo化

rootディレクトリに戻って npm init -y
package.jsonに追加

package.json
"private": true,
"workspaces": [
  "packages/**"
],

yarn add -D npm-run-all wait-on

package.json
"scripts": {
  "test": "yarn workspace contracts test",
  "dev": "run-p dev:*",
  "dev:run-localchain": "yarn workspace contracts localchain",
  "dev:deploy-contract-to-localchain": "wait-on http://localhost:8545 && yarn workspace contracts deploy",
  "dev:frontend": "wait-on http://localhost:8545 && yarn workspace frontend dev"
},

yarn devで立ち上がる

ローカルでの開発環境は以上で構築できる。

typescript化(スキップしてもOK)

tsを使いたいのでrootディレクトリでyarn add -D typescript ts-node ts-generator

rootディレクトリにtsconfig.jsonを作成して

tsconfig.json
{
  "compilerOptions": {
    "target": "es2018",
    "module": "commonjs",
    "strict": true,
    "esModuleInterop": true,
    "resolveJsonModule": true,
    "noImplicitAny": false
  }
}

contractsディレクトリにもtsconfig.jsonを作成して

packages/contracts/tsconfig.json
{
    "extends": "../../tsconfig.json",
    "include": ["./scripts", "./test", "arguments.js", "./deploy"],
    "files": ["./hardhat.config.ts"]
}

テスト環境へのデプロイ

hardhat-deployを使ってデプロイする

contractsディレクトリにnetworks.jsonを作成
infuraのエンドポイントが必要になるので作成してください https://infura.io/

contracts/networks.json
{
    "rinkeby": {
        "chainId": "4",
        "rpc": "https://rinkeby.infura.io/v3/{YOUR_OWN_PROJECT}",
    },
    "localhost": {
        "chainId": "31337",
        "rpc": "http://localhost:8545",
    }
}

contractsディレクトリで yarn add -D hardhat-deploy
hardhat.config.jsをhardhat.config.tsに変更

hardhat.config.ts
import "@nomiclabs/hardhat-waffle";
import "@nomiclabs/hardhat-ethers";
import "hardhat-deploy";

import network from "./networks.json";

const privateKey = process.env.PRIVATE_KEY || "0x0000000000000000000000000000000000000000000000000000000000000000"; // this is to avoid hardhat error

module.exports = {
  solidity: "0.8.0",
  namedAccounts: {
    deployer: 0,
  },
  networks: {
    hardhat: {
      blockGasLimit: 10000000,
    },
    rinkeby: {
      url: network.rinkeby.rpc,
      accounts: [privateKey],
    },
  }
};

deployフォルダを作成し、デプロイのスクリプトを書く

contracts/deploy/0_deployAuctionEngine.ts
import { HardhatRuntimeEnvironment } from 'hardhat/types';
import { DeployFunction } from 'hardhat-deploy/types';

const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
  const { deployments, getNamedAccounts } = hre;
  const { deploy } = deployments;
  const { deployer } = await getNamedAccounts();

  const { address } = await deploy('NFT', {
    from: deployer,
    args: [],
    log: true,
  });

};
export default func;
func.tags = ['NFT'];

package.jsonにscript追加

package.json
"scripts": {
    "test": "hardhat test",
    "localchain": "hardhat node",
    "deploy": "hardhat deploy --network localhost --reset",
    "deploy-rinkeby": "hardhat deploy --network rinkeby --reset"
},

※この時点でscripts/sample-script.jsは不要になるので削除してもOK

RinkebyのETHを持っている秘密鍵を用意する
ターミナルでexport PRIVATE_KEY="用意した秘密鍵"
contractsディレクトリでyarn deploy-rinkeby
スクリーンショット 2021-04-22 11.05.26.png
rinkebyのEtherscanでデプロイできているか確認できる
https://rinkeby.etherscan.io/address/0xa10f1134b2E1Cdb364C77A172458b8AADD2f7FB5

以上になります!

BlockBaseでは誰でも簡単に独自コントラクトのNFTを作成できるChocofactoryというサービスを提供しています。(ソースコードはOSSです)
Chocofactory:https://factory.chocomint.app/
github:https://github.com/chocomintapp/chocofactory

開発の様子などもDiscordで公開しているのでぜひご覧ください
https://discord.com/invite/7P3NChCxhM

33
28
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
33
28