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

Sharer智能合约中assert的使用原因及失败触发条件是什么?

contract Sharer {
    function sendHalf(address payable addr) public payable returns (uint balance) {
        require(msg.value % 2 == 0, "Even value required.");
        uint balanceBeforeTransfer = address(this).balance;
        addr.transfer(msg.value / 2);
        // 由于transfer失败时会抛出异常,且不会回调本合约,理论上不可能仍剩余一半转账金额
        assert(address(this).balance == balanceBeforeTransfer - msg.value / 2);
        return address(this).balance;
    }
}

1. assert断言失败的场景

  • 最常见的可主动触发的场景是传入的addr为合约地址,且该合约实现了payable类型的fallback()或receive()回调函数,回调逻辑中会向当前Sharer合约转入ETH,且整个回调操作的gas消耗不超过transfer()方法固定提供的2300 gas上限。
    • 最典型的攻击实现就是接收方合约在回调中执行selfdestruct(payable(address(this))),强制将自身所有余额转入Sharer合约,该操作gas消耗极低,完全可以在2300 gas限制内完成。此时transfer()执行完成后,Sharer合约除了扣减转出去的msg.value / 2,还会收到接收方合约转回的ETH,最终余额就会不等于balanceBeforeTransfer - msg.value / 2,触发assert失败。
  • 还有极特殊的被动链上场景也会触发:比如在当前sendHalf交易执行过程中,刚好有其他交易通过selfdestruct强制向Sharer合约转入ETH、或者矿工将该区块的coinbase奖励设置为Sharer合约地址,且这部分额外的ETH入账发生在balanceBeforeTransfer赋值完成后、assert校验执行前,也会导致余额不符合预期。

2. 此处assert断言的作用

  • Solidity中assert()专门用于校验开发者认定的永远不应该被打破的内部逻辑不变量,一旦触发就代表出现了预期外的严重逻辑漏洞/攻击行为,触发时会消耗所有剩余gas并回滚整个交易的所有状态变更。
  • 本合约中开发者原本的假设是:transfer()要么执行失败直接抛异常回滚,要么执行成功且不会触发影响本合约余额的回调,因此转账完成后余额的减少量一定等于msg.value / 2。此处加assert就是为了防御所有违反这个假设的异常场景,一旦出现就立刻回滚,避免后续逻辑基于错误的余额执行产生更大的资产损失。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 13:15:03