Hardhat Network 的特点和优势是什么?
Hardhat Network 是 Hardhat 框架内置的本地以太坊开发网络,专为智能合约开发、测试和调试而设计。它让开发者无需部署到真实链上即可完成全流程开发验证,是以太坊开发工具链中的核心组件。
核心特性
1. 即时挖矿(Automining)
Hardhat Network 默认启用自动挖矿模式——每笔交易提交后立即被打包进下一个区块,无需等待出块时间:
javascript// hardhat.config.js module.exports = { networks: { hardhat: { mining: { auto: true, // 默认开启,交易即时确认 interval: 5000 // 也可设为定时出块(毫秒) } } } };
关闭自动挖矿后,交易会进入内存池(mempool),行为与 Geth 客户端一致,适合测试交易排序和 MEV 场景。
2. 预置测试账户
启动时自动生成 20 个测试账户,每个账户预分配 10000 ETH:
javascriptconst [deployer, user1, user2] = await hre.ethers.getSigners(); console.log("Deployer address:", deployer.address); console.log("Balance:", hre.ethers.formatEther(await hre.ethers.provider.getBalance(deployer.address))); // 输出: Balance: 10000.0
还可自定义账户配置:
javascriptnetworks: { hardhat: { accounts: { count: 5, // 只生成 5 个账户 accountsBalance: "100000000000000000000000" // 每个账户 100000 ETH } } }
3. 状态快照与回滚
evm_snapshot 和 evm_revert 允许在测试中保存和恢复网络状态,避免每次测试都重新部署:
javascriptdescribe("Token 测试", function () { let snapshotId; before(async function () { // 部署合约 this.token = await Token.deploy(); // 保存初始状态快照 snapshotId = await hre.network.provider.send("evm_snapshot"); }); afterEach(async function () { // 每个测试用例后恢复快照 await hre.network.provider.send("evm_revert", [snapshotId]); snapshotId = await hre.network.provider.send("evm_snapshot"); }); it("转账测试", async function () { await this.token.transfer(user1.address, 100); // 测试结束后状态自动回滚 }); });
4. Solidity 调试工具
console.log 调试
在合约中直接输出调试信息,无需触发交易:
solidity// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import "hardhat/console.sol"; contract DebugExample { function calculate(uint256 a, uint256 b) public pure returns (uint256) { console.log("Input a:", a); console.log("Input b:", b); uint256 result = a * b + a; console.log("Result:", result); return result; } }
堆栈追踪
交易失败时,Hardhat Network 提供组合 JavaScript 和 Solidity 的完整调用栈:
shellError: VM Exception while processing transaction: reverted with reason string 'Insufficient balance' at Token.transfer (contracts/Token.sol:45) at process._tickCallback (internal/process/next_tick.js:68:7)
5. 主网分叉(Mainnet Forking)
基于真实链上状态创建本地分叉,可直接与已部署的 DeFi 协议交互:
javascriptnetworks: { hardhat: { forking: { url: "https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY", enabled: true, // 可选:锁定到特定区块,确保测试可复现 blockNumber: 18500000 } } }
分叉环境下的典型用法——与 Uniswap 交互测试:
javascriptit("在分叉网络上交换代币", async function () { // 模拟持有 USDC 的鲸鱼账户 await hre.network.provider.request({ method: "hardhat_impersonateAccount", params: ["0x55fe002..."], // USDC 鲸鱼地址 }); const whale = await hre.ethers.getSigner("0x55fe002..."); // 用鲸鱼账户执行交易 const usdc = await hre.ethers.getContractAt("IERC20", USDC_ADDRESS, whale); await usdc.transfer(user1.address, hre.ethers.parseUnits("1000", 6)); });
6. 时间操控与挖矿控制
在测试中灵活操控区块时间和出块:
javascript// 前进指定时间 await hre.network.provider.send("evm_increaseTime", [3600]); // 快进 1 小时 await hre.network.provider.send("evm_mine"); // 挖一个新区块 // 设置到具体时间戳 await hre.network.provider.send("evm_setNextBlockTimestamp", [1700000000]); // 重置网络状态 await hre.network.provider.send("hardhat_reset");
7. Gas 报告
结合 hardhat-gas-reporter 插件,量化合约函数的 Gas 消耗:
javascript// 安装:npm install hardhat-gas-reporter require("hardhat-gas-reporter"); module.exports = { gasReporter: { enabled: true, currency: "USD", gasPrice: 20 // Gwei } };
输出示例:
shell·--------------------------------|---------------------------|-------------|-----------------------------| | Solc version: 0.8.20 · Gas price: 20 gwei · USD/ETH: 3800 | |--------------------------------|---------------------------|-------------|-----------------------------| | Method · Min · Max · Avg | |································|···························|·············|·····························| | transfer · 51654 · 61654 · 53987 | | approve · 46263 · 46263 · 46263 | |--------------------------------|---------------------------|-------------|-----------------------------|
与其他本地网络的对比
| 特性 | Hardhat Network | Ganache | Anvil (Foundry) |
|---|---|---|---|
| 即时挖矿 | 默认开启 | 默认开启 | 默认开启 |
| Solidity 堆栈追踪 | 完整支持 | 不支持 | 支持 |
| console.log | 原生支持 | 不支持 | 支持 |
| 主网分叉 | 原生内置 | 需配置 | 原生内置 |
| 快照/回滚 | 支持 | 支持 | 支持 |
| 账户模拟 | hardhat_impersonateAccount | 不直接支持 | vm.prank |
| TypeScript 集成 | 深度集成 | 弱 | 弱 |
| 运行性能 | 中等(EDR 加速) | 中等 | 极快(Rust 原生) |
| 插件生态 | 最丰富 | 有限 | 增长中 |
典型使用场景
单元测试与集成测试
javascriptdescribe("MyContract", function () { it("应正确执行质押", async function () { const [owner, staker] = await hre.ethers.getSigners(); const token = await Token.deploy(); const staking = await Staking.deploy(token.target); await token.connect(staker).approve(staking.target, hre.ethers.parseEther("100")); await staking.connect(staker).stake(hre.ethers.parseEther("100")); expect(await staking.stakedBalance(staker.address)).to.equal(hre.ethers.parseEther("100")); }); });
DeFi 协议集成测试
通过主网分叉测试与 Aave、Uniswap 等协议的交互逻辑,无需在测试网排队或消耗真实 Gas。
时间依赖逻辑验证
利用 evm_increaseTime 和 evm_mine 测试锁仓期、投票截止时间等时间相关功能。
Gas 优化迭代
通过 Gas 报告对比不同实现的 Gas 消耗,持续优化合约效率。
常见配置参考
javascript// hardhat.config.js — 完整开发配置 require("@nomicfoundation/hardhat-toolbox"); require("hardhat-gas-reporter"); module.exports = { solidity: "0.8.24", networks: { hardhat: { chainId: 31337, mining: { auto: true }, forking: process.env.FORK_URL ? { url: process.env.FORK_URL, blockNumber: 18500000 } : undefined, accounts: { count: 20, accountsBalance: "10000000000000000000000" // 10000 ETH }, allowBlocksWithSameTimestamp: true, throwOnTransactionFailures: true, throwOnCallFailures: true } }, gasReporter: { enabled: process.env.REPORT_GAS === "true" } };
总结
Hardhat Network 的核心优势在于将开发调试效率最大化:即时确认交易、Solidity 级堆栈追踪、主网状态分叉、灵活的时间与账户操控,这些能力让开发者能在本地完成绝大部分验证工作,显著减少对测试网的依赖。虽然 Foundry/Anvil 在执行速度上有优势,但 Hardhat Network 凭借 TypeScript 深度集成和丰富的插件生态,仍是复杂 DApp 开发场景下的首选本地网络。