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

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

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


### 特徴

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

## プロジェクトの設定
※nodeのバージョンは`>=12.0`をインストールしておく

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

```bash
$ 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. プラグインをインストールする。  
各プラグインについては、[ここ](https://hardhat.org/plugins/)を参照してください

```bash
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`を使用

- [スマートコントラクトとは](https://ethereum.org/en/learn/#smart-contracts)
- [Solidity v0.7.3](https://docs.soliditylang.org/en/v0.7.3/structure-of-a-contract.html)
- [Solidity v0.8.11](https://docs.soliditylang.org/en/v0.8.11/structure-of-a-contract.html)


```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];
    }
}
```

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

```bash
$ npx hardhat compile
```

## コントラクトのテスト

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

- [Mocha](https://mochajs.org/)
- [Chai](https://www.chaijs.com/)

完全なテストコードは以下を参照
- [full-coverage](https://hardhat.org/tutorial/testing-contracts.html#full-coverage)

```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);
    });
    
    // ...
  });
});
```

テストの実行

```bash
$ npx hardhat test
```

## Hardhat Networkでのデバッグ

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

- [console.log](https://hardhat.org/hardhat-network/#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);
　　　　　　　　//...
　　}

　　//...
}
```

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

```bash
$ 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

# ...
```

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

```bash
$ 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);
  });
```

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

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

## リモートネットワークへのデプロイ
[Alchemy](https://www.alchemy.com/)を使用して、イーサリアムのテストネット`Ropsten`へデプロイします。
> テストネットには　Ropsten, Kovan, Rinkeby, Goerliなどがある。

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

APIキーの取得まで
![スクリーンショット_2022-02-12_1_35_48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272185/3b6e0343-fa8c-1ecc-a7f6-bf17e51e43ca.png)


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

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

- [Ethereumのテストネットワーク](https://zenn.dev/kihonkei/articles/7750e99468a0ea)
- [テストネットワーク用ETHの入手方法](https://pprct.net/ethereum_testnet_howtouse/)

※自分の環境だと[Metamask Ether Faucet](https://faucet.metamask.io/)にMetamaskを接続してもエラーとなりETHが受け取れなかったため、[Ropsten Ethereum Faucet](https://faucet.ropsten.be/)で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}`]
    }
  }
};
```

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

```bash
$ 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

## リンク

公式

- [Hardhat](https://hardhat.org/)
- [Hardhat チュートリアル](https://hardhat.org/tutorial/)
- [Solidity v0.7.3](https://docs.soliditylang.org/en/v0.7.3/structure-of-a-contract.html)
- [Solidity v0.8.11](https://docs.soliditylang.org/en/v0.8.11/structure-of-a-contract.html)
- [Mocha](https://mochajs.org/)
- [Chai](https://www.chaijs.com/)

引用・参考

- [スマートコントラクトとは](https://ethereum.org/en/learn/#smart-contracts)
- [Alchemyとは](https://www.alchemy.com/)
- [Ethereumのテストネットワーク](https://zenn.dev/kihonkei/articles/7750e99468a0ea)
- [テストネットワーク用ETHの入手方法](https://pprct.net/ethereum_testnet_howtouse/)