|
| 1 | +中文 / [English](./README.md) |
| 2 | + |
| 3 | + |
| 4 | +# Hardhat |
| 5 | + |
| 6 | +Hardhat 是一个编译、部署、测试和调试以太坊应用的开发环境。 |
| 7 | + |
| 8 | +它可以帮助开发人员管理和自动化构建智能合约和 dApps 过程中固有的重复性任务,并围绕这一工作流程轻松引入更多功能。这意味着 hardhat 在最核心的地方是编译、运行和测试智能合约。 |
| 9 | +Hardhat 内置了 Hardhat 网络,这是一个专为开发设计的本地以太坊网络。主要功能有 Solidity 调试,跟踪调用堆栈、console.log()和交易失败时的明确错误信息提示等。 |
| 10 | + |
| 11 | +Hardhat Runner 是与 Hardhat 交互的 CLI 命令,是一个可扩展的任务运行器。它是围绕任务和插件的概念设计的。每次你从 CLI 运行 Hardhat 时,你都在运行一个任务。例如,`npx hardhat compile` 运行的是内置的 compile 任务。任务可以调用其他任务,允许定义复杂的工作流程。用户和插件可以覆盖现有的任务,从而定制和扩展工作流程。 |
| 12 | + |
| 13 | +## 准备工作 - Preparatory Work |
| 14 | + |
| 15 | +在开始学习 hardhat 之前,你需要提前了解以下知识点: |
| 16 | + |
| 17 | +- dotenv 将私钥存放在 `.env` 文件中可以避免将私钥暴露在服务器上,格式为 "PRIVATE_KEY=xxxx", 然后代码自动从中读取,详情参考 [dotenv](https://www.npmjs.com/package/dotenv) |
| 18 | +- npx 想要解决的主要问题,就是调用项目内部安装的模块。详情参考 [npx 使用教程](https://www.ruanyifeng.com/blog/2019/02/npx.html) |
| 19 | +- ethers.js 与以太坊网络交互的工具库,相比 web3.js 接口设计更加易于使用(注意 v5 和 v4 接口差别较大) [ethers.js v5 文档](https://docs.ethers.io/v5/) |
| 20 | +- mocha.js 测试框架,用于编写合约交互的测试案例 [mochajs 文档](https://mochajs.org/#getting-started) |
| 21 | +- chai.js 断言库,辅助测试脚本编写,使用方法参考 [ethereum-waffle chai 使用文档](https://ethereum-waffle.readthedocs.io/en/latest/matchers.html) |
| 22 | +- infura 连接区块链的节点服务商,有免费的使用额度,足够开发调试使用 [infura 官网](https://infura.io/) |
| 23 | + |
| 24 | +## 项目结构和配置 hardhat |
| 25 | + |
| 26 | +```sh |
| 27 | +mkdir 07-hardhat // 创建项目文件夹 |
| 28 | +cd 07-hardhat // 移动到项目文件夹下 |
| 29 | +npm install --save-dev hardhat // 安装hardhat |
| 30 | +npx hardhat // 创建hardhat项目 |
| 31 | +``` |
| 32 | + |
| 33 | +输入`npx hardhat`后,命令行中会出现如下的界面: |
| 34 | + |
| 35 | +```sh |
| 36 | +888 888 888 888 888 |
| 37 | +888 888 888 888 888 |
| 38 | +888 888 888 888 888 |
| 39 | +8888888888 8888b. 888d888 .d88888 88888b. 8888b. 888888 |
| 40 | +888 888 "88b 888P" d88" 888 888 "88b "88b 888 |
| 41 | +888 888 .d888888 888 888 888 888 888 .d888888 888 |
| 42 | +888 888 888 888 888 Y88b 888 888 888 888 888 Y88b. |
| 43 | +888 888 "Y888888 888 "Y88888 888 888 "Y888888 "Y888 |
| 44 | +
|
| 45 | +Welcome to Hardhat v2.9.0 |
| 46 | +
|
| 47 | +? What do you want to do? ... |
| 48 | +> Create a basic sample project |
| 49 | + Create an advanced sample project |
| 50 | + Create an advanced sample project that uses TypeScript |
| 51 | + Create an empty hardhat.config.js |
| 52 | + Quit |
| 53 | +``` |
| 54 | +
|
| 55 | +我们使用'Create a basic sample project'选项,创建一个基础项目,后面的两个选项直接敲回车选择默认值。 |
| 56 | +
|
| 57 | +### 项目结构 |
| 58 | +
|
| 59 | +一个标准的使用 hardhat 构建的项目通常是这样的: |
| 60 | +
|
| 61 | +```sh |
| 62 | +contracts/ |
| 63 | +scripts/ |
| 64 | +test/ |
| 65 | +hardhat.config.js |
| 66 | +``` |
| 67 | +
|
| 68 | +- contracts 用于存放 solidity 合约文件 |
| 69 | +- scripts 用于存放脚本文件,如部署合约的脚本 |
| 70 | +- test 用于存放测试脚本,通常以 `contractName.test.js` 的形式命名 |
| 71 | +- `hardhat.config.js` 是 hardhat 的配置文件 |
| 72 | +
|
| 73 | +### 配置 hardhat |
| 74 | +
|
| 75 | +`hardhat.config.js` 配置文件示例 |
| 76 | +
|
| 77 | +```js |
| 78 | +require('@nomiclabs/hardhat-waffle'); |
| 79 | +require('dotenv').config(); |
| 80 | +
|
| 81 | +module.exports = { |
| 82 | + networks: { |
| 83 | + // hardhat 内置测试网络(选填) |
| 84 | + hardhat: { |
| 85 | + // 可以设置一个固定的gasPrice,在测试gas消耗的时候会很有用 |
| 86 | + gasPrice: 1000000000, |
| 87 | + }, |
| 88 | + // 你可以在这里配置任意网络 |
| 89 | + // rinkeby 测试网络 |
| 90 | + rinkeby: { |
| 91 | + // 请将 INFURA_ID 替换成你自己的 |
| 92 | + // url: 'https://rinkeby.infura.io/v3/{INFURA_ID}', |
| 93 | + url: 'https://rinkeby.infura.io/v3/' + process.env.INFURA_ID, //<---- 在.env文件中配置自己的INFURA_ID |
| 94 | +
|
| 95 | + // 填写测试账户的私钥,可填写多个 |
| 96 | + accounts: [process.env.PRIVATE_KEY, ...] |
| 97 | + } |
| 98 | + }, |
| 99 | + solidity: { |
| 100 | + version: "0.8.0", // 合约编译的版本,必填 |
| 101 | + settings: { // 编译设置,选填 |
| 102 | + optimizer: { // 优化设置 |
| 103 | + enabled: true, |
| 104 | + runs: 200 |
| 105 | + } |
| 106 | + } |
| 107 | + }, |
| 108 | + // 项目路径配置,可指定任意路径,但下列是常用的一种结构 |
| 109 | + // sources, tests, scripts 下的目录文件会被自动逐一执行 |
| 110 | + paths: { |
| 111 | + sources: "./contracts", // 合约目录 |
| 112 | + tests: "./test", // 测试文件目录 |
| 113 | + cache: "./cache", // 缓存目录,由hardhat自动生成 |
| 114 | + artifacts: "./artifacts" // 编译结果目录,由hardhat自动生成 |
| 115 | + }, |
| 116 | + // 测试框架设置 |
| 117 | + mocha: { |
| 118 | + timeout: 20000 // 运行单元测试的最大等待时间 |
| 119 | + } |
| 120 | +} |
| 121 | +``` |
| 122 | +
|
| 123 | +### 内置 hardhat 网络 |
| 124 | +
|
| 125 | +hardhat 内置了一个特殊的安全测试网络,其名称也叫 `hardhat`, 通常你不需要对他进行特殊配置。该网络会模拟真实区块链网络的运行机制,并为你生成好 10 个测试账户(和 truffle 类似)。 |
| 126 | +
|
| 127 | +### 使用插件 |
| 128 | +
|
| 129 | +Hardhat 的很多功能都来自于插件,而作为开发者,你可以自由选择想使用的插件。 |
| 130 | +
|
| 131 | +例如常用的 waffle 插件,使得 hardhat 可以集成 waffle 框架,进行开发,测试,部署。 |
| 132 | +
|
| 133 | +```js |
| 134 | +// hardhat.config.js |
| 135 | +require('@nomiclabs/hardhat-waffle'); // hardhat waffle 插件 |
| 136 | +... |
| 137 | +``` |
| 138 | +
|
| 139 | +### 安装依赖 |
| 140 | +
|
| 141 | +1. 安装 nodejs (略) |
| 142 | +
|
| 143 | +2. 安装项目依赖: |
| 144 | +
|
| 145 | + ```sh |
| 146 | + npm install --save-dev @nomiclabs/hardhat-waffle ethereum-waffle chai @nomiclabs/hardhat-ethers ethers dotenv |
| 147 | + ``` |
| 148 | +
|
| 149 | + 或使用 yarn 安装(需要先安装 yarn 依赖) |
| 150 | +
|
| 151 | + ```sh |
| 152 | + yarn add -D hardhat-deploy-ethers ethers chai chai-ethers mocha @types/chai @types/mocha dotenv |
| 153 | + ``` |
| 154 | +
|
| 155 | +3. 配置私钥和网络: |
| 156 | +
|
| 157 | + 在项目文件夹下新建`.env`文件,并且在 `.env` 文件中填写私钥和 infura 节点 |
| 158 | +
|
| 159 | + ```js |
| 160 | + PRIVATE_KEY = xxxxxxxxxxxxxxxx; // 替换为你的私钥 |
| 161 | + INFURA_ID = yyyyyyyy; // 替换为infura节点 |
| 162 | + ``` |
| 163 | +
|
| 164 | +## usage |
| 165 | +
|
| 166 | +hardhat 的用法 |
| 167 | +
|
| 168 | +### compile |
| 169 | +
|
| 170 | +运行如下命令,hardhat 会自动编译配置中 `sources` 路径下的所有合约文件,默认是 `./contracts` 路径。 |
| 171 | +
|
| 172 | +```sh |
| 173 | +npx hardhat compile |
| 174 | +``` |
| 175 | +
|
| 176 | +### test |
| 177 | +
|
| 178 | +运行如下命令,hardhat 会自动运行配置中 `tests` 路径下的所有测试文件,默认是 `./test` 路径。 |
| 179 | +
|
| 180 | +```sh |
| 181 | +npx hardhat test |
| 182 | +``` |
| 183 | +
|
| 184 | +也可以指定运行某个特定测试文件 |
| 185 | +
|
| 186 | +```sh |
| 187 | +npx hardhat test ./test/Greeter.test.js |
| 188 | +``` |
| 189 | +
|
| 190 | +### run |
| 191 | +
|
| 192 | +拷贝 script 目录下的脚本,作为我们的运行脚本: |
| 193 | +
|
| 194 | +windows: |
| 195 | +
|
| 196 | +```bash |
| 197 | +copy .\scripts\sample-script.js .\scripts\deploy.js |
| 198 | +``` |
| 199 | +
|
| 200 | +linux: |
| 201 | +
|
| 202 | +```bash |
| 203 | + cp ./scripts/sample-script.js ./scripts/deploy.js |
| 204 | +``` |
| 205 | +
|
| 206 | +运行指定脚本。如果不指定运行网络,会默认在 hardhat 内置网络内运行 (Hardhat Network)。 |
| 207 | +
|
| 208 | +```sh |
| 209 | +npx hardhat run ./scripts/deploy.js |
| 210 | +``` |
| 211 | +
|
| 212 | +指定运行的网络,例如在 rinkeby 测试网部署合约(请确保钱包地址在 rinkeby 测试网有足够的 gas 才能成功部署) |
| 213 | +
|
| 214 | +```sh |
| 215 | +npx hardhat run ./scripts/deploy.js --network rinkeby |
| 216 | +``` |
| 217 | +
|
| 218 | +### task |
| 219 | +
|
| 220 | +hardhat 本身预设了一些程序任务,例如编译合约,运行测试文件,这些其实在 hardhat 中是预先配置好的任务。 |
| 221 | +
|
| 222 | +实际上你也可以自定义一些 task,比如打印一下当前网络中的账户状态: |
| 223 | +
|
| 224 | +```js |
| 225 | +// hardhat.config.js |
| 226 | +... |
| 227 | +
|
| 228 | +task('accounts', 'Prints the list of accounts', async () => { |
| 229 | + const accounts = await ethers.getSigners(); |
| 230 | +
|
| 231 | + for (const account of accounts) { |
| 232 | + console.log(account.address); |
| 233 | + } |
| 234 | +}); |
| 235 | +
|
| 236 | +... |
| 237 | +``` |
| 238 | +
|
| 239 | +运行 task |
| 240 | +
|
| 241 | +```sh |
| 242 | +npx hardhat accounts |
| 243 | +``` |
| 244 | +
|
| 245 | +命令行会打印出 10 个测试账户地址 |
| 246 | +
|
| 247 | +```sh |
| 248 | +0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 |
| 249 | +... |
| 250 | +``` |
| 251 | +
|
| 252 | +### console |
| 253 | +
|
| 254 | +hardhat 的控制台模式,实时与链上交互。默认会启动 hardhat 内置网络。 |
| 255 | +
|
| 256 | +```sh |
| 257 | +npx hardhat console |
| 258 | +``` |
| 259 | +
|
| 260 | +控制内置 ethers 和 web3 库,可以直接使用,无须引入。 |
| 261 | +
|
| 262 | +```js |
| 263 | +// hardhat console mode: |
| 264 | +// 可以直接使用 async/await 语法 |
| 265 | +> await ethers.provider.getBlockNumber() // 0 |
| 266 | +``` |
| 267 | +
|
| 268 | +### console.log debug |
| 269 | +
|
| 270 | +hardhat 提供了一个 `console.log()` 方法,可以在合约运行时打印日志,方便调试和测试。**此方法仅在 hardhat 内置网络中运行有效。** |
| 271 | +
|
| 272 | +在合约中引入 `hardhat/console.sol` 即可使用: |
| 273 | +
|
| 274 | +```solidity |
| 275 | +import "hardhat/console.sol"; |
| 276 | +
|
| 277 | +contract Greeter { |
| 278 | + ... |
| 279 | +
|
| 280 | + function setGreeting(string memory _greeting) public { |
| 281 | + console.log("Changing greeting from '%s' to '%s'", greeting, _greeting); |
| 282 | + greeting = _greeting; |
| 283 | + } |
| 284 | +
|
| 285 | +} |
| 286 | +``` |
| 287 | +
|
| 288 | +在运行测试文件时,可以看到打印出的日志: |
| 289 | +
|
| 290 | +```sh |
| 291 | +Changing greeting from 'Hello, world!' to 'hello Dapp-Learning!' |
| 292 | +``` |
| 293 | +
|
| 294 | +## 实操流程 |
| 295 | +
|
| 296 | +### 编译和测试 |
| 297 | +
|
| 298 | +1. 编译合约 |
| 299 | +
|
| 300 | + ```bash |
| 301 | + npx hardhat compile |
| 302 | + ``` |
| 303 | +
|
| 304 | +2. 批量运行测试脚本 |
| 305 | +
|
| 306 | + ```bash |
| 307 | + npx hardhat test |
| 308 | + ``` |
| 309 | +
|
| 310 | +3. 部署到测试网: |
| 311 | +
|
| 312 | + ```bash |
| 313 | + npx hardhat run scripts/deploy.js --network <network-name> |
| 314 | + ``` |
| 315 | +
|
| 316 | + 这里的 `network-name` 替换成你指定的网络名称,这里可以换成 `rinkeby`,对应配置文件中的网络名称。 |
| 317 | +
|
| 318 | +## 参考文档 |
| 319 | +
|
| 320 | +- hardhat 官方文档: <https://hardhat.org/guides/project-setup.html> |
| 321 | +- hardhat 中文文档: <https://learnblockchain.cn/docs/hardhat/getting-started/> |
| 322 | +- ethers.js 和 hardhat 基础使用讲解: <https://www.bilibili.com/video/BV1Pv411s7Nb> |
| 323 | +- <https://rahulsethuram.medium.com/the-new-solidity-dev-stack-buidler-ethers-waffle-typescript-tutorial-f07917de48ae> |
| 324 | +- erc20 openzepplin介绍: <https://segmentfault.com/a/1190000015400380> |
0 commit comments