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运行机制有关,请问如何验证转账是否正常执行?
补充信息:
- 我已通过将success设为false验证Hardhat配置不会静默失败;
- 测试日志如下:
Old Balance:276014338387200
New Balance:276014338387200
- 无论初始存入金额多少,该账户查询余额始终不变,我认为这是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

