什么是整数溢出和 unchecked?Solidity 0.8 为什么默认让交易失败
Solidity 0.8 默认在整数溢出或下溢时回退,unchecked 会恢复绕回行为。本文讲清边界、类型转换、位移、Gas 与安全证明。
一个 uint8 最大只能保存 255。如果它已经是 255,再加 1 会怎样?在普通数学里答案是 256;在固定位宽整数里,结果要么被拒绝,要么绕回 0。智能合约中的余额、计数、价格和份额都依赖整数,这种边界差异可能直接变成资产漏洞。
Solidity 0.8.0 起,整数算术默认检查溢出和下溢,超出类型范围会回退。开发者也可以用 unchecked 显式恢复绕回行为,但必须证明边界安全或确实需要模运算。
Unchecked 不是“更快模式”,而是开发者主动拿回边界正确性的责任。
什么是溢出和下溢
运算结果大于类型最大值叫溢出,小于最小值叫下溢。uint8 范围是 0 到 255,255+1 超界;无符号 0-1 同样超界。int 类型同时有正负边界,最小负数取反也可能溢出。
Solidity 0.8 之后默认会怎样
加减乘等受检查运算超界时,编译器触发 Panic,常见错误码为 0x11,当前调用状态回退。于是很多过去依赖 SafeMath 的基础检查由语言默认提供,但业务公式和类型转换仍可能出错。

0.8 之前为什么常见 SafeMath
旧版 Solidity 的整数算术通常直接绕回,合约需要库函数先检查结果。阅读历史合约时必须确认编译器版本,不能把新版本的默认保护假设套到旧字节码上。
Unchecked 做了什么
写在 unchecked { ... } 语法块内的相关整数运算发生超界时会绕回,不触发默认检查。例如 uint256 的 0-1 会得到最大值。它只影响块内语句,调用的其他函数不会自动继承 unchecked。

哪些运算受影响
官方文档列出的相关操作包括递增递减、加减乘、除法、取模、乘方以及对应复合赋值。除零和模零即使放进 unchecked 也不能关闭检查,仍会 Panic。
| 操作 | 默认检查 | Unchecked 内 |
|---|---|---|
| uint 最大值 + 1 | 回退 | 绕回 0 |
| uint 0 - 1 | 回退 | 绕到最大值 |
| 除以 0 | 回退 | 仍回退 |
| 显式缩窄转换 | 可能截断 | 行为不由 unchecked 改变 |
| 位移 | 不做同类溢出检查 | 同样不检查 |
类型转换是另一条风险路径
把 uint256 显式转成 uint8,过大的高位会被截断,并不会因为默认算术检查自动回退。开发者要在转换前验证范围,或使用提供安全转换检查的成熟库。
位移也不会替你检查范围
位运算和左移不执行与乘法相同的溢出检查。用左移代替乘以 2 的幂,可能在高位被丢弃而不回退。Gas 优化不能只比较结果在常见输入下相同,还要证明完整输入域一致。
什么时候 Unchecked 可能合理
- 循环计数器在严格边界下递增,已证明不可能到最大值。
- 先显式检查 a >= b,再在块内执行 a-b。
- 算法本身需要模 2 的固定位宽绕回。
- 性能分析确认检查是热点,且不变量可清楚审计。
什么时候不应该使用
余额、供应量、份额、价格、债务和权限计数通常不应仅为省 Gas 关闭检查。若安全理由需要一长串隐含前提才能成立,保留默认检查往往更可靠。
先检查后 Unchecked 也可能有漏洞
检查的表达式必须与实际运算使用同一变量、同一单位和同一时刻。外部调用后状态可能变化,复杂表达式可能在检查前已经溢出,错误的严格/非严格边界也会漏掉最大值。
循环递增为何常被优化
若循环条件保证 i 小于数组长度,并且数组长度远小于 uint256 最大值,i++ 理论上不会溢出。一些代码把递增放入 unchecked 节省检查。但还要确认循环体不会修改长度或 i,边界证明不会被重构破坏。
错误码如何帮助排查
0x11 通常指 checked 算术溢出或下溢。定位失败交易时查看调用栈、源码行和输入,判断是恶意边界、业务数据异常还是程序不变量被破坏。不要简单把语句改成 unchecked 让交易“通过”。
公式顺序也会造成中间溢出
最终结果在范围内,不代表中间值一定安全。例如先乘后除可能在乘法阶段超界;先除后乘又可能损失精度。需要使用经过审查的精度算法、扩大中间表示或重新排列公式,并验证舍入方向。
边界测试怎么设计
| 类别 | 代表值 |
|---|---|
| 无符号边界 | 0、1、max-1、max |
| 有符号边界 | min、min+1、-1、0、max |
| 类型转换 | 目标 max、max+1、高位非零 |
| 业务组合 | 最大余额、份额总和、极端价格 |
| 性质测试 | 守恒、单调、结果范围与可逆条件 |
使用模糊测试和不变量
与其只列几个样例,不如定义总供应量等于余额和、用户不能提取超过权益、份额不会凭空增加等性质,让工具生成大量边界输入。发现反例后保留为回归测试。
审计每个 Unchecked 的方法
- 列出块内全部算术与类型。
- 写出每个输入可能范围和来源。
- 证明执行前不变量如何建立。
- 检查外部调用、继承和代理升级是否破坏前提。
- 验证边界、随机输入和失败路径。
- 记录为何需要 unchecked 及预期收益。
用户为什么也应关注
审计报告中的 arithmetic、rounding、unsafe cast 和 unchecked 可能影响余额与份额。项目使用 Solidity 0.8+ 并不代表所有数学天然安全;默认检查只能发现超出类型范围,不能判断经济公式是否正确。
官方资料
本文依据 Solidity:Checked or Unchecked Arithmetic。不同编译器版本默认行为不同,分析合约时应核对实际编译设置。
默认溢出检查是重要安全网,unchecked 则是需要证明的例外。真正可靠的优化会把边界、不变量、测试和 Gas 数据一起交给审查者,而不是留下一句“这里不可能溢出”。
本文为链上指南原创科普内容,不构成任何投资建议。整数边界、精度与类型转换可能影响真实资产,开发和交互前请结合测试、验证源码与独立审计。