完整的Hardhat实践教程

3個實用項目助您入門

Hardhat是以太坊開發人員堆棧中最受歡迎的工具之一。 在本教程中,我們將學習如何使用 Hardhat 並了解其主要功能。 本教程將主要是動手操作; 我們將做以下項目:

**項目 1:**對於第一個項目,主要目的是對 Hardhat 的工作原理有一個大致的了解。 我們將創建一個智能合約,對其進行測試,將其部署在 Rinkeby 上,並在 Etherscan 上進行驗證。

**項目 2:**對於第二個項目,我們將重新創建 Parity hack。 以太坊歷史上最大的黑客攻擊之一。

項目 3:最後,我們將通過在我們的機器內部運行 Hardhat 網絡來與合約和 EOA 進行交互。

完成這 3 個項目後,您應該對 Hardhat 在實際操作中的工作原理有一個很好的了解。

這是 3 個項目的 GitHub 存儲庫

先決條件

為了遵循本教程,建議您具備以下知識:

  • 熟悉 Solidity

  • 熟悉以太坊(EVM)

  • 熟悉 JavaScript

  • 對區塊鏈基礎知識有很好的理解

  • 熟悉命令行

  • 熟悉單元測試

在開始之前,我想對其他非常好的工具大喊大叫:

  • Truffle Suite:使用 JavaScript 構建,由 Consensys 開發。它是以太坊上最早的開發環境之一,你可以在這裡找到它。

  • Brownie:如果你喜歡 Python,這就是你要走的路。你可以在這裡查看。Dapp 工具:這裡。

  • Foundry:Paradigm 團隊用 Rust 重寫的 Dapp 工具,你可以在這裡找到它。

開始吧!

什麼是Hardhat?

Hardhat 是一個用於編譯、測試、部署和調試以太坊軟件的開發環境。它可以幫助開發人員管理和自動化構建智能合約和 dApp 過程中固有的重複性任務,並圍繞此工作流程輕鬆引入更多功能。這意味著在核心上編譯、運行和測試智能合約。

Hardhat 將幫助您完成整個智能合約開發之旅。從最初的創建、測試、交互和部署。它對於測試已經部署的合約和創建“未來假設”也非常有幫助。

在部署合約之前,您將在下面找到一個簡單的圖表,其中包含一個非常基本的開發人員工作流程:

Hardhat 是開發人員旅程中這些步驟的絕佳工具,它將與您一路同行。讓我們進一步解開它們:

**智能合約創建/測試:**這是您對合約進行編碼的步驟。通常你在編寫智能合約和測試代碼之間存在共生關係,這是因為你需要測試每一段代碼。 Hardhat 非常擅長這一點,因為它提供了非常好的插件來測試和優化代碼。

**部署:**在此步驟中,您將代碼(將solidity 或vyper 轉換)代碼編譯為字節碼,對其進行優化並進行部署。 Hardhat 有很多不錯的插件,我們稍後會看到它們非常有用。

此外,使用 Hardhat,您可以重現過去的場景。例如,您可以告訴 Hardhat 回到過去並像我們在“x”日期一樣重新進行黑客攻擊,或者您想做的任何其他事情。這是通過分叉主網來完成的。我們將在第二個項目中回顧這個特性。

如您所見,Hardhat 為我們提供了許多不錯的功能,可以在以太坊(或 EVM 兼容鏈)中發揮作用。

Hardhat的架構

Hardhat 是圍繞任務和插件的概念設計的。 Hardhat 的大部分功能來自插件,作為開發人員,您可以自由選擇要使用的插件。 Hardhat 對您最終使用的工具沒有意見,但它帶有一些可以覆蓋的內置默認值。

插件 → 插件是 Hardhat 的支柱,它們是使用您在 Hardhat 配置中使用的相同配置 DSL 構建的。您可以在此處找到 Hardhat 插件的完整列表。

任務 → 任務是帶有一些相關元數據的 JavaScript 異步函數。 Hardhat 使用此元數據為您自動執行某些操作。處理參數解析、驗證和幫助消息。您在 Hardhat 中可以做的所有事情都被定義為一項任務。

您可以將插件視為向基礎層添加額外功能的可重用代碼段。這樣做的好處是,您既可以自己創建插件,也可以使用任何社區和/或 Hardhat 的插件。

Hardhat網絡

Hardhat 內置了 Hardhat Network,這是一個專為開發而設計的本地以太坊網絡節點。

以太坊的核心是一套所有客戶都必須遵守的規範。以太坊協議(即客戶端)有不同的實現,最常用的是 GETH(用 GO 編寫)。但也有其他用不同語言編寫的。重要的是,它們都必須遵循以太坊的規範。

在底層,Hardhat 使用 EVM 的 JS 實現來運行您的文件。這意味著您正在您的機器上運行 Ethereum JS。這就是當您在內部發送交易、測試和部署合約時,Hardhat 知道該做什麼的方式。

項目結構

您將在下面找到一個圖表,該圖表顯示了平均架構結構的外觀。 請記住,每個項目都是不同的,並且大小差異很大。 但這只是為了得到一個大致的了解。

simple-smart-contracts-project-structure

讓我們分析每個目錄:

contracts → 在這裡,您將擁有所有合同和派生合同。 這意味著,您創建的所有合約、接口、庫和抽象合約都將位於 contracts 文件夾下。 唯一的例外是您通過 npm 包導入其他合約。

deployments → 在部署下,您將擁有將合約部署到網絡的腳本。

test→ 所有的測試用例都在這個文件夾下。 如圖所示,最好將每個合同文件的測試分開。

hardhat.config.js→ Hardhat 的配置文件。

現在我們已經大致了解了 Hardhat 在理論上是如何工作的,讓我們從項目開始吧!

注意:我們將在 3 個項目中重複許多任務。 這樣做是為了增加練習。

項目 1. 創建、測試、部署和驗證簡單代幣合約

安裝和環境設置

通過項目中的本地安裝使用硬帽。 這樣您的環境將是可重現的,並且您將避免未來的版本衝突。

你應該已經安裝了節點,你可以通過運行來檢查:

node -v

如果您沒有安裝它,您可以在此處查看安裝過程。對於整個教程,我們將在 hardhat-tutorial 中創建所有項目。 所以對於第一個項目,我們將創建一個名為 project1 的目錄並從那裡開始工作。

運行以下命令:

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

安裝hardhat後,運行以下命令:

npx hardhat

您應該看到以下輸出:

選擇“創建一個空的 hardhat.config.js”選項。 這只會給我們一個空的安全帽配置文件,稍後我們將更詳細地查看它。

安裝完成後,安裝以下插件:

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

這些是hardhat最常用的插件之一,我們正在安裝 hardhat-ethers 和 hardhat-waffle。 Ethers 是一個與以太坊交互的庫,而 waffle 是一個測試智能合約的框架。

準備就緒後,打開您的 hardhat.config.js 文件並添加以下代碼:

hardhat.config.js

在這裡,我們只需要 hardhat-ethers 和 hardhat waffle,並告訴 hardhat 我們要使用 Solidity 的編譯器版本“0.8.8”。 稍後,我們將增加更多的複雜性並進入更多的細節。

一旦我們準備好基本配置,讓我們從有趣的部分開始。

對於第一個項目,我們將構建一個非常簡單的智能合約,對其進行測試,並將其部署在 Rinkeby 上。 這只是為了讓您初步了解創建、測試和部署合約的過程。

創建合同

我們需要做的第一件事是創建一個contracts目錄,如“simple-smart-contracts-project-structure”圖所示。

mkdir contracts

在合約內部,我們將創建一個名為 Token.sol 的文件。 請記住,最好將文件命名為與合同相同的名稱。

touch contracts/Token.sol

如果一切正確,你應該有這個文件夾結構:

在 Token 文件中,添加以下代碼:

這是一個非常簡單的代幣合約(不符合 ERC-20 標準),我們將所有初始供應提供給所有者。 同樣,這裡的目的是了解如何測試和部署合約。

測試合約

準備好 Token.sol 後,創建一個測試文件夾。 在文件夾中,創建一個名為 token.js 的文件:

mkdir test
touch test/token.js

這應該是您的文件結構:

在 token.js 文件中添加以下測試用例:

測試用例的描述應該是不言自明的。 但我們只是在測試 Token.sol 合約的基本功能。

準備就緒後,運行以下命令來測試該文件:

npx hardhat test

如果一切順利,您應該會看到所有測試用例都通過了:

npx hardhat test 是 Hardhat 中的一項全局任務,它基本上是說,去查看一個名為 test 的文件夾並檢查測試用例。

請記住,如果您更改文件夾的名稱,除非您指定位置,否則它將不起作用:npx hardhat test

當項目變得更大時,指定確切的位置也非常有用。 這是因為您不想一直測試所有內容,這非常耗時。

對於我們的案例,我們還可以像這樣測試文件:

npx hardhat test test/token.js

準備就緒後,讓我們在 Rinkeby 中部署合約。

部署合約

為了部署合約,我們首先需要對我們的配置文件進行一些更改。 在此之前,請安裝以下依賴項:

npm install dotenv

Dotenv 是一個零依賴模塊,它將環境變量從 .env 文件加載到 process.env 中。 在將代碼推送到 GitHub 或其他地方時,我們將使用 dotenv 來保護我們的私鑰安全。

安裝 dotenv 後,創建 .env 文件並添加以下變量:

您只需要添加您的私鑰和與 Infura、alchemy 或您想使用的任何提供商的 URL 連接(確保該帳戶中有一些 rinkeby eth)。

注意:一定要選擇 Rinkeby 網絡。

準備就緒後,在 hardhat.config.js 中進行以下更改:

hardhat.config.js

為了使用 dotenv,我們需要在頂層導入它 →

require(“dotenv”).config();。

之後,我們可以創建變量並通過 → process.env.VARIABLE_NAME 獲取值。 將變量名全部大寫是一種很好的做法。

我們還修改了模塊,我們添加了關鍵字“networks”,這是為了指定 Hardhat,我們要在哪個網絡中部署我們的合約,例如 rinkeby、ropsten、mainnet 等……後面是 URL(節點連接)和帳戶(部署合約的私鑰)。準備就緒後,我們需要創建一個部署目錄,然後是一個 deployToken.js 文件:

mkdir deployments
touch deployments/deployToken.js

在 deployToken.js 中添加以下代碼:

這是我們將用來部署我們的合約的腳本。

const initialSupply = ethers.utils.parseEther(“100000”); → 我們正在創建一個名為 initialSupply 的變量,其值為 100,000 * 10 ^18。

const [deployer] = await ethers.getSigners(); → 這是合約的部署者,即 .env 文件中提供的私鑰的地址。

const tokenFactory = await ethers.getContractFactory(“Token”); → 合約的 Ethers 抽象,以便部署它。

**const contract = await tokenFactory.deploy(initialSupply);**→ 這行代碼使用 initialSupply 作為構造函數參數來部署合約。當然,如果您沒有構造函數參數,那麼您必須將其留空。同樣,如果您有更多構造函數參數,則需要在此處提供所有參數。

準備好後,我們將編譯合約。請記住,以太坊虛擬機不知道什麼是可靠性,它不理解它。它只理解字節碼,機器可讀的代碼。

執行:

npx hardhat compile

您應該會看到兩個新目錄、artifacts和cache:

ABI(application binary interface)和字節碼等所有相關信息都將位於 artifacts/contracts/CONTRACT_NAME/CONTRACT_NAME.json 下。

對於我們的案例:artifacts/contracts/Token.sol/Token.json

檢查里面的文件,ABI 基本上就是我們可以和合約交互的方式。它具有函數的所有規範,例如參數、狀態可變性和名稱。在文件的底部,您還將找到合約的字節碼。

現在是最終將合約部署到 Rinkeby 的時候了,為此,我們需要告訴 Hardhat 運行腳本:npx hardhat run — network

對於我們的例子,運行:

npx hardhat run deployments/deployToken.js — network rinkeby

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

驗證合同

在 Etherscan 上驗證合約很重要,這樣人們就可以看到源代碼,這增強了信任和安全性。它增強了信任,因為人們可以看到他們正在與之交互的協議的來源。還有安全性,因為會有更多的眼球,所以時間越長,潛在的黑客攻擊的可能性就越小。

轉到 https://rinkeby.etherscan.io/ 並輸入剛剛部署的地址,然後單擊“合約”選項卡,您應該只看到字節碼:

Harhdat 使用插件 hardhat-etherscan 簡化了驗證源的過程。 我們需要做的第一件事是安裝插件:

npm install --save-dev @nomiclabs/hardhat-etherscan

安裝後,我們需要對配置文件進行一些調整:

為了驗證合約,您需要獲得一個 Etherscan api 密鑰。 擁有它後,請確保將其添加到 .env 文件中。

為了驗證合約,我們需要運行以下命令:npx hardhat verify — network <networkName> <contractAddress> <ConstructorArguments>.

npx hardhat verify --network rinkeby CONTRACT_ADDRESS "100000000000000000000000"

注意:將 CONTRACT_ADDRESS 替換為新創建的合約地址。

如果一切順利,它應該說“在 Etherscan 上成功驗證了合約 Token”。 如果我們現在看一下合約,我們應該會看到經過驗證的源代碼:

這就是第一個項目!

項目 2. 重新創建 Parity 錢包黑客

奇偶校驗黑客是以太坊中一個非常大且重要的黑客攻擊。攻擊者竊取了超過 150000 ETH。

我們要做的是回到過去(或者如果可以的話,回到以太坊的區塊長度)並充當黑客來竊取資金。在繼續之前,重要的是要了解出了什麼問題。

多重簽名錢包非常適合存儲大量資金和/或減輕一方風險。通常它們的工作方式是擁有一組所有者和一個門檻。閾值是執行給定交易所需的最小簽名。

無需過多介紹,這裡有一個實現合約或具有所有錢包功能的“單例”,以及一個部署代理合約的代理工廠,該代理合約將所有調用委託給實現合約。因此,當您創建一個新錢包時,它具有唯一的地址和存儲空間,但會將所有調用委託給實現合約。

好的,那麼出了什麼問題?

通常,當您擁有這種類型的架構時,您需要確保: 1)所有更改狀態的函數都受到保護(只有特定的一組人可以調用它們)。2) 設置合約的初始函數只能調用一次。

如果我們看到他們的代碼,initWallet 函數是開放給每個人調用的。這意味著您可以直接調用該函數,將您的地址添加為所有者,並控制錢包。

所以,黑客在發現漏洞後第一時間做的,就是尋找 Eth 數量最多的錢包。 對於這個例子,我們將回到區塊 4043802 並從特定錢包中獲取 82189 Eth。 你可以在這裡看到真實的交易。 以下是交易的快照:

在繼續之前,我們需要了解 Hardhat 的一些不錯的功能,我們將使用這些功能重新創建 hack:

**主網分叉:**您可以啟動一個分叉主網的 Hardhat Network 實例。這意味著它將模擬與主網相同的狀態,但它將作為本地開發網絡工作。這樣您就可以與已部署的協議進行交互並在本地測試複雜的交互。在分叉主網時,有一些非常好的功能:

  • 模擬帳戶→此功能使您可以像我們是給定帳戶的所有者一樣行事。對於我們的示例,我們將像黑客一樣行事。

  • 固定塊→ Hardhat 允許您指定塊號。這意味著鏈的狀態將就像我們在那個給定的塊中一樣。注意:要固定塊,您需要訪問存檔節點(Alchemy 提供此功能)。

對於我們的示例,我們將主要使用這些功能,但如果您想檢查所有這些功能,請轉到此處

讓我們繼續前進。

我們需要做的第一件事是建立我們的新項目。在 hardhat-tutorial 中,創建一個名為 project2 的新目錄。然後,讓我們進行基本設置。以下是命令,請務必在 hardhat-tutorial 中:

mkdir project2
cd project2
npm init -y
npm install --save-dev hardhat
npx hardhat

選擇“創建一個空的 hardhat.config.js”

然後安裝一些hardhat插件和 dotenv :

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

對於這個項目,我們實際上不會編寫任何智能合約。 這是因為我們扮演著“黑客”的角色,所以我們從我們的外部賬戶發送交易。

如前所述,為了分叉主網,我們需要一個存檔節點。 Alchemy 提供了這樣的東西,所以獲取一個 Alchemy URL 並確保選擇 Mainnet。準備好後,創建一個 dotenv 文件並添加 URL:

touch .env

然後在您的 hardhat.config.js 文件中添加以下代碼:

hardhat.config.js

如您所見,語法非常簡單,我們只需要告訴 Hardhat 我們正在分叉鏈,提供一個存檔節點和一個區塊號。 不強制提供區塊號,如果不提供,安全帽會分叉到最新狀態。

下一步是創建我們的測試用例,我們將在其中實現所有邏輯(黑客攻擊)。 繼續運行以下命令:

mkdir test
touch test/parityHack.js

在展示代碼之前,了解我們在做什麼非常重要。

  1. 我們要回到區塊號 4043801 → 實際的黑客攻擊是在區塊 4043802 中,但我們不能在那個區塊上這樣做,因為那是黑客耗盡所有資金的時候。

  2. 我們正在冒充黑客的賬號,地址如下:0xB3764761E297D6f121e79C32A65829Cd1dDb4D32

  3. 我們正在調用未受保護的 initWallet 函數,以便我們控制錢包。 這是錢包地址:0xBEc591De75b8699A3Ba52F073428822d0Bfc0D7e

  4. 我們正在將所有資金轉移到黑客的賬戶。

然後通過運行測試文件:

npx hardhat test

輸出:

如您所見,我們成功耗盡了錢包的所有資金!

項目 3. 使用Hardhat網絡

我們之前看到了安全帽網絡的快速定義。讓我們更深入一點:

Hardhat 內置了 Hardhat Network,這是一個專為開發而設計的本地以太坊網絡節點。它允許您部署合約、運行測試和調試代碼。

它作為進程內或獨立守護程序運行,為 JSON-RPC 和 WebSocket 請求提供服務。默認情況下,它會在收到的每筆交易中按順序挖掘一個塊,並且沒有延遲。如前所述,它由 ethereumjs/vm 支持。

此功能允許您使用外部擁有的帳戶,非常快速地部署智能合約並與之交互。

讓我們看看這是如何工作的。

注意:Hardhat 附帶 20 個確定性帳戶。確定性意味著 20 個帳戶對於使用 Hardhat 的每個人來說都是相同的。所有的私鑰都被洩露了。永遠不要向這些賬戶發送真實資金。它們僅用於測試目的。

首先,我們需要進行基本設置。使用所有基本配置創建一個名為 project3 的目錄:

確保位於根目錄中:

mkdir project3
cd project3
npm init -y
npm install --save-dev hardhat
npm install --save-dev @nomiclabs/hardhat-ethers
npx hardhat

選擇“創建一個空的 hardhat.config.js”。

然後,讓我們創建一個名為“Hello”的合約。 這個簡單的合約將只有一個返回“hello”的函數(這裡的目的是演示如何與 Hardhat 網絡交互)。 繼續在contracts目錄下創建Hello.sol:

mkdir contracts
touch contracts/Hello.sol

添加以下代碼,然後編譯合約:

npx hardhat compile

然後我們準備將合約部署到hardhat網絡。

您應該記住,我們需要創建一個部署腳本:

在運行腳本之前,我們需要讓網絡運行。

運行:

npx hardhat node

您應該會看到服務器在 http://127.0.0.1:8545/ 運行,這將是我們的主要端點,還會看到 Hardhat 的 20 個確定性帳戶。

npx hardhat node

為了部署合約,您需要保持鏈運行,因此打開另一個終端並運行:npx hardhat run — network localhost

npx hardhat run --network localhost deployments/deployHello.js

您應該在運行區塊鏈的終端中看到類似的輸出:

如您所見,通過在本地運行鏈,我們可以更“深入”地了解幕後發生的事情。

確保複製新合約的地址(它應該在另一個終端窗口上)。

準備就緒後,創建一個 main.js 文件:

touch main.js

在這裡,我們將只進行簡單的操作,例如顯示余額、進行交易以及與我們的 Hello 合約進行交互。

玩弄文件,獨立調用每個函數以查看輸出。

要運行文件:

node main.js

其他插件和任務

有很多非常好的插件、任務和功能。 不幸的是,我們無法涵蓋所有這些。 我將分享我認為最有用的以及我看到大型項目使用最多的那些:

Console.sol:Hardhat 允許您在智能合約內部控制日誌,這對於調試非常有用。 為此,您只需要導入 hardhat/console.sol。 如果我們回到我們的 Token 示例,它看起來像這樣:

如果你看到了,我們正在記錄構造函數中的總供應量。 這是運行測試文件時的輸出:

Typescript 集成:在開發大型項目時,通常希望使用強類型語言以減少錯誤。去這裡查看安裝要求。

驗證合約:正如我們在第一個示例中看到的,安全帽使驗證合約的源代碼變得非常簡單。如果你想深入挖掘,請到這裡

主網分叉:正如我們之前在第二個示例中看到的,主網分叉對於與已部署的協議進行交互和模擬鏈的狀態非常有用。例如,如果你有一個與 Uniswap 交互的合約,你可以分叉鏈並模擬交易。如果你想更深入,去這裡

**測試:**測試是 Dapp 開發過程中最重要的步驟之一。 Hardhat 提供了很多不錯的插件來使測試更好。一個開箱即用的插件組合是 ethers.js 和 waffle。如果您想了解更多信息,請前往此處

Gas Reporter:這個插件告訴你每個單元測試的gas使用量。去這裡了解更多詳情。

而已 !

Subscribe to Boyn
Receive the latest updates directly to your inbox.
Mint this entry as an NFT to add it to your collection.
Verification
This entry has been permanently stored onchain and signed by its creator.