name: hardhat-framework description: 精通Hardhat框架,用于智能合约开发、测试和部署。包括TypeChain类型生成、插件生态系统、网络分叉和部署管理。 allowed-tools: Read, Grep, Write, Bash, Edit, Glob, WebFetch
Hardhat框架技能
精通Hardhat,最流行的以太坊开发环境。
能力
- 配置: 为多网络设置hardhat.config.js
- 测试: 使用ethers.js/viem编写测试
- 插件: 使用插件(升级、gas报告、覆盖率)
- 本地网络: 运行Hardhat Network进行开发
- 部署: 执行脚本并管理部署
- 分叉: 分叉主网进行测试
- TypeChain: 生成TypeScript类型定义
安装
# 创建项目
mkdir my-project && cd my-project
npm init -y
# 安装Hardhat
npm install --save-dev hardhat
# 初始化项目
npx hardhat init
# 安装常用依赖
npm install --save-dev @nomicfoundation/hardhat-toolbox
配置
hardhat.config.js
require("@nomicfoundation/hardhat-toolbox");
require("@openzeppelin/hardhat-upgrades");
require("hardhat-gas-reporter");
require("solidity-coverage");
/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
solidity: {
version: "0.8.20",
settings: {
optimizer: {
enabled: true,
runs: 200,
},
viaIR: false,
},
},
networks: {
hardhat: {
forking: {
url: process.env.MAINNET_RPC_URL,
blockNumber: 18000000,
},
},
sepolia: {
url: process.env.SEPOLIA_RPC_URL,
accounts: [process.env.PRIVATE_KEY],
},
mainnet: {
url: process.env.MAINNET_RPC_URL,
accounts: [process.env.PRIVATE_KEY],
},
},
etherscan: {
apiKey: process.env.ETHERSCAN_API_KEY,
},
gasReporter: {
enabled: true,
currency: "USD",
coinmarketcap: process.env.COINMARKETCAP_API_KEY,
},
paths: {
sources: "./contracts",
tests: "./test",
cache: "./cache",
artifacts: "./artifacts",
},
};
TypeScript配置
// hardhat.config.ts
import { HardhatUserConfig } from "hardhat/config";
import "@nomicfoundation/hardhat-toolbox";
import "@openzeppelin/hardhat-upgrades";
const config: HardhatUserConfig = {
solidity: "0.8.20",
networks: {
sepolia: {
url: process.env.SEPOLIA_RPC_URL || "",
accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [],
},
},
};
export default config;
测试
基础测试
// test/Token.test.js
const { expect } = require("chai");
const { ethers } = require("hardhat");
describe("Token", function () {
let token;
let owner;
let addr1;
beforeEach(async function () {
[owner, addr1] = await ethers.getSigners();
const Token = await ethers.getContractFactory("Token");
token = await Token.deploy();
await token.waitForDeployment();
});
describe("部署", function () {
it("应设置正确的所有者", async function () {
expect(await token.owner()).to.equal(owner.address);
});
it("应将总供应量分配给所有者", async function () {
const ownerBalance = await token.balanceOf(owner.address);
expect(await token.totalSupply()).to.equal(ownerBalance);
});
});
describe("交易", function () {
it("应转移代币", async function () {
await token.transfer(addr1.address, 50);
expect(await token.balanceOf(addr1.address)).to.equal(50);
});
it("应触发Transfer事件", async function () {
await expect(token.transfer(addr1.address, 50))
.to.emit(token, "Transfer")
.withArgs(owner.address, addr1.address, 50);
});
it("如果发送方资金不足应失败", async function () {
await expect(
token.connect(addr1).transfer(owner.address, 1)
).to.be.revertedWith("余额不足");
});
});
});
TypeScript测试
// test/Token.test.ts
import { expect } from "chai";
import { ethers } from "hardhat";
import { Token } from "../typechain-types";
describe("Token", function () {
let token: Token;
beforeEach(async function () {
const Token = await ethers.getContractFactory("Token");
token = await Token.deploy();
});
it("应成功部署", async function () {
expect(await token.getAddress()).to.be.properAddress;
});
});
分叉测试
const { expect } = require("chai");
const { ethers, network } = require("hardhat");
describe("分叉测试", function () {
const USDC_ADDRESS = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48";
const WHALE = "0x47ac0Fb4F2D84898e4D9E7b4DaB3C24507a6D503";
beforeEach(async function () {
// 模拟鲸鱼账户
await network.provider.request({
method: "hardhat_impersonateAccount",
params: [WHALE],
});
});
it("应从鲸鱼账户转移USDC", async function () {
const whale = await ethers.getSigner(WHALE);
const usdc = await ethers.getContractAt("IERC20", USDC_ADDRESS);
const [, recipient] = await ethers.getSigners();
const amount = ethers.parseUnits("1000", 6);
await usdc.connect(whale).transfer(recipient.address, amount);
expect(await usdc.balanceOf(recipient.address)).to.equal(amount);
});
});
部署脚本
基础部署
// scripts/deploy.js
const hre = require("hardhat");
async function main() {
const Token = await hre.ethers.getContractFactory("Token");
const token = await Token.deploy();
await token.waitForDeployment();
console.log("Token部署地址:", await token.getAddress());
// 在Etherscan上验证
if (hre.network.name !== "hardhat") {
await hre.run("verify:verify", {
address: await token.getAddress(),
constructorArguments: [],
});
}
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
可升级部署
// scripts/deploy-upgradeable.js
const { ethers, upgrades } = require("hardhat");
async function main() {
const Token = await ethers.getContractFactory("TokenV1");
// 部署代理
const token = await upgrades.deployProxy(Token, [], {
initializer: "initialize",
});
await token.waitForDeployment();
console.log("代理部署地址:", await token.getAddress());
}
main();
升级脚本
// scripts/upgrade.js
const { ethers, upgrades } = require("hardhat");
async function main() {
const PROXY_ADDRESS = "0x...";
const TokenV2 = await ethers.getContractFactory("TokenV2");
const token = await upgrades.upgradeProxy(PROXY_ADDRESS, TokenV2);
console.log("Token已升级");
}
main();
Hardhat任务
// hardhat.config.js
task("accounts", "打印账户", async (taskArgs, hre) => {
const accounts = await hre.ethers.getSigners();
for (const account of accounts) {
console.log(account.address);
}
});
task("balance", "打印余额")
.addParam("account", "账户地址")
.setAction(async (taskArgs, hre) => {
const balance = await hre.ethers.provider.getBalance(taskArgs.account);
console.log(hre.ethers.formatEther(balance), "ETH");
});
命令
# 编译
npx hardhat compile
# 测试
npx hardhat test
npx hardhat test --grep "transfer"
# 覆盖率
npx hardhat coverage
# 运行脚本
npx hardhat run scripts/deploy.js --network sepolia
# 控制台
npx hardhat console --network localhost
# 节点
npx hardhat node
# 验证
npx hardhat verify --network mainnet <address>
流程集成
| 流程 | 目的 |
|---|---|
smart-contract-development-lifecycle.js |
完整开发流程 |
dapp-frontend-development.js |
dApp前端集成 |
| 所有代币流程 | 代币部署 |
| 所有DeFi流程 | 协议部署 |
最佳实践
- 使用TypeScript确保类型安全
- 配置多网络
- 使用环境变量
- 启用gas报告
- 生成覆盖率报告
- 在区块浏览器上验证合约
另请参阅
skills/foundry-framework/SKILL.md- 替代框架skills/openzeppelin/SKILL.md- 合约库- Hardhat文档