Solidity中使用address(this).balance做减法返回异常大数值问题
问题原因排查与解决方案
你遇到的异常值是无符号整数下溢导致的绕回结果,具体原因拆解如下:
1. 余额计算时机错误
你在payable修饰的函数内部访问address(this).balance时,用户本次调用转入的msg.value已经被计入合约余额。你转入0.01 ETH对应10^16 wei,此时合约余额为10000000000000000,远大于运算中的被减数1。
2. 无符号整数下溢规则
Solidity默认的整数类型为无符号256位整数(uint256),不支持负数取值。当被减数小于减数时会发生下溢:
- 如果你使用的是Solidity 0.8.0之前的版本,默认不开启溢出/下溢检查,下溢后数值会绕回为
2^256 - (减数 - 被减数),你得到的异常值正好符合下溢绕回的计算结果。 - 如果你使用的是0.8.0及以上版本,默认会在算术溢出时回滚交易,出现该异常值说明你大概率用了
unchecked块包裹了运算逻辑。
修复方案
- 首先补充运算前的合法性校验,如果你的业务需要基于转账前的余额计算,需先扣除本次转入的金额:
function myFunction() public payable { // 计算转账前的合约余额 uint256 balanceBefore = address(this).balance - msg.value; // 校验被减数大于等于减数,避免下溢 require(1 >= balanceBefore, "Subtraction overflow"); myStruct.number = 1 - balanceBefore; }
- 如果你确实需要负数计算结果,可以将
myStruct.number的类型改为有符号整数int256,同时注意数值范围校验。 - 建议升级到Solidity 0.8及以上版本,默认开启算术溢出检查,能提前发现这类问题,避免异常值写入合约存储。
内容的提问来源于stack exchange,提问作者Jim Parsnip
相关产品推荐
相关产品推荐

