こんにちは、エンジニアの黒岩(@kro96_xr)です。 バックエンドを中心にフロントエンドやらインフラやら色々担当しています。
前回自分の記事ではブラウザ上で動作するRemix IDEを使ってコントラクトの実装を試してみました。
今回はその続編として、Hardhatを使ったコントラクトのテスト周りについて書いていきたいと思います。
Hardhatとは
Hardhatとは公式サイトにあるOverviewの言葉を借りると「Ethereumソフトウェアのコンパイル、デプロイ、テスト、およびデバッグを行うための開発環境」とのことです。Ethereumソフトウェア=Solidityで実装されたコントラクトという感じでしょうか。
また、「Hardhatには、開発用に設計されたローカルなEthereumネットワークであるHardhat Networkが内蔵されている」ため、ローカルでのテストを行うことができます。
検証環境
- OS
- macOS Big Sur 11.4 (Apple M1)
- Node.js
- v16.13.2
- npm
- 8.1.2
インストール
新しいディレクトリを作りインストールします。詳しくは公式をご覧ください。
$ mkdir hardhat $ cd hardhat $ npm init -y $ npm install --save-dev hardhat
ついでにOpenZeppelinもインストールしておきます。
$ npm i @openzeppelin/contracts
プロジェクト作成
npx hardhat
を実行するとプロジェクトを作成できます。
$ npx hardhat 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 8888888888 8888b. 888d888 .d88888 88888b. 8888b. 888888 888 888 "88b 888P" d88" 888 888 "88b "88b 888 888 888 .d888888 888 888 888 888 888 .d888888 888 888 888 888 888 888 Y88b 888 888 888 888 888 Y88b. 888 888 "Y888888 888 "Y88888 888 888 "Y888888 "Y888 👷 Welcome to Hardhat v2.9.9 👷
作成時にプロジェクトの目的などを聞かれますので選択してEnterで進んでいきます。今回は全てデフォルトのままです。
? What do you want to do? … ❯ Create a basic sample project Create an advanced sample project Create an advanced sample project that uses TypeScript Create an empty hardhat.config.js Quit ? Hardhat project root: › /path/to/project/hardhat ? Do you want to add a .gitignore? (Y/n) › y ? Do you want to install this sample project's dependencies with npm (@nomiclabs/hardhat-waffle ethereum-waffle chai @nomiclabs/hardhat-ethers ethers)? (Y/n) › y
これでプロジェクトに必要なファイルが自動的に生成されます。
コントラクトの実装
コントラクトの実装はcontracts/
以下に行います。
今回は前回実装したコードを少し修正し、mint時に既存のトークン数や1回にmintできるトークン数をチェックするロジックを入れてみました。
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol"; contract KrocksNFT is ERC721, ERC721Enumerable, Ownable { // 定数 uint256 public constant MAX_SUPPLY = 10; uint256 public constant MAX_MINT_PER_TRANSACTION = 5; // コンストラクタ constructor() ERC721("Krocks NFT", "KRONFT") {} // mint時のロジック function mint(uint256 numberOfTokens) public payable { uint256 ts = totalSupply(); require( numberOfTokens <= MAX_MINT_PER_TRANSACTION, "Exceeded max token per transaction" ); require( ts + numberOfTokens <= MAX_SUPPLY, "Exceed max tokens" ); for (uint256 i = 0; i < numberOfTokens; i++) { _safeMint(msg.sender, ts + i); } } // BeforeTransfer function _beforeTokenTransfer( address from, address to, uint256 tokenId ) internal override(ERC721, ERC721Enumerable) { super._beforeTokenTransfer(from, to, tokenId); } // SupportInterface function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721, ERC721Enumerable) returns (bool) { return super.supportsInterface(interfaceId); } }
テストコードの実装
テストの実装はtests/
以下にJavaScriptで実装します。
先程実装したバリデーションに関してテストしてみます。
const { expect } = require("chai"); const { ethers } = require("hardhat"); describe("KrocksNFT", function () { let KrocksNFT, krocksNFTcontruct, addr1 beforeEach(async function () { ;[owner, addr1] = await ethers.getSigners() // デプロイ KrocksNFT = await ethers.getContractFactory("KrocksNFT"); krocksNFTcontruct = await KrocksNFT.deploy(); await krocksNFTcontruct.deployed(); }) describe("mint", function () { it('Should be reverted if exceeded max token purchase', async function () { await expect( // 1回で6個のトークンをmint krocksNFTcontruct.connect(addr1).mint(6), ).to.be.revertedWith('Exceeded max token per transaction') }) it('Should be reverted because the caller exceeds max token', async function () { //10個のトークンをmint for (let i = 0; i < 2; i++) { await krocksNFTcontruct.connect(addr1).mint(5) } // 11個目のトークンをmint await expect( krocksNFTcontruct.connect(addr1).mint(1), ).to.be.revertedWith('Exceed max total tokens') }) }) })
テストを実行
$ npx hardhat test KrocksNFT mint ✔ Should be reverted if exceeded max token purchase ✔ Should be reverted because the caller exceeds max token (71ms) 2 passing (619ms)
というわけで無事にテストが通りました。 コントラクトはデプロイ後の修正が出来ないのでしっかりテストしておきたいですね。
おわりに
以上、今回はHardhatを使ったコントラクトのテストについて書いてみました。
今回はローカルネットワークへのデプロイまでは行っていないので今後はその辺りについて書ければいいなと思っています。
参考
以下のサイトを参考にさせていただきました、ありがとうございます!
Hardhat | Ethereum development environment for professionals by Nomic Foundation
公式です。
GitHub - a3994288/erc-721-hardhat-test
やりたいことがドンピシャで実装されており、かなり簡易化して参考にさせてもらいました。
ちゃんと理解できるようにこれからも参考にさせていただきます。
Hardhatで始めるスマートコントラクト開発 | DevelopersIO
ちょうど書こうと思っていた内容が1週間前に公開されていました。いつもお世話になっております。