使用evm_increaseTime后Hardhat测试交易回滚问题排查
问题诊断与修复方案
核心问题:合约ETH余额不足导致转账失败
你的claim()函数最后执行payable(msg.sender).transfer(policy.payout)时,合约ETH余额不足以支付0.1ETH的赔付金额,这是交易回滚的直接原因:
- 用户购买保单仅支付了0.01ETH,合约初始部署后无额外资金,无法覆盖0.1ETH的赔付额度
- Solidity的
transfer()函数在余额不足时会直接触发交易回滚,这就是报错的根源
时间操作的正确性验证
你使用evm_increaseTime(10)+evm_mine()的方式是正确的,从日志输出的时间来看,区块时间确实已推进至保单到期之后,block.timestamp >= policy.expiresAt、!policy.claimed、currentPrice <= policy.triggerPrice这三个前置检查均已通过,问题出在最后一步转账环节。
修复步骤
1. 给合约注入足够赔付准备金
在测试脚本的合约部署完成后,添加代码让合约所有者向合约转入足够ETH:
// 部署合约后新增此段代码 await owner.sendTransaction({ to: crash.address, value: hre.ethers.parseEther("1.0") // 转入1ETH作为赔付准备金 });
2. 可选:优化合约资金安全检查
为提前暴露余额不足问题,可在claim()函数中增加余额校验:
function claim(uint256 currentPrice) external { Policy storage policy = policies[msg.sender]; require(block.timestamp >= policy.expiresAt, "Policy still active"); require(!policy.claimed, "Already claimed"); require(currentPrice <= policy.triggerPrice, "Trigger not met"); // 新增:提前检查合约余额是否足够支付赔付 require(address(this).balance >= policy.payout, "Insufficient contract balance"); policy.claimed = true; payable(msg.sender).transfer(policy.payout); }
3. 验证修复效果
修改后重新执行测试脚本,合约有足够余额完成赔付,claim()交易即可成功执行。
额外提示
- 测试时间依赖合约时,
evm_increaseTime仅修改节点时间偏移,必须配合evm_mine生成新区块,新区块的timestamp才会更新,你的时间操作用法是正确的 - 涉及ETH转账的合约,建议采用pull模式(让用户主动提取资金)替代合约主动转账,避免因gas限制或余额不足导致的交易失败
内容的提问来源于stack exchange,提问作者Aditya General
相关产品推荐
相关产品推荐

