Hardhat测试ERC20合约触发'余额不足'报错的修复求助
问题:ERC20合约Hardhat测试触发余额不足回滚错误
我编写了一份ERC20标准智能合约,在Remix平台测试时运行正常,但使用Hardhat在VS Code中执行测试脚本时,触发了回滚错误,错误信息为You don't have enough token。以下是相关代码及错误信息:
合约代码
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "hardhat/console.sol"; contract ERC20{ uint256 public totalSupply; string public name; string public symbol; mapping (address => uint256) public balanceOf; mapping (address => mapping(address => uint256)) allowance; constructor(string memory name_, string memory symbol_){ name = name_; symbol = symbol_; _mint(msg.sender, 100); } function _mint(address to , uint256 amount) internal { require(to != address(0),"ERC20: mint to the zero address"); totalSupply += amount; balanceOf[to] = balanceOf[to] + amount; } function transfer(address recipient, uint256 amount) external returns(bool){ return _transfer(msg.sender, recipient, amount); } function transferFrom(address sender, address recipient, uint256 amount) external returns(bool){ uint256 currentAllowance = allowance[sender][msg.sender]; require( currentAllowance >= amount, "ERC20: transfer amount exceeds allowance" ); allowance[sender][msg.sender] -= currentAllowance; return _transfer(sender, recipient, amount); } function approve(address spender, uint256 amount) external returns(bool){ require( spender != address(0), "ERC20: approve to the zero address"); allowance[msg.sender][spender] = amount; return true; } function _transfer(address sender,address recipient, uint256 amount) private returns(bool){ require(recipient != address(0),"ERC20: transfer to the zero address"); uint256 senderBalances = balanceOf[sender]; require(senderBalances >= amount,"You don't have enough token"); balanceOf[sender] = senderBalances - amount; balanceOf[recipient] += amount; return true; } }
测试脚本
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers" import { expect } from "chai"; import { ethers } from "hardhat"; import { ERC20 } from "../typechain-types"; describe("ERC20",function(){ let ERC20: ERC20; let someaddress: SignerWithAddress; let someOtheraddress: SignerWithAddress; beforeEach(async function () { const ERC20ContractFactory = await ethers.getContractFactory("ERC20"); ERC20 = await ERC20ContractFactory.deploy("Hello","SYM"); await ERC20.deployed(); someaddress = (await ethers.getSigners())[1]; someOtheraddress = (await ethers.getSigners())[2]; ;}); describe('when i have 10 token', function() { beforeEach(async function () { //mint 10 token await ERC20.transfer(someaddress.address, 10); }); }); describe("When i transfer 10 token", function(){ it("should transfer tokens correctly", async function(){ await ERC20.connect(someaddress).transfer(someOtheraddress.address,10) expect ( await (await ERC20.balanceOf(someOtheraddress.address))).to.equal(10); }); }) });
错误信息
Error: VM Exception while processing transaction: reverted with reason string 'You don't have enough token' at ERC20._transfer (contracts/ERC20_Contract.sol:44) at ERC20.transfer (contracts/ERC20_Contract.sol:23) at processTicksAndRejections (node:internal/process/task_queues:95:5) at runNextTicks (node:internal/process/task_queues:64:3) at listOnTimeout (node:internal/timers:533:9) at processTimers (node:internal/timers:507:7) at async HardhatNode._mineBlockWithPendingTxs (node_modules\hardhat\src\internal\hardhat-network\provider\node.ts:1819:23) at async HardhatNode.mineBlock (node_modules\hardhat\src\internal\hardhat-network\provider\node.ts:508:16) at async EthModule._sendTransactionAndReturnHash (node_modules\hardhat\src\internal\hardhat-network\provider\modules\eth.ts:1522:18)
问题排查与修复
核心问题:测试结构逻辑错误
你编写的测试套件中,when i have 10 token和When i transfer 10 token是同级的describe块,前者内部的beforeEach只会作用于自身包含的测试用例,不会影响到同级的测试块。因此当执行转账测试时,someaddress的余额仍然是0,调用transfer时自然触发余额不足的回滚。
修复方案:调整测试嵌套结构
将转账测试块嵌套到when i have 10 token块内部,确保执行转账测试前,someaddress已经收到10个代币。修复后的测试脚本如下:
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers" import { expect } from "chai"; import { ethers } from "hardhat"; import { ERC20 } from "../typechain-types"; describe("ERC20",function(){ let ERC20: ERC20; let someaddress: SignerWithAddress; let someOtheraddress: SignerWithAddress; beforeEach(async function () { const ERC20ContractFactory = await ethers.getContractFactory("ERC20"); ERC20 = await ERC20ContractFactory.deploy("Hello","SYM"); await ERC20.deployed(); someaddress = (await ethers.getSigners())[1]; someOtheraddress = (await ethers.getSigners())[2]; }); describe('when i have 10 token', function() { beforeEach(async function () { // 从部署者地址转账10个代币给someaddress await ERC20.transfer(someaddress.address, 10); }); describe("When i transfer 10 token", function(){ it("should transfer tokens correctly", async function(){ await ERC20.connect(someaddress).transfer(someOtheraddress.address,10); expect(await ERC20.balanceOf(someOtheraddress.address)).to.equal(10); }); }); }); });
额外优化:修复transferFrom的allowance扣除逻辑
你的合约中transferFrom函数存在一个逻辑错误:扣除授权额度时,代码写的是allowance[sender][msg.sender] -= currentAllowance;,这会直接把授权额度清零,但正确的逻辑应该是扣除实际转账的金额amount。修复后的transferFrom函数如下:
function transferFrom(address sender, address recipient, uint256 amount) external returns(bool){ uint256 currentAllowance = allowance[sender][msg.sender]; require( currentAllowance >= amount, "ERC20: transfer amount exceeds allowance" ); allowance[sender][msg.sender] -= amount; // 修改为扣除实际转账金额 return _transfer(sender, recipient, amount); }
内容的提问来源于stack exchange,提问作者Anh Kiệt Nguyễn
相关产品推荐
相关产品推荐

