22
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Hardhatのチュートリアルをやってみた

Last updated at Posted at 2022-02-11

Hardhatとは

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

公式:https://hardhat.org/
チュートリアル:https://hardhat.org/tutorial/

特徴

  • ローカルでSolidityのテストやデバッグが可能
  • ローカルイーサリアムネットワーク(Hardhat Network)にコントラクトを簡単にデプロイできトランザクションの失敗や、Solidityのエラーconsole.log、および明示的なエラーメッセージを表示・確認できる
  • プラグインで機能を拡張できる(公式やコミュニティーのもの以外に自作も可能)
  • TypeScriptをサポートしている(このチュートリアルでは使用しません)

プロジェクトの設定

※nodeのバージョンは>=12.0をインストールしておく

1. プロジェクトのディレクトリーを作成し、package.jsonを生成とhardhatのインストール

$ mkdir hardhat-tutorial
$ cd hardhat-tutorial
$ npm init -y
$ npm install --save-dev hardhat

2. プロジェクトルートでnpx hardhatを実行し、hardhat.config.jsを生成する。

$ 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.0.0

? What do you want to do? …
  Create a sample project
❯ Create an empty hardhat.config.js
  Quit

3. プラグインをインストールする。

各プラグインについては、ここを参照してください

npm install --save-dev @nomiclabs/hardhat-ethers ethers @nomiclabs/hardhat-waffle ethereum-waffle chai

hardhat-waffleを読み込む

hardhat.config.js
require("@nomiclabs/hardhat-waffle");

/**
 * @type import('hardhat/config').HardhatUserConfig
 */
module.exports = {
  solidity: "0.7.3",
};

スマートコントラクトの作成とコンパイル

スマートコントラクトやSolidityについては以下を参照してください
※Solidityの2022/02/11時点での最新版は0.8.11だが、このチュートリアルでは0.7.3を使用

contracts/Token.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.7.0;

contract Token {
    // トークン名と識別子
    string public name = "My Hardhat Token";
    string public symbol = "MHT";
        // トークンの量
    uint256 public totalSupply = 1000000;
        // コントラクトのオーナーのアドレス
    address public owner;
        // 各アカウント残高を保存しておく領域
    mapping(address => uint256) balances;

    // `constructor`はコントラクトの作成時に1回だけ実行されます。
    constructor() {
                // ここでの`msg.sender`はこのコントラクトをデプロイしたアドレス
        balances[msg.sender] = totalSupply;
        owner = msg.sender;
    }

        // 金額を転送する関数
    function transfer(address to, uint256 amount) external {
        require(balances[msg.sender] >= amount, "Not enough tokens");

        balances[msg.sender] -= amount;
        balances[to] += amount;
    }

        // アカウントのトークン残高を取得するための読み取り専用関数
    function balanceOf(address account) external view returns (uint256) {
        return balances[account];
    }
}

コントラクトをコンパイルする

$ npx hardhat compile

コントラクトのテスト

テストフレームワークはMocha、アサーションライブラリはChaiを使用

完全なテストコードは以下を参照

test/Token.js
const { ethers } = require("hardhat");
const { expect } = require("chai");

describe("Token contract", function () {
  let Token;
  let hardhatToken;
  let owner;
  let addr1;
  let addr2;
  let addrs;

  beforeEach(async function () {
    Token = await ethers.getContractFactory("Token");
    [owner, addr1, addr2, ...addrs] = await ethers.getSigners();

    hardhatToken = await Token.deploy();
  });

  describe("Deployment", function () {
    it("Should set the right owner", async function () {
      expect(await hardhatToken.owner()).to.equal(owner.address);
    });

    it("Should assign the total supply of tokens to the owner", async function () {
      const ownerBalance = await hardhatToken.balanceOf(owner.address);
      expect(await hardhatToken.totalSupply()).to.equal(ownerBalance);
    });
  });

  describe("Transactions", function () {
    it("Should transfer tokens between accounts", async function () {
      await hardhatToken.transfer(addr1.address, 50);
      const addr1Balance = await hardhatToken.balanceOf(addr1.address);
      expect(addr1Balance).to.equal(50);

      await hardhatToken.connect(addr1).transfer(addr2.address, 50);
      const addr2Balance = await hardhatToken.balanceOf(addr2.address);
      expect(addr2Balance).to.equal(50);
    });
    
    // ...
  });
});

テストの実行

$ npx hardhat test

Hardhat Networkでのデバッグ

hardhat/console.solをインポートすることでconsole.logを使用できる

contracts/Token.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.7.0;

import "hardhat/console.sol"; // 追加

contract Token {
  //...

   function transfer(address to, uint256 amount) external {
    console.log("Sender balance is %s tokens", balances[msg.sender]);
    console.log("Trying to send %s tokens to %s", amount, to);
        //...
  }

  //...
}

テストを実行すると、ログ出力が表示されます。

$ npx hardhat test

  Token contract
    Deployment
      ✓ Should set the right owner
      ✓ Should assign the total supply of tokens to the owner
    Transactions
Sender balance is 1000 tokens
Trying to send 50 tokens to 0xead9c93b79ae7c1591b1fb5323bd777e86e150d4
Sender balance is 50 tokens
Trying to send 50 tokens to 0xe5904695748fe4a84b40b3fc79de2277660bd1d3

# ...

ローカルネットワークでテストする場合

$ npx hardhat node
$ npx hardhat test --network localhost

ライブネットワークへのデプロイ

テストネットへのデプロイの前に、ライブネットワークにデプロイしてみる

scripts/deploy.js
async function main() {
  const [deployer] = await ethers.getSigners();

  console.log("Deploying contracts with the account:", deployer.address);

  console.log("Account balance:", (await deployer.getBalance()).toString());

  const Token = await ethers.getContractFactory("Token");
  const token = await Token.deploy();

  console.log("Token address:", token.address);
}

main()
  .then(() => process.exit(0))
  .catch((error) => {
    console.error(error);
    process.exit(1);
  });

デプロイコードが機能することをテストする

$ npx hardhat run scripts/deploy.js
Deploying contracts with the account: 0xf39...
Account balance: 10000000000000000000000
Token address: 0x5FbD....

リモートネットワークへのデプロイ

Alchemyを使用して、イーサリアムのテストネットRopstenへデプロイします。

テストネットには Ropsten, Kovan, Rinkeby, Goerliなどがある。

Alchemyとは

Alchemy(アルケミー)は、開発者がブロックチェーンアプリケーションを簡単かつ効率的に開発するための必要なツールを提供しているサービス

APIキーの取得まで
スクリーンショット_2022-02-12_1_35_48.png

デプロイを行う際はガス代が必要なので、MetamaskにETH(ropsten-ETH)が必要です。
テストネット用のETHを配布するサービスであるfaucetからETHを無料で入手できます。

テストネットワークやテストネットワーク用ETHについては以下を参考

※自分の環境だとMetamask Ether FaucetにMetamaskを接続してもエラーとなりETHが受け取れなかったため、Ropsten Ethereum FaucetでETHを受け取りました。(受け取りに1日ぐらいかかりました。。。)

hardhat.config.js
require("@nomiclabs/hardhat-waffle");

// alchemyのAPIキー
const ALCHEMY_API_KEY = "KEY";


// Repstenネットワークを設定しているMetamaskアカウントの秘密鍵
const ROPSTEN_PRIVATE_KEY = "YOUR ROPSTEN PRIVATE KEY";

/**
 * @type import('hardhat/config').HardhatUserConfig
 */
module.exports = {
  solidity: "0.7.3",
  // 以下を追加
  networks: {
    ropsten: {
      url: `https://eth-ropsten.alchemyapi.io/v2/${ALCHEMY_API_KEY}`,
      accounts: [`${ROPSTEN_PRIVATE_KEY}`]
    }
  }
};

デプロイが成功するとコントラクトのアドレスが表示されます。

$ npx hardhat run scripts/deploy.js --network ropsten
Deploying contracts with the account: 0x2af....
Account balance: 1289165500634905903
Token address: 0x2b....

最後に

最初は全然ropsten-ETHが取得できなくて心が折れそうになりました><
デプロイはあっさりできたので、今度はDefiやNFTとか作ってデプロイしようと思います!
(多分Alchemyがすごいだけ。。。)

今回のチュートリアルのコード
https://github.com/koffe0522/hardhat-tutorial

リンク

公式

引用・参考

22
10
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
22
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?