4、Solidity智能合约开发实战:Remix IDE使用、Hardhat框架搭建、合约编译与部署、单元测试编写
好,咱们进入第四章节。说实话,很多刚入行的朋友一上来就问我:「老师,写合约到底用什么工具?」我的回答永远是——先玩熟 Remix,再上 Hardhat。这两样东西,一个像新手村的木剑,一个像出山后的神兵,缺一不可。
4.1 Remix IDE:快速原型验证
Remix 这东西,说白了就是个浏览器里的 Solidity 编辑器。我最早接触它的时候还觉得「这玩意儿能写正经合约?」后来真香了——快速验证想法,它比任何本地环境都快。
核心功能点:
- 在线编译器:支持 Solidity 0.4.x 到 0.8.x 所有版本
- 内置调试器:可以单步执行,查看变量变化
- JavaScript VM:本地模拟链,不用花钱
- 插件系统:比如 Solidity 静态分析、ABI 编码器等
举个例子,你写一个最简单的 ERC-20 合约:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract MyToken {
string public name = "MyToken";
string public symbol = "MTK";
uint8 public decimals = 18;
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
constructor(uint256 _initialSupply) {
totalSupply = _initialSupply * 10 ** decimals;
balanceOf[msg.sender] = totalSupply;
}
function transfer(address _to, uint256 _value) public returns (bool) {
require(balanceOf[msg.sender] >= _value, "Insufficient balance");
balanceOf[msg.sender] -= _value;
balanceOf[_to] += _value;
return true;
}
}
在 Remix 里,你直接粘贴这段代码,选好编译器版本,点「Compile」,然后切到「Deploy & Run Transactions」面板,选 JavaScript VM,填个初始供应量,点「Deploy」——整个过程不到 30 秒。嗯,这就是 Remix 的魅力。
4.2 Hardhat 框架搭建:从零到一
但 Remix 毕竟只是玩具。真正要上生产环境,你得用 Hardhat。我个人觉得 Hardhat 比 Truffle 好用太多,尤其是它的任务系统和插件生态。
搭建步骤:
- 初始化项目:
mkdir my-nft-project && cd my-nft-project && npm init -y - 安装 Hardhat:
npm install --save-dev hardhat - 初始化 Hardhat 项目:
npx hardhat(选「Create an empty hardhat.config.js」) - 安装常用插件:
npm install --save-dev @nomiclabs/hardhat-waffle @nomiclabs/hardhat-ethers ethers
npx hardhat compile 一直报错,后来发现是 Node.js 版本太旧。Hardhat 要求 Node.js 16.x 以上,低于这个版本会出各种奇怪问题。所以第一步先检查你的 Node 版本:node -v。
配置好后的 hardhat.config.js 大概长这样:
require("@nomiclabs/hardhat-waffle");
module.exports = {
solidity: "0.8.9",
networks: {
goerli: {
url: "https://eth-goerli.g.alchemy.com/v2/YOUR_API_KEY",
accounts: ["你的私钥"]
}
}
};
4.3 合约编译与部署
写合约和部署合约,其实是两回事。写的时候你只管逻辑对不对,部署的时候要考虑 gas 费、网络状态、甚至合约大小。
编译命令:
npx hardhat compile
编译成功后,你会在 artifacts/contracts/ 目录下看到编译产物。这里面最重要的是 ABI(Application Binary Interface)和 Bytecode。ABI 是给前端调用的接口描述,Bytecode 是真正上链的机器码。
部署脚本示例:
// scripts/deploy.js
async function main() {
const [deployer] = await ethers.getSigners();
console.log("Deploying contracts with account:", deployer.address);
const MyNFT = await ethers.getContractFactory("MyNFT");
const myNFT = await MyNFT.deploy();
console.log("MyNFT deployed to:", myNFT.address);
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
运行部署:npx hardhat run scripts/deploy.js --network goerli
避坑指南: 我曾经在部署一个 NFT 合约时,因为合约里有个循环遍历了 1000 个地址,结果 gas 费直接爆了。后来我改成用 Merkle Tree 的方式批量 mint,gas 费降了 90%。所以部署前一定要估算 gas,用 hardhat-gas-reporter 插件可以帮你做这个。
4.4 单元测试编写:别偷懒
说实话,我见过太多人写完合约直接部署,结果上线后漏洞百出。单元测试不是可选项,是必选项。Hardhat 内置了 Waffle 测试框架,写起来很顺手。
测试文件结构:
// test/MyNFT.test.js
const { expect } = require("chai");
describe("MyNFT", function () {
it("Should mint a token and assign it to the owner", async function () {
const [owner] = await ethers.getSigners();
const MyNFT = await ethers.getContractFactory("MyNFT");
const myNFT = await MyNFT.deploy();
await myNFT.mint(owner.address, 1);
expect(await myNFT.ownerOf(1)).to.equal(owner.address);
});
it("Should not allow minting to zero address", async function () {
const MyNFT = await ethers.getContractFactory("MyNFT");
const myNFT = await MyNFT.deploy();
await expect(
myNFT.mint("0x0000000000000000000000000000000000000000", 1)
).to.be.revertedWith("ERC721: mint to the zero address");
});
});
运行测试:npx hardhat test
测试覆盖哪些场景?
| 测试类型 | 覆盖内容 | 示例 |
|---|---|---|
| 正向测试 | 正常流程是否通过 | mint 一个 token,检查 owner 是否正确 |
| 边界测试 | 极端值是否处理 | mint 到零地址、mint 重复 tokenId |
| 权限测试 | 只有授权者能操作 | 非 owner 调用 mint 应该 revert |
| Gas 测试 | 操作是否在合理 gas 范围内 | 批量 mint 100 个 token 的 gas 消耗 |
好了,这一章的内容就这些。总结一下:Remix 用来快速验证想法,Hardhat 用来搭建生产级项目,编译部署要关注 gas 和网络状态,单元测试是保命符。下一章我们会深入 NFT 合约标准,讲讲 ERC-721 和 ERC-1155 的区别,以及怎么在合约里实现版税机制。