Hardhat 開發框架 - Solidity開發教程連載

Decert.me 要連載教程了, 《Solidity 開發教程》 力求系統深入的介紹 Solidity 開發, 同時這是一套交互式教程,你可以實時的修改教程裏的合約代碼並運行。

本教程來自貢獻者 @Tiny熊,讓我們正式開始學習吧。

如果你已經是 Hardhat 的使用者,可以直接跳到文末,參與挑戰領取技能認證 NFT。


Hardhat 提供了一個靈活且易於使用的環境,可以輕鬆地編寫、測試和部署智能合約。類似的開發工具或框架還有: Remix IDETruffle Foundry, 目前最受歡迎的是 Hardhat 與 Foundry。

Hardhat 使用 Node 進行包管理,如果你熟悉 Node 及 Javascript, Hardhat 將非常簡單上手。

Hardhat還內置了Hardhat 網絡(Hardhat Node),它是爲開發而設計的本地以太坊網絡。 用來部署合約,運行測試和調試代碼

在本文中,我們將介紹:

  1. 創建及配置Hardhat項目
  2. 編寫智能合約
  3. Hardhat 編譯合約
  4. 使用 Ethers.js 和爲合約編寫自動化測試
  5. 使用 console.log()調試 Solidity
  6. 使用 Hardhat 部署合約
  7. 使用 Hardhat Etherscan 進行開源驗證。
  8. Hardhat 插件的使用

本文對應的代碼在:https://github.com/xilibi2003/training_camp_2/tree/main/w1_hardhat

創建及配置Hardhat項目

Hardhat 構建在Node.js之上, 使用 Hardhat 要求我們在電腦先安裝好Node.js (>= 16.0), 環境準備可以參考這裏

先創建項目目錄:

mkdir hardhat-tutorial
cd hardhat-tutorial

初始化 Node 項目:

npm init

安裝 Hardhat :

npm install --save-dev hardhat

在安裝Hardhat的目錄下運行:

npx hardhat

使用鍵盤選擇"創建一個新的hardhat.config.js(Create a JavaScript project)" ,然後回車。

$ 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.13.0 👷‍

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

這個 JavaScript Hardhat 工程會默認下載 hardhat-toolbox 插件及一些常規設置:

創建好的Hardhat工程包含文件有:

  • contracts:智能合約目錄
  • scripts :部署腳本文件
  • test:智能合約測試用例文件夾。
  • hardhat.config.js:配置文件,配置hardhat連接的網絡及編譯選項。

編寫合約

合約開發推薦使用 VSCode 編輯器 + solidity 插件,在contracts 下新建一個合約文件 Counter.sol (*.sol 是 Solidity 合約文件的後綴名), 複製如下代碼:

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Counter {
    uint counter;

    constructor() {
        counter = 0;
    }

    function count() public {
        counter = counter + 1;
    }

    function get() public view returns (uint) {
        return counter;
    }
}

接下來就可以編譯這個合約了。

使用OpenZepplin 等第三方庫

在編寫合約時,儘量不要重複造輪子,基於優質開源的第三方庫,不僅可以提交效率,還可以讓我們的合約代碼更安全,例如要開發一個 Token,可以用npm 安裝OpenZepplin 庫:

npm install @openzeppelin/contracts --save-dev

然後在合約中 import 相應庫中的合約文件及可。

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract Token is ERC20 {
  constructor(uint256 initialSupply) ERC20("Token Name", "Token Symbol") {
    _mint(msg.sender, initialSupply);
  }
}

編譯合約

hardhat.config.js 有默認的Solidity 編譯器配置:

require("@nomicfoundation/hardhat-toolbox");

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

因此我們直接編譯合約即可,在終端中運行 npx hardhat compilecompile任務是內置任務之一。

$ npx hardhat compile
Compiling 1 file with 0.8.18
Compilation finished successfully

合約已成功編譯了。

成功編譯後,會在 artifacts/contracts/ 目錄下生成Counter.json 和 build-info, Counter.json包含了智能合約的 ABI 、字節碼(Bytecode)等。

:::tip

智能合約的 ABI(Application Binary Interface)信息,其中包括了合約的函數、事件等接口信息。這個文件通常會在與其他合約交互時使用,因爲它可以被其他合約和 DApp 使用。

Bytecode 是部署合約所需的字節碼(也稱爲創建時字節碼),部署合約時,就是把該字節碼作爲交易的輸入數據發送鏈上。:::

:::

編寫測試用例

爲智能合約編寫自動化測試至關重要,因爲事關用戶資金。

在我們的測試中,使用 Harhdat 內置的網絡,使用ethers.js與前面的合約進行交互,並使用 Mocha 作爲測試運行器。

在項目 test下,並創建一個名爲Counter.js的新文件:

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

let counter;

describe("Counter", function () {
  async function init() {
    const [owner, otherAccount] = await ethers.getSigners();
    const Counter = await ethers.getContractFactory("Counter");
    counter = await Counter.deploy();
    await counter.deployed();
    console.log("counter:" + counter.address);
  }

  before(async function () {
    await init();
  });

  // 
  it("init equal 0", async function () {
    expect(await counter.get()).to.equal(0);
  });

  it("add 1 equal 1", async function () {
    let tx = await counter.count();
    await tx.wait();
    expect(await counter.get()).to.equal(1);
  });

});

在終端上運行npx hardhat test。 你應該看到以下輸出:

> npx hardhat test


  Counter
counter:0x5FbDB2315678afecb367f032d93F642f64180aa3
    ✔ init equal 0
    ✔ add 1 equal 1

  2 passing (1s)

這意味着測試通過了。 現在我們解釋主要代碼:

  const Counter = await ethers.getContractFactory("Counter");

ethers.js中的ContractFactory是用於部署新智能合約的抽象,因此此處的Counter是用來實例合約的工廠。

counter = await Counter.deploy();

ContractFactory上調用deploy()將啓動部署,並返回解析爲ContractPromise。 該對象包含了智能合約所有函數的方法。

let tx = await counter.count();
await tx.wait();

counter 上調用合約方法, 並等待交易執行完畢。

注意,默認情況下, ContractFactoryContract實例連接到第一個簽名者(Singer)

若需要使用其他的簽名這, 可以使用合約實例connect 到另一個簽名者, 如 counter.connect(otherAccount)

expect(await counter.get()).to.equal(0);

判斷相等,我們使用Chai,這是一個斷言庫。 這些斷言函數稱爲“匹配器”,在此實際上使用的“匹配器”來自Hardhat Chai Matchers

使用 Console.log 調試合約

在**Hardhat Node **節點上運行合約和測試時,你可以在Solidity代碼中調用console.log()打印日誌信息和合約變量,可以方便我們調試代碼。

在合約代碼中導入**Hardhat **的console.log就可以使用它。

pragma solidity ^0.8.0;

import "hardhat/console.sol";

contract Counter {
    uint public counter;

    constructor(uint x) {
        counter = x;
    }

    function count() public {
        counter = counter + 1;
        console.log("counter is %s ", counter);
    }

}

就像在JavaScript中使用一樣, 將一些console.log添加函數中,運行測試時,將輸出日誌記錄:

> npx hardhat test

  Counter
counter:0x5FbDB2315678afecb367f032d93F642f64180aa3
    ✔ init equal 0
counter is 1
    ✔ add 1 equal 1 (38ms)


  2 passing (1s)

可以在這裏瞭解更多 console.log 。

部署合約

其實我們在測試時, 合約已經部署到了Hardhat 內置的網絡上,部署合約我們需要編寫一個部署腳本。

scripts文件夾,新建一個deploy.js 用來寫部署腳本,部署腳本其實和前面測試時 init 函數類似:

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

async function main() {

   const Counter = await ethers.getContractFactory("Counter");
   const counter = await Counter.deploy();
   await counter.deployed();

  console.log("Counter address:", counter.address);
}

main();

運行 npx hardhat run scripts/deploy.js 時, 可以合約會部署到Hardhat 內置網絡上。

> npx hardhat run scripts/deploy.js
Counter address: 0x5FbDB2315678afecb367f032d93F642f64180aa3

爲了在運行任何任務時指示Hardhat連接到特定的EVM網絡,可以使用--network參數。 像這樣:

npx hardhat run scripts/deploy.js --network <network-name>

network-name 需要在 hardhat.config.js 文件中進行配置:

require("@nomicfoundation/hardhat-toolbox");

// 填入自己的私鑰或助記詞,
const PRIVATE_KEY1 = "0x.... YOUR PRIVATE KEY1";
const PRIVATE_KEY2 = "0x....  YOUR PRIVATE KEY1";
const Mnemonic = "YOUR Mnemonic";


module.exports = {
  solidity: "0.8.9", // solidity的編譯版本
  networks: {
    goerli: {
      url: "https://eth-goerli.api.onfinality.io/public",
      accounts: [PRIVATE_KEY1,PRIVATE_KEY2],
      chainId: 5,
    },
    
     mumbai: {
      url: "https://endpoints.omniatech.io/v1/matic/mumbai/public",
      accounts: {
        mnemonic: Mnemonic,
      },
      chainId: 80001,
    },
  }
};

以上配置了兩個網絡,一個是以太坊測試網 goerli, 一個是 Polygon 測試網mumbai, 我們可以在 https://chainlist.org 找到每個網絡的節點 URL 及 chainID。

在網絡配置中,需要提供提交交易賬號, 可以通過私鑰或助記詞 進行配置,這裏配置的賬號(需要提前充幣進入到賬號中),在hardhat 腳本中(測試及部署腳本)調用getSigners 即可獲得:

const [owner, otherAccount] = await ethers.getSigners();

一個私鑰對應一個Singer,助記詞則對應無數個 Singer , 爲每個項目生成一個獨立的賬號是比較推薦的做法,使用 ChainTool 開源工具 可以生成賬號。

:::tip

助記詞可以推導出無數了私鑰,可參考:BIP39

:::

另外要注意, 在 Goerli 上進行部署,需要將Goerli-ETH發送到將要進行部署的地址中。 可以從水龍頭免費或一些測試幣,這是Goerli的一個水龍頭:

最後運行:

npx hardhat run scripts/deploy.js --network goerli

如果一切順利,你應該看到已部署的合約地址。

代碼開源驗證

智能代碼開源會增加了合約的透明度和可靠性,是項目建立信任很重要的一個步驟。

hardhat-toolbox 工具箱裏,包含了 hardhat-etherscan 插件用於驗證已經部署到區塊鏈網絡上的智能合約代碼與源代碼是否匹配,在完成驗證後在區塊鏈瀏覽器中合約標籤上會出現✅, 如圖:

image-20230313104044517

在部署智能合約時,合約字節碼會被寫入到區塊鏈中,這意味着其他人無法檢查合約的源代碼。代碼驗證的過程是將已部署合約的字節碼與原始Solidity代碼再次編譯後與部署的字節碼進行比較,確保它們是一致的。

相比在區塊鏈瀏覽器上上傳代碼驗證, hardhat-etherscan 有很多優點,否則會自動使用 hardhat config 值設置的編譯器選項,並且當代碼中引用的第三方庫或合約, hardhat-etherscan 能自動探測並處理。

開源驗證的步驟是:

  1. 安裝 hardhat-toolboxhardhat-etherscan , 這一步我們這裏已經完成,因爲在初始化項目的時候安裝了 hardhat-toolbox , 如果沒有安裝,可以使用以下命令安裝

    npm install --save-dev @nomiclabs/hardhat-etherscan
    
  2. hardhat.config.js 中配置您的 Etherscan API 密鑰和網絡設置,例如:

  require("@nomicfoundation/hardhat-toolbox");
  或
  // require("@nomiclabs/hardhat-etherscan");
  
  etherscan: {
    apiKey: ""
  },
  
 

如何獲取 Etherscan API 密鑰?

  1. 訪問部署網絡對應主網的 Etherscan 網站,並註冊一個賬號(如果還沒有賬號的話)。
  2. 登錄你的賬號並進入 Etherscan 的「我的帳戶」頁面。
  3. 點擊頁面左側的「API-KEYs」標籤頁。
  4. 在頁面上方的「Create New API KEY」部分,輸入 API 密鑰的名稱和描述,然後選擇需要訪問的 API 權限。
  5. 點擊「Generate」按鈕來生成 API 密鑰。
  1. 執行驗證命令:

    npx hardhat verify <deployed-contract-address> "參數(若有)" --network <network-name> 
    

    例如,要在 goerli 網絡上驗證合約,可以運行以下命令:

    npx hardhat verify 0x..... --network goerli
    

該命令會爲我們上傳合約代碼並驗證其源代碼。如果一切順利(網絡順暢的話),在 Etherscan 上看到的合約被成功驗證。

Hardhat 插件(Plugin)的使用

上面代碼開源驗證時,使用了hardhat-etherscan插件,其實也可以使用 hardhat-verify 插件。

https://hardhat.org/hardhat-runner/plugins 鏈接可以找到當前使用較多的插件,例如:hardhat-gas-reporter 可以對部署合約及函數執行的Gas消耗給出報告;solidity-coverage 可以對測試覆蓋率給出報告。

要使用一個插件通常要:

  1. 用 Node.js 包管理先安裝相應的插件

  2. hardhat.config.js 文件中引入插件,以便Hardhat 能加載上對應的插件。

參考文檔

示例非常簡單, 更多使用方法,可參考文檔:

小結

本文介紹了 Hardhat 開發框架的一些基本概念和使用方法,瞭解瞭如何使用 Hardhat 進行合約編譯、部署、調試及測試,在開發中要經常查看文檔,瞭解更多Hardhat 用法。


Hardhat 的使用你掌握了嗎?來這裏挑戰一下看看,挑戰完成你就可以領取到一枚技能認證 NFT。

碼一個未來


原教程鏈接:https://decert.me/tutorial/solidity/tools/hardhat
Decert.me -- 碼一個未來

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章