Hardhat 入门指南(25年更新版)
说明:原版发布于 2024-04-15,基于 Node 20.x,Ubuntu 20.04 系统开发,最新版更新于2025-12-12,基于 Node 22.x,Ubuntu 24.04 系统开发。
Hardhat 是一个以 Node.js/JavaScript 生态为核心的以太坊智能合约开发环境,帮助开发者把「写合约 → 编译 → 测试 → 本地调试 → 部署到测试网/主网」这一系列重复工作自动化与工程化;它内置 Hardhat Network(本地以太坊网络),用于部署、跑测试、调试合约逻辑。
在 Hardhat 的设计里:
- 在命令行每跑一次
npx hardhat xxx,本质上是在执行一个 task; - 大部分能力由 plugins 提供(例如 ethers 集成、chai-matchers、Ignition 部署等),官方推荐初学者使用
@nomicfoundation/hardhat-toolbox一揽子插件。
一、配置环境
在 Windows 系统使用 VS Code 配置 Ubuntu 24.04 上的开发环境。
安装 22.x 版 Node.js:
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt-get install -y nodejs
# 查看安装的版本
node -v
npm -v
二、创建 Hardhat 项目
使用 Node.js 的包管理器 npm 来安装 hardhat:
初始化项目目录与 npm 工程:
mkdir hardhat-tutorial
cd hardhat-tutorial
npm init
安装 Hardhat v2(官方教程使用 hardhat@hh2 这个标签来锁定 v2):
npm install --save-dev hardhat@hh2 # 该过程可能比较花时间
初始化 Hardhat 配置文件:
npx hardhat init # 选择:Create an empty hardhat.config.js
安装官方推荐插件集合 hardhat-toolbox:
npm install --save-dev @nomicfoundation/hardhat-toolbox@hh2
在 hardhat.config.js 文件中添加 require("@nomicfoundation/hardhat-toolbox");,这一步完成后,hardhat.config.js 如下:
require("@nomicfoundation/hardhat-toolbox");
/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
solidity: "0.8.28",
};
补充:如果使用 npm install --save-dev hardhat 安装 hardhat 太慢,可以使用 yarn add --dev hardhat 来安装。
安装 yarn 指令如下:yarn 官方提供了一个 APT 仓库,可以通过它来安装 yarn,配置仓库并导入 Yarn 的 GPG 公钥:
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt-get update
sudo apt-get install yarn
yarn --version
三、创建 & 编译智能合约
在项目根目录中新建一个 contracts 目录,接下来就可以在 contracts 目录中编写我们的合约代码,以下 MiniToken.sol 参考自 Hardhat 官方教程:
// SPDX-License-Identifier:MIT
pragma solidity ^0.8.28;
import "hardhat/console.sol";
contract MiniToken {
string public name = "Mini Hardhat Token";
string public symbol = "MHT";
uint256 public totalSupply = 1000000;
address public owner;
mapping(address => uint256) private balances;
event Transfer(address indexed from, address indexed to, uint256 value);
constructor() {
owner = msg.sender;
balances[msg.sender] = totalSupply;
}
function balanceOf(address account) external view returns (uint256) {
return balances[account];
}
function transfer(address to, uint256 amount) external {
require(balances[msg.sender] >= amount, "Not enough tokens");
balances[msg.sender] -= amount;
balances[to] += amount;
emit Transfer(msg.sender, to, amount);
}
}
注:可以在 VS Code 中安装 hardhat 的扩展程序 Solidity by Nomic Foundation。
合约代码编写完后,使用 npx hardhat compile 指令编译。
四、编写测试并运行(基于 Hardhat Network)
Hardhat 默认网络就是 Hardhat Network,跑测试无需额外启动节点或配置。官方教程用 ethers.js + Mocha + Chai 来写测试。
在根目录中新建一个目录 test,添加如下 MiniToken.js 文件:
const { expect } = require("chai");
describe("MiniToken", function () {
it("部署后:部署者应拿到全部 totalSupply", async function () {
const [owner] = await ethers.getSigners();
const token = await ethers.deployContract("MiniToken");
const ownerBalance = await token.balanceOf(owner.address);
expect(await token.totalSupply()).to.equal(ownerBalance);
});
it("转账:应能在不同账户间转移余额", async function () {
const [owner, addr1, addr2] = await ethers.getSigners();
const token = await ethers.deployContract("MiniToken");
await token.transfer(addr1.address, 50);
expect(await token.balanceOf(addr1.address)).to.equal(50);
await token.connect(addr1).transfer(addr2.address, 50);
expect(await token.balanceOf(addr2.address)).to.equal(50);
});
it("失败用例:余额不足应 revert", async function () {
const [, addr1, addr2] = await ethers.getSigners();
const token = await ethers.deployContract("MiniToken");
await expect(token.connect(addr1).transfer(addr2.address, 1))
.to.be.revertedWith("Not enough tokens");
});
});
执行npx hardhat test指令,测试结果如下:
test@DESKTOP-958GQ8P:~/desktop/hardhat-tutorial$ npx hardhat test
MiniToken
✔ 部署后:部署者应拿到全部 totalSupply (486ms)
✔ 转账:应能在不同账户间转移余额
✔ 失败用例:余额不足应 revert
3 passing (537ms)
官方教程还推荐用 loadFixture 做测试夹具与快照回滚,提高测试速度并减少重复部署代码。
五、使用 Hardhat Network 调试
5.1 在 Solidity 里直接 console.log
Hardhat Network 支持在 Solidity 代码中调用 console.log() 输出日志信息、合约变量,用法是在合约代码中导入 import "hardhat/console.sol"; 并在函数里打印。
示例:编辑 contracts/MiniToken.sol,在 transfer() 中加入日志:
import "hardhat/console.sol";
function transfer(address to, uint256 amount) external {
require(balances[msg.sender] >= amount, "Not enough tokens");
console.log(
"Transferring from %s to %s %s tokens",
msg.sender,
to,
amount
);
balances[msg.sender] -= amount;
balances[to] += amount;
emit Transfer(msg.sender, to, amount);
}
再跑一次测试指令npx hardhat test,会在测试输出中看到转账日志:
test@DESKTOP-958GQ8P:~/desktop/hardhat-tutorial$ npx hardhat test
Compiled 2 Solidity files successfully (evm target: paris).
MiniToken
✔ 部署后:部署者应拿到全部 totalSupply (471ms)
Transferring from 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266 to 0x70997970c51812dc3a010c7d01b50e0d17dc79c8 50 tokens
Transferring from 0x70997970c51812dc3a010c7d01b50e0d17dc79c8 to 0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc 50 tokens
✔ 转账:应能在不同账户间转移余额
✔ 失败用例:余额不足应 revert
3 passing (520ms)
注意点(来自 Hardhat Network 参考文档):
console.log可在 call/transaction 中使用,view可用,但pure不行;- 需要
import "hardhat/console.sol"; console.log最多支持 4 个参数,且有明确的类型范围与若干单参 API。
5.2 额外调试抓手
失败堆栈:Hardhat Network 默认会在交易失败时抛出“JavaScript + Solidity”的组合堆栈信息(便于定位 revert 触发点)。
in-process vs JSON-RPC node:测试时用的是 in-process Hardhat Network;如果用 node 任务启动 JSON-RPC server(便于前端/钱包连接),日志与行为会略有不同(例如 loggingEnabled 默认值对 in-process 与 node 有差异)。
六、部署到 Sepolia 测试网
6.1 用 Ignition Module 描述部署
Ignition 的部署入口是 Module,放在 ignition/modules 目录下。
创建 ignition/modules/MiniToken.js 文件如下:
const { buildModule } = require("@nomicfoundation/hardhat-ignition/modules");
const TokenModule = buildModule("TokenModule", (m) => {
const token = m.contract("Token");
return { token };
});
module.exports = TokenModule;
6.2 配置 Sepolia RPC 与私钥
部署到远程测试网络需要在 hardhat.config.js 中配置 RPC 信息,然后在部署指令中使用 --network参数告知 Hardhat 连接到哪个网络。
注册一个 Alchemy 账户, 然后创建一个 Ethereum Sepolia 的 App,获取该 App 的 API key。
从虚拟钱包发出的每一笔交易都需要使用private key来签名,该步骤通过把钱包地址的 private key、alchemy API key 存储在一个 environment file 中,来为程序提供必要的访问权限。
通过npm install dotenv指令安装 dotenv 模块,以便读取.env文件:
ALCHEMY_API_KEY = "xxx"
SEPOLIA_PRIVATE_KEY = "xxx"
注:测试用的钱包地址中不要放置任何真实资产。
在hardhat.config.js文件中添加配置,以便部署到 Sepolia 测试网:
require("@nomicfoundation/hardhat-toolbox");
require("dotenv").config();
const { ALCHEMY_API_KEY, SEPOLIA_PRIVATE_KEY } = process.env;
/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
solidity: "0.8.28",
networks: {
sepolia: {
url: `https://eth-sepolia.g.alchemy.com/v2/${ALCHEMY_API_KEY}`,
accounts: [SEPOLIA_PRIVATE_KEY],
},
},
};
6.3 部署合约
到目前为止,已经可以完成最后的部署了,在终端中执行如下指令:
$ npx hardhat ignition deploy ./ignition/modules/MiniToken.js --network sepolia
[dotenv@17.2.3] injecting env (2) from .env -- tip: 🔐 prevent committing .env to code: https://dotenvx.com/precommit
✔ Confirm deploy to network sepolia (11155111)? … yes
[ MiniTokenModule ] Nothing new to deploy based on previous execution stored in ./ignition/deployments/chain-11155111
Deployed Addresses
MiniTokenModule#MiniToken - 0x07831829b9F8182eE65B446adc7cD6Dc0Ba61b6D
Hardhat 也支持通过脚本来部署合约。在项目根目录下新建scripts/ 目录,将 JS 编写的部署脚本 deploy.js 放在该scripts/ 目录中,最后执行 npx hardhat run scripts/deploy.js --network sepolia就可以了。
const hre = require("hardhat");
async function main() {
const [deployer] = await hre.ethers.getSigners();
const balance = await hre.ethers.provider.getBalance(deployer.address);
console.log("Deployer:", deployer.address);
console.log("Deployer balance:", hre.ethers.formatEther(balance), "ETH");
// 推荐写法:hardhat-ethers 提供 ethers.deployContract
const token = await hre.ethers.deployContract("MiniToken");
await token.waitForDeployment();
// ethers v6 合约地址常用 token.target(或 await token.getAddress())
console.log("MiniToken deployed to:", token.target);
const tx = token.deploymentTransaction();
console.log("Deployment tx hash:", tx.hash);
}
main().catch((err) => {
console.error(err);
process.exitCode = 1;
});
执行结果大致如下:
test@DESKTOP-958GQ8P:~/desktop/hardhat-tutorial$ npx hardhat run scripts/deploy.js --network sepolia
[dotenv@17.2.3] injecting env (2) from .env -- tip: ⚙️ load multiple .env files with { path: ['.env.local', '.env'] }
[dotenv@17.2.3] injecting env (0) from .env -- tip: 🛠️ run anywhere with `dotenvx run -- yourcommand`
Deployer: 0x84B62e4c0766414a867A5aCc7BCa14901B3c713C
Deployer balance: 0.508237594610744345 ETH
MiniToken deployed to: 0xEc2E9d1ddEb8d6b1F695c23512afaa70d716458e
Deployment tx hash: 0x421c29757589388e092d52d48bf65d89060bb725cfc96a9b02497557b64bd8fa