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

Hardhat测试中合约转账后账户余额未更新问题咨询

问题

我正在为自研智能合约编写测试用例,为简化说明,合约核心逻辑如下:
说明:我硬编码了转账数值,以明确转出金额。

function withdraw(uint256 tokenId, address to, address owner) external returns (uint256 amount) {
    amount = _positionManager.withdraw(tokenId, owner);
    
    _wrapper.withdraw(amount);
    //(bool success, ) = to.call{value: amount }("");
    (bool success, ) = to.call{value: 100000000000000000 }("");

    // failed transfer...
    require(success, "3FM:FT");
    emit Withdraw(to, amount);
}

我的测试代码如下:

const provider = ethers.getDefaultProvider();
const oldBalance = await provider.getBalance(owner.address);
console.log('Old Balance:' + oldBalance);

await fundManager.withdraw(defaultTokenId, owner.address, owner.address);
const newBalance = await provider.getBalance(owner.address);
const expectedAmount = oldBalance.add(BigNumber.from("100000000000000000"));    

console.log('New Balance:' + newBalance);
expect(newBalance).to.equal(expectedAmount);

控制台显示执行转账后账户新旧余额一致,但合约未抛出异常。我怀疑这与Hardhat运行机制有关,请问如何验证转账是否正常执行?

补充信息:

  1. 我已通过将success设为false验证Hardhat配置不会静默失败;
  2. 测试日志如下:

Old Balance:276014338387200
New Balance:276014338387200

  1. 无论初始存入金额多少,该账户查询余额始终不变,我认为这是Hardhat相关问题。
解决方案
  • 检查合约余额是否充足
    在调用withdraw前后打印合约的ETH余额,确认合约有足够资金完成硬编码的转账:

    const contractOldBalance = await ethers.provider.getBalance(fundManager.address);
    console.log('合约初始余额:', contractOldBalance.toString());
    
    await fundManager.withdraw(defaultTokenId, owner.address, owner.address);
    
    const contractNewBalance = await ethers.provider.getBalance(fundManager.address);
    console.log('合约转账后余额:', contractNewBalance.toString());
    

    若合约余额不足100000000000000000 wei,低版本Solidity中to.call不会触发revert,只会返回success=true,实际转账并未执行。

  • 替换为Hardhat内置Provider
    你当前使用的ethers.getDefaultProvider()可能连接到公共测试网,而Hardhat测试默认在本地节点运行,改用内置provider可避免状态读取偏差:

    // 替换原provider定义
    const provider = ethers.provider;
    

    公共provider存在查询延迟,无法实时获取本地链的余额变化。

  • 监听Withdraw事件验证执行状态
    合约触发了Withdraw事件,测试中监听该事件确认逻辑是否走到最后一步:

    const tx = await fundManager.withdraw(defaultTokenId, owner.address, owner.address);
    const receipt = await tx.wait();
    
    const withdrawEvent = receipt.events.find(e => e.event === 'Withdraw');
    expect(withdrawEvent.args.to).to.equal(owner.address);
    expect(withdrawEvent.args.amount).to.equal(BigNumber.from("100000000000000000"));
    

    若事件正常触发,说明require(success)已通过,问题大概率出在余额查询环节。

  • 检查交易Gas消耗
    正常ETH转账固定消耗约21000 Gas,打印交易Gas使用量可判断转账是否实际执行:

    const tx = await fundManager.withdraw(defaultTokenId, owner.address, owner.address);
    const receipt = await tx.wait();
    console.log('交易Gas消耗:', receipt.gasUsed.toString());
    

    若Gas消耗远低于21000,说明转账逻辑未真正执行。

  • 合约内添加自定义日志(临时调试)
    在Solidity合约中添加自定义事件,输出内部状态:

    // 先定义两个自定义事件
    event LogContractBalance(uint256 balance);
    event LogTransferSuccess(bool success);
    
    function withdraw(uint256 tokenId, address to, address owner) external returns (uint256 amount) {
        amount = _positionManager.withdraw(tokenId, owner);
        
        _wrapper.withdraw(amount);
        
        emit LogContractBalance(address(this).balance);
        
        (bool success, ) = to.call{value: 100000000000000000 }("");
        emit LogTransferSuccess(success);
        
        require(success, "3FM:FT");
        emit Withdraw(to, amount);
    }
    

    测试中监听这些事件,直接获取合约内部的余额和转账结果,定位问题更精准。

内容的提问来源于stack exchange,提问作者Ritzy Dev

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 01:37:48