中文 / English
Hardhat 是一个编译、部署、测试和调试以太坊应用的开发环境。
它可以帮助开发人员管理和自动化构建智能合约和 dApps 过程中固有的重复性任务,并围绕这一工作流程轻松引入更多功能。这意味着 hardhat 在最核心的地方是编译、运行和测试智能合约。 Hardhat 内置了 Hardhat 网络,这是一个专为开发设计的本地以太坊网络。主要功能有 Solidity 调试,跟踪调用堆栈、console.log()和交易失败时的明确错误信息提示等。
Hardhat Runner 是与 Hardhat 交互的 CLI 命令,是一个可扩展的任务运行器。它是围绕任务和插件的概念设计的。每次你从 CLI 运行 Hardhat 时,你都在运行一个任务。例如,npx hardhat compile
运行的是内置的 compile 任务。任务可以调用其他任务,允许定义复杂的工作流程。用户和插件可以覆盖现有的任务,从而定制和扩展工作流程。
在开始学习 hardhat 之前,你需要提前了解以下知识点:
- dotenv 将私钥存放在
.env
文件中可以避免将私钥暴露在服务器上,格式为 "PRIVATE_KEY=xxxx", 然后代码自动从中读取,详情参考 dotenv - npx 想要解决的主要问题,就是调用项目内部安装的模块。详情参考 npx 使用教程
- ethers.js 与以太坊网络交互的工具库,相比 web3.js 接口设计更加易于使用(注意 v5 和 v4 接口差别较大) ethers.js v5 文档
- mocha.js 测试框架,用于编写合约交互的测试案例 mochajs 文档
- chai.js 断言库,辅助测试脚本编写,使用方法参考 ethereum-waffle chai 使用文档
- infura 连接区块链的节点服务商,有免费的使用额度,足够开发调试使用 infura 官网
mkdir 07-hardhat // 创建项目文件夹
cd 07-hardhat // 移动到项目文件夹下
npm install --save-dev hardhat // 安装hardhat
npx hardhat // 创建hardhat项目
输入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.9.0
? What do you want to do? ...
> Create a basic sample project
Create an advanced sample project
Create an advanced sample project that uses TypeScript
Create an empty hardhat.config.js
Quit
我们使用'Create a basic sample project'选项,创建一个基础项目,后面的两个选项直接敲回车选择默认值。
一个标准的使用 hardhat 构建的项目通常是这样的:
contracts/
scripts/
test/
hardhat.config.js
- contracts 用于存放 solidity 合约文件
- scripts 用于存放脚本文件,如部署合约的脚本
- test 用于存放测试脚本,通常以
contractName.test.js
的形式命名 hardhat.config.js
是 hardhat 的配置文件
hardhat.config.js
配置文件示例
require('@nomiclabs/hardhat-waffle');
require('dotenv').config();
module.exports = {
networks: {
// hardhat 内置测试网络(选填)
hardhat: {
// 可以设置一个固定的gasPrice,在测试gas消耗的时候会很有用
gasPrice: 1000000000,
},
// 你可以在这里配置任意网络
// goerli 测试网络
goerli: {
// 请将 INFURA_ID 替换成你自己的
// url: 'https://goerli.infura.io/v3/{INFURA_ID}',
url: 'https://goerli.infura.io/v3/' + process.env.INFURA_ID, //<---- 在.env文件中配置自己的INFURA_ID
// 填写测试账户的私钥,可填写多个
accounts: [process.env.PRIVATE_KEY, ...]
}
},
solidity: {
version: "0.8.0", // 合约编译的版本,必填
settings: { // 编译设置,选填
optimizer: { // 优化设置
enabled: true,
runs: 200
}
}
},
// 项目路径配置,可指定任意路径,但下列是常用的一种结构
// sources, tests, scripts 下的目录文件会被自动逐一执行
paths: {
sources: "./contracts", // 合约目录
tests: "./test", // 测试文件目录
cache: "./cache", // 缓存目录,由hardhat自动生成
artifacts: "./artifacts" // 编译结果目录,由hardhat自动生成
},
// 测试框架设置
mocha: {
timeout: 20000 // 运行单元测试的最大等待时间
}
}
hardhat 内置了一个特殊的安全测试网络,其名称也叫 hardhat
, 通常你不需要对他进行特殊配置。该网络会模拟真实区块链网络的运行机制,并为你生成好 10 个测试账户(和 truffle 类似)。
Hardhat 的很多功能都来自于插件,而作为开发者,你可以自由选择想使用的插件。
例如常用的 waffle 插件,使得 hardhat 可以集成 waffle 框架,进行开发,测试,部署。
// hardhat.config.js
require('@nomiclabs/hardhat-waffle'); // hardhat waffle 插件
...
-
安装 nodejs (略)
-
安装项目依赖:
npm install --save-dev @nomiclabs/hardhat-waffle ethereum-waffle chai @nomiclabs/hardhat-ethers ethers dotenv
或使用 yarn 安装(需要先安装 yarn 依赖)
yarn add -D hardhat-deploy-ethers ethers chai chai-ethers mocha @types/chai @types/mocha dotenv
-
配置私钥和网络:
在项目文件夹下新建
.env
文件,并且在.env
文件中填写私钥和 infura 节点PRIVATE_KEY = xxxxxxxxxxxxxxxx; // 替换为你的私钥 INFURA_ID = yyyyyyyy; // 替换为infura节点
hardhat 的用法
运行如下命令,hardhat 会自动编译配置中 sources
路径下的所有合约文件,默认是 ./contracts
路径。
npx hardhat compile
运行如下命令,hardhat 会自动运行配置中 tests
路径下的所有测试文件,默认是 ./test
路径。
npx hardhat test
也可以指定运行某个特定测试文件
npx hardhat test ./test/Greeter.test.js
运行指定脚本。如果不指定运行网络,会默认在 hardhat 内置网络内运行 (Hardhat Network)。
npx hardhat run ./scripts/deploy.js
指定运行的网络,例如在 goerli 测试网部署合约(请确保钱包地址在 goerli 测试网有足够的 gas 才能成功部署)
npx hardhat run ./scripts/deploy.js --network goerli
验证智能合约,这里以goerli
举例。
在 hardhat.config.js
添加配置:
etherscan: {
apiKey: "<etherscan的api key>",
}
运行脚本:
npx hardhat verify --network goerli <合约地址>
hardhat 本身预设了一些程序任务,例如编译合约,运行测试文件,这些其实在 hardhat 中是预先配置好的任务。
实际上你也可以自定义一些 task,比如打印一下当前网络中的账户状态:
// hardhat.config.js
...
task('accounts', 'Prints the list of accounts', async () => {
const accounts = await ethers.getSigners();
for (const account of accounts) {
console.log(account.address);
}
});
...
运行 task
npx hardhat accounts
命令行会打印出 10 个测试账户地址
0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
...
hardhat 的控制台模式,实时与链上交互。默认会启动 hardhat 内置网络。
npx hardhat console
控制内置 ethers 和 web3 库,可以直接使用,无须引入。
// hardhat console mode:
// 可以直接使用 async/await 语法
> await ethers.provider.getBlockNumber() // 0
hardhat 提供了一个 console.log()
方法,可以在合约运行时打印日志,方便调试和测试。此方法仅在 hardhat 内置网络中运行有效。
在合约中引入 hardhat/console.sol
即可使用:
import "hardhat/console.sol";
contract Greeter {
...
function setGreeting(string memory _greeting) public {
console.log("Changing greeting from '%s' to '%s'", greeting, _greeting);
greeting = _greeting;
}
}
在运行测试文件时,可以看到打印出的日志:
Changing greeting from 'Hello, world!' to 'hello Dapp-Learning!'
-
编译合约
npx hardhat compile
-
批量运行测试脚本
npx hardhat test
-
部署到测试网:
npx hardhat run scripts/deploy.js --network <network-name>
这里的
network-name
替换成你指定的网络名称,这里可以换成goerli
,对应配置文件中的网络名称。 -
验证智能合约
npx hardhat verify --network goerli <network-name> <contract-address>
network-name
:你指定的网络名称,这里可以换成goerli
,对应配置文件中的网络名称。contract-address
:上一步部署的合约地址。
- hardhat 官方文档: https://hardhat.org/guides/project-setup.html
- hardhat 中文文档: https://learnblockchain.cn/docs/hardhat/getting-started/
- ethers.js 和 hardhat 基础使用讲解: https://www.bilibili.com/video/BV1Pv411s7Nb
- https://rahulsethuram.medium.com/the-new-solidity-dev-stack-buidler-ethers-waffle-typescript-tutorial-f07917de48ae
- erc20 openzepplin介绍: https://segmentfault.com/a/1190000015400380