1.Solidity使用状态恢复异常来处理错误
Solidity 使用状态恢复异常来处理错误。这种异常将撤消对当前调用(及其所有子调用)中的状态所做的所有更改,并且还向调用者标记错误。
如果异常在子调用发生,那么异常会自动冒泡到顶层(异常会重新抛出)。
但是如果是在 send 和 低级别如:call, delegatecall 和 staticcall 的调用里发生异常时, 他们会返回 false (第一个返回值) 而不是冒泡异常。
2.异常 assert
assert 函数只能用于测试内部错误,检查不变量,正常的函数代码永远不会产生Panic, 甚至是基于一个无效的外部输入时。 如果发生了,那就说明出现了一个需要你修复的 bug。如果使用得当,语言分析工具可以识别出那些会导致 Panic 的 assert 条件和函数调用。
下列情况将会产生一个Panic异常: 提供的错误码编号,用来指示Panic的类型。
- 0x01: 如果你调用 assert 的参数(表达式)结果为 false 。
- 0x11: 在
unchecked { … }
外,如果算术运算结果向上或向下溢出。 - 0x12; 如果你用零当除数做除法或模运算(例如 5 / 0 或 23 % 0 )。
- 0x21: 如果你将一个太大的数或负数值转换为一个枚举类型。
- 0x22: 如果你访问一个没有正确编码的存储byte数组.
- 0x31: 如果在空数组上 .pop() 。
- 0x32: 如果你访问 bytesN 数组(或切片)的索引太大或为负数。(例如: x[i] 而 i >= x.length 或 i < 0).
- 0x41: 如果你分配了太多的内内存或创建了太大的数组。
-
3.错误 require
require 函数要么创建一个 Error(string) 类型的错误,或者没有错误数据的错误并且 require 函数应该用于确认条件有效性,例如输入变量,或合约状态变量是否满足条件,或验证外部合约调用返回的值。
下列情况将会产生一个 Error(string) (或没有数据)的错误: 如果你调用 require 的参数(表达式)最终结果为 false 。
- 如果你在不包含代码的合约上执行外部函数调用。
- 如果你通过合约接收以太币,而又没有 payable 修饰符的公有函数(包括构造函数和 fallback 函数)。
- 如果你的合约通过公有 getter 函数接收 Ether 。
4. 错误和异常同时被触发的情况
在下面的情况下,来自外部调用的错误数据(如果提供的话)被转发,这意味可能 Error 或 Panic 都有可能触发。
- 如果 .transfer() 失败。
- 如果你通过消息调用调用某个函数,但该函数没有正确结束(例如, 它耗尽了 gas,没有匹配函数,或者本身抛出一个异常),不包括使用低级别 call , send , delegatecall , callcode 或 staticcall 的函数调用。低级操作不会抛出异常,而通过返回 false 来指示失败。
- 如果你使用 new 关键字创建合约,但合约创建 没有正确结束 。
4. revert
在下面的情况下,来自外部调用的错误数据(如果提供的话)被转发,这意味可能 Error 或 Panic 都有可能触发。
- 如果 .transfer() 失败。
- 如果你通过消息调用调用某个函数,但该函数没有正确结束(例如, 它耗尽了 gas,没有匹配函数,或者本身抛出一个异常),不包括使用低级别 call , send , delegatecall , callcode 或 staticcall 的函数调用。低级操作不会抛出异常,而通过返回 false 来指示失败。
- 如果你使用 new 关键字创建合约,但合约创建 没有正确结束 。
pragma solidity >=0.5.0 <0.9.0;
contract VendingMachine {
function buy(uint amount) payable {
if (amount > msg.value / 2 ether)
revert("Not enough Ether provided.");
// 下边是等价的方法来做同样的检查:
require(
amount <= msg.value / 2 ether,
"Not enough Ether provided."
);
// 执行购买操作
}
}