金融与交易
测试 Solidity/EVM 智能合约时,用…
适用于为 Solidity 合约搭建 Hardhat/Foundry 测试套件,覆盖单元测试、集成测试、Gas 优化、模糊测试、主网分叉、覆盖率报告和 Etherscan 验证。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 38/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 60/100 ×0.15
适用场景
测试 Solidity/EVM 智能合约特定
解决思路
- 执行这些测试类型:为智能合约编写单元测试;搭建集成测试套件;进行 Gas 优化测试;对边界情况进行模糊测试;fork 主网进行真实测试;自动生成测试覆盖率报告;在 Etherscan 验证合约。
- 在 `hardhat.config.js` 引入 `@nomicfoundation/hardhat-toolbox`、`@nomiclabs/hardhat-etherscan`、`hardhat-gas-reporter`、`solidity-coverage`。
- 配置 Solidity 编译器:`solidity.version` 设为 `0.8.19`,`settings.optimizer.enabled` 设为 `true`,`runs` 设为 `200`。
- 配置 `networks.hardhat.forking`:`url` 读取 `process.env.MAINNET_RPC_URL`,`blockNumber` 设为 `15000000`。
- 配置 `goerli` 网络:`url` 读取 `process.env.GOERLI_RPC_URL`,`accounts` 使用 `[process.env.PRIVATE_KEY]`。
- 配置 `gasReporter`:启用 `enabled: true`,`currency` 设为 `USD`,`coinmarketcap` 读取 `process.env.COINMARKETCAP_API_KEY`。
- 配置 `etherscan.apiKey`:读取 `process.env.ETHERSCAN_API_KEY`。
- 在 Hardhat 单元测试中引入 `chai` 的 `expect`、`hardhat` 的 `ethers`,以及 `@nomicfoundation/hardhat-network-helpers` 的 `loadFixture`、`time`。
- 用 `describe` 组织测试套件,例如 `Token Contract`。
- 编写 `deployTokenFixture`:用 `ethers.getSigners()` 获取 `owner`、`addr1`、`addr2`,用 `ethers.getContractFactory('Token')` 部署 `Token`,并返回 `{ token, owner, addr1, addr2 }`。
- 每个测试用 `await loadFixture(deployTokenFixture)` 初始化合约和账户,避免重复部署。
- 测试部署所有权:`expect(await token.owner()).to.equal(owner.address)`。
- 测试初始供应量:读取 `ownerBalance = await token.balanceOf(owner.address)`,断言 `await token.totalSupply()` 等于 `ownerBalance`。
- 测试账户间转账:调用 `token.transfer(addr1.address, 50)`,用 `changeTokenBalances(token, [owner, addr1], [-50, 50])` 断言余额变化。
- 测试余额不足回滚:从 `addr1` 连接合约调用 `token.connect(addr1).transfer(owner.address, 1)`,断言 `revertedWith('Insufficient balance')`。
- 测试 `Transfer` 事件:调用 `token.transfer(addr1.address, 50)`,用 `.to.emit(token, 'Transfer').withArgs(owner.address, addr1.address, 50)` 断言。
- 测试时间相关逻辑:用 `await time.increase(86400)` 推进 1 天,再执行时间依赖功能测试。
- 测试 Gas 效率:发起 `token.transfer(addr1.address, 100)`,`await tx.wait()` 获取 receipt,断言 `receipt.gasUsed` 小于 `50000`。
- Foundry 测试文件引入 `forge-std/Test.sol` 和被测合约 `../src/Token.sol`。
- 定义 `contract TokenTest is Test`,声明 `Token token`,准备测试地址 `owner = address(1)`、`user1 = address(2)`、`user2 = address(3)`。
- 在 `setUp` 中用 `vm.prank(owner)` 后执行 `token = new Token()`。
- 编写 `testInitialSupply`:用 `assertEq(token.totalSupply(), 1000000 * 10**18)` 断言初始供应量。
- 编写 `testTransfer`:用 `vm.prank(owner)` 后 `token.transfer(user1, 100)`,断言 `balanceOf(user1)` 和 `balanceOf(owner)`。
- 编写 `testFailTransferInsufficientBalance`:用 `vm.prank(user1)` 后 `token.transfer(user2, 100)`,让测试预期失败。
- 编写 `testCannotTransferToZeroAddress`:用 `vm.prank(owner)`,再 `vm.expectRevert('Invalid recipient')`,然后调用 `token.transfer(address(0), 100)`。
- 编写模糊测试:定义 `testFuzzTransfer(uint256 amount)`,用 `vm.assume(amount > 0 && amount <= token.totalSupply())` 限定输入,`vm.prank(owner)` 后转账,断言 `balanceOf(user1)` 等于 `amount`。
- 编写 cheatcode 测试:用 `vm.deal(user1, 10 ether)` 给地址发 ETH,用 `vm.prank(user1)` 模拟调用者,断言 `user1.balance == 10 ether`。
- 编写主网分叉测试:用 `vm.createSelectFork('https://eth-mainnet.alchemyapi.io/v2/...')` 创建分叉,读取 DAI 地址 `0x6B175474E89094C44Da98b954EedeAC495271d0F`,断言 `IERC20(dai).symbol() == 'DAI'`。
- 使用快照与回滚:在 `beforeEach` 中执行 `snapshotId = await network.provider.send('evm_snapshot')`。
- 在 `afterEach` 中执行 `await network.provider.send('evm_revert', [snapshotId])`,让每个测试回到干净状态。
- 主网分叉测试前用 `network.provider.request` 调用 `hardhat_reset`,参数中设置 `forking.jsonRpcUrl = process.env.MAINNET_RPC_URL`,`blockNumber = 15000000`。
- 用 `ethers.getContractAt('IUniswapV2Router', '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D')` 连接主网 Uniswap Router。
- 用 `ethers.getContractAt('IERC20', '0x6B175474E89094C44Da98b954EedeAC495271d0F')` 连接主网 DAI。
- 在主网分叉测试中调用真实 Uniswap 合约执行 swap 测试。
- 模拟鲸鱼账户:设置 `whaleAddress`,用 `network.provider.request` 调用 `hardhat_impersonateAccount`,参数为 `[whaleAddress]`。
- 用 `await ethers.getSigner(whaleAddress)` 获取鲸鱼 signer。
- 用 `dai.connect(whale).transfer(addr1.address, ethers.utils.parseEther('1000'))` 使用鲸鱼资产转账。
- 阅读 `references/details.md` 获取更多详细模板和完整模式库。
适用边界 · 注意事项
- 没有配置 Hardhat/Foundry、RPC URL、Etherscan/CoinMarketCap API key 或无法访问主网节点时,不要执行主网分叉、Gas 报告、覆盖率与 Etherscan 验证步骤。
- 被测合约不是 Solidity/EVM 智能合约(如 Solana、Move、CosmWasm)时,不适用。
- 只做轻量静态检查或不需要链上状态、事件、Gas、分叉场景时,不必搭建完整测试套件。