Hardhat是一个功能强大、灵活且易于扩展的Solidity开发框架,广泛用于以太坊智能合约的开发、测试、调试和部署。它的设计目标是提升开发效率、支持复杂项目,并与现代开发工具无缝集成。Hardhat简介Hardhat是什么?Hardhat是一个JavaScript开发的以太
Hardhat 是一个功能强大、灵活且易于扩展的 Solidity 开发框架,广泛用于以太坊智能合约的开发、测试、调试和部署。它的设计目标是提升开发效率、支持复杂项目,并与现代开发工具无缝集成。
Hardhat 是什么?
Hardhat 是一个 JavaScript 开发的以太坊开发环境,支持 Solidity 智能合约的编译、测试、调试和部署。它通过模块化的设计和丰富的插件生态,为开发者提供了高效的工作流。与 Truffle 和 Foundry 等工具相比,Hardhat 的优势在于:
适用场景:
与其他工具的对比:
特性 | Hardhat | Truffle | Foundry |
---|---|---|---|
语言 | JavaScript/TypeScript | JavaScript | Rust |
测试框架 | Mocha/Chai | Mocha/Chai | Rust-based |
速度 | 快 | 中等 | 非常快 |
调试 | 强大(console.log、堆栈跟踪) | 中等 | 有限 |
插件生态 | 丰富 | 丰富 | 较少 |
学习曲线 | 中等 | 中等 | 较陡峭 |
选择建议:
Hardhat 基于 Node.js 运行,需安装 Node.js(建议版本 16 或更高)和 npm。
安装 Node.js:
sudo apt update
sudo apt install nodejs npm
node -v
npm -v
全局安装 Hardhat:
npm install -g hardhat
npx hardhat --version
初始化项目:
mkdir my-hardhat-project
cd my-hardhat-project
npx hardhat init
Hardhat 会提示选择项目类型:
Create a JavaScript project
(适合大多数开发者)。@openzeppelin/contracts
)。项目结构: 初始化后,项目结构如下:
my-hardhat-project/
├── contracts/ # Solidity 合约代码
│ └── Lock.sol
├── scripts/ # 部署和交互脚本
│ └── deploy.js
├── test/ # 测试用例
│ └── Lock.js
├── hardhat.config.js # Hardhat 配置文件
├── package.json # Node.js 项目依赖
└── README.md
安装常用依赖:
npm install @openzeppelin/contracts @nomicfoundation/hardhat-toolbox
@openzeppelin/contracts
:提供标准化的安全合约(如 ERC20、ERC721)。@nomicfoundation/hardhat-toolbox
:集成测试、调试和部署工具。hardhat.config.js
是 Hardhat 的核心配置文件,定义编译器版本、网络和插件。
示例配置:
require("@nomicfoundation/hardhat-toolbox");
module.exports = {
solidity: {
version: "0.8.20",
settings: {
optimizer: {
enabled: true,
runs: 200
}
}
},
networks: {
hardhat: {}, // 本地 Hardhat Network
sepolia: {
url: "https://sepolia.infura.io/v3/YOUR_INFURA_KEY",
accounts: ["YOUR_PRIVATE_KEY"]
}
},
etherscan: {
apiKey: "YOUR_ETHERSCAN_API_KEY"
}
};
0.8.20
)。hardhat
)和测试网(如 Sepolia)。运行以下命令确保环境正常:
npx hardhat compile
成功后,artifacts/
和 cache/
文件夹将生成编译产物。
Hardhat 使用 npx hardhat compile
编译 Solidity 代码,生成 ABI 和字节码。
多版本支持:
在 hardhat.config.js
中配置多个 Solidity 版本:
solidity: {
compilers: [
{ version: "0.8.20" },
{ version: "0.7.6" }
]
}
错误检查: Hardhat 提供详细的编译错误信息,便于调试。
Hardhat 集成了 Mocha/Chai 测试框架,支持单元测试和集成测试。
编写测试用例:
在 test/
文件夹中创建测试文件。
示例(测试 ERC20 代币):
const { expect } = require("chai");
describe("Token", function () {
let Token, token, owner, addr1;
beforeEach(async function () {
Token = await ethers.getContractFactory("Token");
[owner, addr1] = await ethers.getSigners();
token = await Token.deploy("MyToken", "MTK", 1000000);
await token.deployed();
});
it("should assign initial supply to owner", async function () {
expect(await token.balanceOf(owner.address)).to.equal(1000000);
});
it("should transfer tokens correctly", async function () {
await token.transfer(addr1.address, 100);
expect(await token.balanceOf(addr1.address)).to.equal(100);
expect(await token.balanceOf(owner.address)).to.equal(999900);
});
});
运行测试:
npx hardhat test
覆盖率分析:
npx hardhat coverage
生成覆盖率报告,显示测试覆盖的代码行。
Hardhat 提供强大的调试工具,如 console.log
和堆栈跟踪。
使用 console.log: 在 Solidity 代码中添加调试输出:
import "hardhat/console.sol";
contract Debug {
function debugValue(uint256 value) public view {
console.log("Value is:", value);
}
}
运行调试:
使用 npx hardhat node
启动本地节点,然后运行脚本查看输出:
npx hardhat run scripts/debug.js --network hardhat
Hardhat 支持通过脚本部署合约到本地或远程网络。
编写部署脚本:
在 scripts/
文件夹中创建 deploy.js
:
async function main() {
const Token = await ethers.getContractFactory("Token");
const token = await Token.deploy("MyToken", "MTK", 1000000);
await token.deployed();
console.log("Token deployed to:", token.address);
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
部署到本地网络:
npx hardhat run scripts/deploy.js --network hardhat
部署到测试网:
配置 hardhat.config.js
中的网络,然后运行:
npx hardhat run scripts/deploy.js --network sepolia
验证合约: 使用 Etherscan 插件验证合约源码:
npx hardhat verify --network sepolia CONTRACT_ADDRESS "MyToken" "MTK" 1000000
Hardhat Network 是一个内置的以太坊模拟器,支持快速测试和调试。
启动节点:
npx hardhat node
提供 20 个测试账户,每个账户有 10000 ETH。
连接前端:
配置 MetaMask 使用 Hardhat Network(URL:http://127.0.0.1:8545
)。
Hardhat 的插件生态丰富,支持扩展功能。以下是常用插件:
@nomicfoundation/hardhat-toolbox: 集成测试、调试和部署工具。
npm install @nomicfoundation/hardhat-toolbox
@openzeppelin/hardhat-upgrades: 支持可升级合约。
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
contract MyContract is UUPSUpgradeable {
function initialize() public initializer {}
}
hardhat-gas-reporter: 分析 Gas 消耗。
npm install hardhat-gas-reporter
配置:
module.exports = {
gasReporter: {
enabled: true,
currency: "USD"
}
};
hardhat-etherscan: 验证合约源码。
npm install @nomiclabs/hardhat-etherscan
hardhat-waffle: 增强测试功能,支持 Waffle。
npm install @nomiclabs/hardhat-waffle
组织项目以提高可维护性:
my-hardhat-project/
├── contracts/ # 合约代码
│ ├── interfaces/ # 接口定义
│ ├── libraries/ # 库合约
│ └── Token.sol
├── scripts/ # 部署和交互脚本
│ ├── deploy.js
│ └── interact.js
├── test/ # 测试用例
│ ├── unit/ # 单元测试
│ └── integration/ # 集成测试
├── tasks/ # 自定义任务
│ └── balance.js
├── hardhat.config.js
└── package.json
覆盖所有场景: 测试正常、边界和错误情况。
it("should revert on zero transfer", async function () {
await expect(token.transfer(addr1.address, 0)).to.be.revertedWith("Zero amount");
});
使用快照: 在 Hardhat Network 中使用快照加速测试:
let snapshotId;
beforeEach(async function () {
snapshotId = await ethers.provider.send("evm_snapshot", []);
});
afterEach(async function () {
await ethers.provider.send("evm_revert", [snapshotId]);
});
启用优化器:
solidity: {
settings: {
optimizer: {
enabled: true,
runs: 200
}
}
}
分析 Gas 消耗:
使用 hardhat-gas-reporter
查看每个函数的 Gas 成本。
使用 OpenZeppelin 库:
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract Token is ERC20 {
constructor(string memory name, string memory symbol, uint256 initialSupply)
ERC20(name, symbol)
{
_mint(msg.sender, initialSupply);
}
}
防止重入攻击:
使用 ReentrancyGuard
:
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
contract Secure is ReentrancyGuard {
function withdraw() public nonReentrant {
// 逻辑
}
}
静态分析: 使用 Slither 检查漏洞:
npx hardhat compile
slither .
创建自定义 Hardhat 任务以自动化流程:
task("balance", "Prints an account's balance")
.addParam("account", "The account's address")
.setAction(async (taskArgs, hre) => {
const balance = await hre.ethers.provider.getBalance(taskArgs.account);
console.log("Balance:", hre.ethers.utils.formatEther(balance));
});
运行:
npx hardhat balance --account 0x123...
以下是一个完整的 ERC20 代币开发、测试和部署流程。
合约代码:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
/// @title A secure ERC20 token contract Token is ERC20, Ownable, ReentrancyGuard { constructor(string memory name, string memory symbol, uint256 initialSupply) ERC20(name, symbol) Ownable(msg.sender) { _mint(msg.sender, initialSupply * 10**decimals()); }
function transfer(address to, uint256 amount)
public
override
nonReentrant
returns (bool)
{
require(to != address(0), "Invalid address");
require(amount > 0, "Zero amount");
return super.transfer(to, amount);
}
function burn(uint256 amount) public onlyOwner {
_burn(msg.sender, amount);
}
}
2. **测试代码**:
```javascript
const { expect } = require("chai");
describe("Token", function () {
let Token, token, owner, addr1;
beforeEach(async function () {
Token = await ethers.getContractFactory("Token");
[owner, addr1] = await ethers.getSigners();
token = await Token.deploy("MyToken", "MTK", 1000000);
await token.deployed();
});
it("should assign initial supply to owner", async function () {
expect(await token.balanceOf(owner.address)).to.equal(1000000n * 10n**18n);
});
it("should transfer tokens correctly", async function () {
await token.transfer(addr1.address, 100n * 10n**18n);
expect(await token.balanceOf(addr1.address)).to.equal(100n * 10n**18n);
});
it("should revert on invalid transfer", async function () {
await expect(token.transfer(addr1.address, 0)).to.be.revertedWith("Zero amount");
await expect(token.transfer(ethers.constants.AddressZero, 100)).to.be.revertedWith("Invalid address");
});
it("should allow owner to burn tokens", async function () {
await token.burn(100n * 10n**18n);
expect(await token.balanceOf(owner.address)).to.equal(999900n * 10n**18n);
});
it("should revert burn for non-owner", async function () {
await expect(token.connect(addr1).burn(100)).to.be.revertedWith("Ownable: caller is not the owner");
});
});
async function main() { const Token = await ethers.getContractFactory("Token"); const token = await Token.deploy("MyToken", "MTK", 1000000); await token.deployed(); console.log("Token deployed to:", token.address); }
main().catch((error) => { console.error(error); process.exitCode = 1; });
4. **运行流程**:
- 编译:`npx hardhat compile`
- 测试:`npx hardhat test`
- 部署:`npx hardhat run scripts/deploy.js --network sepolia`
- 验证:`npx hardhat verify --network sepolia CONTRACT_ADDRESS "MyToken" "MTK" 1000000`
---
### 高级功能
#### 交互脚本
与已部署的合约交互:
```javascript
async function main() {
const tokenAddress = "0xYOUR_CONTRACT_ADDRESS";
const Token = await ethers.getContractFactory("Token");
const token = await Token.attach(tokenAddress);
const balance = await token.balanceOf("0xYOUR_ADDRESS");
console.log("Balance:", ethers.utils.formatEther(balance));
}
main().catch((error) => {
console.error(error);
process.exitCode = 1);
});
运行:
npx hardhat run scripts/interact.js --network sepolia
添加自定义网络(如 Polygon):
networks: {
polygon: {
url: "https://polygon-rpc.com",
accounts: ["YOUR_PRIVATE_KEY"]
}
}
将 Hardhat 项目转换为 TypeScript:
安装 TypeScript 依赖:
npm install --save-dev ts-node typescript @types/chai @types/mocha @types/node
重命名配置文件为 hardhat.config.ts
:
import { HardhatUserConfig } from "hardhat/config";
import "@nomicfoundation/hardhat-toolbox";
const config: HardhatUserConfig = {
solidity: "0.8.20",
networks: {
hardhat: {},
sepolia: {
url: "https://sepolia.infura.io/v3/YOUR_INFURA_KEY",
accounts: ["YOUR_PRIVATE_KEY"]
}
}
};
export default config;
转换测试文件为 .ts
(如 Token.test.ts
)。
hardhat.config.js
中指定正确的编译器版本。await token.transfer(addr1.address, 100, { gasLimit: 100000 });
hardhat.config.js
中的网络配置。console.log
或使用 Hardhat 的 --show-stack-traces
选项。如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!