Hardhat是以太坊開發人員堆棧中最受歡迎的工具之一。 在本教程中,我們將學習如何使用 Hardhat 並了解其主要功能。 本教程將主要是動手操作; 我們將做以下項目:
**項目 1:**對於第一個項目,主要目的是對 Hardhat 的工作原理有一個大致的了解。 我們將創建一個智能合約,對其進行測試,將其部署在 Rinkeby 上,並在 Etherscan 上進行驗證。
**項目 2:**對於第二個項目,我們將重新創建 Parity hack。 以太坊歷史上最大的黑客攻擊之一。
項目 3:最後,我們將通過在我們的機器內部運行 Hardhat 網絡來與合約和 EOA 進行交互。
完成這 3 個項目後,您應該對 Hardhat 在實際操作中的工作原理有一個很好的了解。
為了遵循本教程,建議您具備以下知識:
熟悉 Solidity
熟悉以太坊(EVM)
熟悉 JavaScript
對區塊鏈基礎知識有很好的理解
熟悉命令行
熟悉單元測試
在開始之前,我想對其他非常好的工具大喊大叫:
Truffle Suite:使用 JavaScript 構建,由 Consensys 開發。它是以太坊上最早的開發環境之一,你可以在這裡找到它。
Brownie:如果你喜歡 Python,這就是你要走的路。你可以在這裡查看。Dapp 工具:這裡。
Foundry:Paradigm 團隊用 Rust 重寫的 Dapp 工具,你可以在這裡找到它。
開始吧!
Hardhat 是一個用於編譯、測試、部署和調試以太坊軟件的開發環境。它可以幫助開發人員管理和自動化構建智能合約和 dApp 過程中固有的重複性任務,並圍繞此工作流程輕鬆引入更多功能。這意味著在核心上編譯、運行和測試智能合約。
Hardhat 將幫助您完成整個智能合約開發之旅。從最初的創建、測試、交互和部署。它對於測試已經部署的合約和創建“未來假設”也非常有幫助。
在部署合約之前,您將在下面找到一個簡單的圖表,其中包含一個非常基本的開發人員工作流程:
Hardhat 是開發人員旅程中這些步驟的絕佳工具,它將與您一路同行。讓我們進一步解開它們:
**智能合約創建/測試:**這是您對合約進行編碼的步驟。通常你在編寫智能合約和測試代碼之間存在共生關係,這是因為你需要測試每一段代碼。 Hardhat 非常擅長這一點,因為它提供了非常好的插件來測試和優化代碼。
**部署:**在此步驟中,您將代碼(將solidity 或vyper 轉換)代碼編譯為字節碼,對其進行優化並進行部署。 Hardhat 有很多不錯的插件,我們稍後會看到它們非常有用。
此外,使用 Hardhat,您可以重現過去的場景。例如,您可以告訴 Hardhat 回到過去並像我們在“x”日期一樣重新進行黑客攻擊,或者您想做的任何其他事情。這是通過分叉主網來完成的。我們將在第二個項目中回顧這個特性。
如您所見,Hardhat 為我們提供了許多不錯的功能,可以在以太坊(或 EVM 兼容鏈)中發揮作用。
Hardhat 是圍繞任務和插件的概念設計的。 Hardhat 的大部分功能來自插件,作為開發人員,您可以自由選擇要使用的插件。 Hardhat 對您最終使用的工具沒有意見,但它帶有一些可以覆蓋的內置默認值。
插件 → 插件是 Hardhat 的支柱,它們是使用您在 Hardhat 配置中使用的相同配置 DSL 構建的。您可以在此處找到 Hardhat 插件的完整列表。
任務 → 任務是帶有一些相關元數據的 JavaScript 異步函數。 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 個項目中重複許多任務。 這樣做是為了增加練習。
通過項目中的本地安裝使用硬帽。 這樣您的環境將是可重現的,並且您將避免未來的版本衝突。
你應該已經安裝了節點,你可以通過運行來檢查:
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”。 如果我們現在看一下合約,我們應該會看到經過驗證的源代碼:
這就是第一個項目!
奇偶校驗黑客是以太坊中一個非常大且重要的黑客攻擊。攻擊者竊取了超過 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
在展示代碼之前,了解我們在做什麼非常重要。
我們要回到區塊號 4043801 → 實際的黑客攻擊是在區塊 4043802 中,但我們不能在那個區塊上這樣做,因為那是黑客耗盡所有資金的時候。
我們正在冒充黑客的賬號,地址如下:0xB3764761E297D6f121e79C32A65829Cd1dDb4D32
我們正在調用未受保護的 initWallet 函數,以便我們控制錢包。 這是錢包地址:0xBEc591De75b8699A3Ba52F073428822d0Bfc0D7e
我們正在將所有資金轉移到黑客的賬戶。
然後通過運行測試文件:
npx hardhat test
輸出:
如您所見,我們成功耗盡了錢包的所有資金!
我們之前看到了安全帽網絡的快速定義。讓我們更深入一點:
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使用量。去這裡了解更多詳情。
而已 !