assert(bool condition)
如果不满足条件,则会导致Panic 错误,则撤销状态更改 - 用于检查内部错误。
require(bool condition)
如果条件不满足则撤销状态更改 - 用于检查由输入或者外部组件引起的错误。
require(bool condition, string memory message)
如果条件不满足则撤销状态更改 - 用于检查由输入或者外部组件引起的错误,可以同时提供一个错误消息。
revert()
终止运行并撤销状态更改。
revert(string memory reason)
终止运行并撤销状态更改,可以同时提供一个解释性的字符串。
异常可以包含错误数据,以 error的形式传回给调用者。
内置的错误 Error(string)
和 Panic(uint256)
被作为特殊函数使用,
Error
用于 “常规” 错误条件,而 Panic
用于在(无bug)代码中不应该出现的错误
函数 assert
和 require
可用于检查条件并在条件不满足时抛出异常,然后回滚
assert
函数会创建一个 Panic(uint256)
类型的错误。 同样的错误在以下列出的特定情形会被编译器创建。
assert
函数应该只用于测试内部错误,检查不变量,正常的函数代码永远不会产生Panic, 甚至是基于一个无效的外部输入时。 如果发生了,那就说明出现了一个需要你修复的 bug。如果使用得当,语言分析工具可以识别出那些会导致 Panic 的 assert
条件和函数调用
require
函数可以创建无错误提示的错误,也可以创建一个 Error(string)
类型的错误。 require
函数应该用于确认条件有效性,例如输入变量,或合约状态变量是否满足条件,或验证外部合约调用返回的值
contract Sharer { function sendHalf(address addr) public payable returns (uint balance) { require(msg.value % 2 == 0, "Even value required."); uint balanceBeforeTransfer = this.balance; addr.transfer(msg.value / 2); // 由于转账函数在失败时抛出异常并且不会调用到以下代码,因此我们应该没有办法检查仍然有一半的钱。 assert(this.balance == balanceBeforeTransfer - msg.value / 2); return address(this).balance; } }
require
是一个像其他函数一样可被执行的函数。 意味着,所有的参数在函数被执行之前就都会被执行。 尤其,在 require(condition, f())
里,函数 f
会被执行,即便 condition
为 True
revert函数
revert(); revert(“description”);
错误数据将被传回给调用者,以便在那里捕获到错误数据。 使用 revert()
会触发一个没有任何错误数据的回退,而 revert("description")
会产生一个 Error(string)
错误
try和catch
try/catch
语句允许我们在 Solidity 合约中捕获和处理异常,并在异常发生时执行指定的逻辑。这对于处理外部调用、控制合约流程以及错误处理非常有用。
try {
// 潜在会触发异常的代码
} catch Error(string memory error) {
// 处理特定错误类型
} catch (bytes memory error) {
// 处理未知错误类型
}
在 try
块中,我们放置可能会发生异常的代码。如果这些代码成功执行,那么 catch
语句将被跳过。
如果在 try
块中发生异常,它将被抛出并被最接近的 catch
块捕获。catch
块可以指定不同类型的异常,并执行相应的错误处理逻辑。
在上面的代码示例中,catch
块捕获 Error
类型的异常,并接收一个 string
类型的错误消息。这样我们可以根据不同类型的错误消息执行不同的处理。
我们也可以使用 catch
来捕获未知类型的异常,使用 bytes
类型接收错误信息。这样可以处理一些无法预料的异常。
catch Error(string memory reason) { ... }
: 如果错误是由revert("reasonString")
或require(false, "reasonString")
(或导致这种异常的内部错误)引起的,则执行这个catch子句。catch Panic(uint errorCode) { ... }
: 如果错误是由 panic 引起的(如:assert
失败,除以0,无效的数组访问,算术溢出等),将执行这个catch子句。catch (bytes memory lowLevelData) { ... }
: 如果错误签名不符合任何其他子句,如果在解码错误信息时出现了错误,或者如果异常没有一起提供错误数据。在这种情况下,子句声明的变量提供了对低级错误数据的访问。catch { ... }
: 如果你对错误数据不感兴趣,你可以直接使用catch { ... }
(甚至是作为唯一的catch子句) 而不是前面几个catch子句
需要注意的是,异常只能在 try
块中被捕获,无法从内部函数传播到外部函数。
contract ExceptionExample {
function divide(uint a, uint b) public pure returns (uint) {
uint result;
try this.calculateDivide(a, b) returns (uint quotient) {
result = quotient;
} catch Error(string memory error) {
// 处理除以零的错误
revert(error);
} catch (bytes memory error) {
// 处理其他错误
revert(string(error));
}
return result;
}
function calculateDivide(uint a, uint b) internal pure returns (uint) {
if (b == 0) {
revert("除数不能为零");
}
return a / b;
}
}