You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 23:42:31