用于捕获并处理合约调用或创建过程中可能发生的异常。
但只适用于 外部函数调用 和合约创建调用。 可以使用 恢复状态 来创建错误。
英语部分
try 试图,努力;试用,试做,试验
catch 接住;抓住,握住;逮住,捕获;
Error 错误
Panic 恐慌,惊慌;人心惶惶,惶恐不安;忙乱,慌乱;粟,稷,糜子
作用和适用场景
- 外部函数调用:
当你调用另一个合约的函数时,该函数可能会因为各种原因(如require
条件失败、状态变量修改失败等)而抛出异常。
使用try/catch
可以在调用方合约中捕获这些异常,并根据需要进行处理。
- 合约创建
在通过new
关键字创建合约实例时,如果构造函数中发生异常(如因require
失败等),
可以使用try/catch
来捕获并处理这些异常。
try externalCall() returns (DataType1 data1, DataType2 data2) {
// 处理成功的情况
} catch Error(string memory reason) {
//这个catch子句会被执行,
// 如果错误是由 revert("reasonString") 或 require(false, "reasonString") 造成的 (或内部错误造成的)。
} catch Panic(uint errorCode) {
// 如果错误是由Panic异常引起的,
// 例如由失败的 assert、除以0、无效的数组访问、算术溢出和其他原因引起的,这个catch子句将被运行。
} catch (bytes memory lowLevelData) {
// 处理低级错误(如断言失败、调用不存在的函数等)
// 如果错误签名与其他子句不匹配, 或者在解码错误信息时出现了错误,或者没有与异常一起提供错误数据,
// 那么这个子句就会被执行。在这种情况下,声明的变量提供了对低级错误数据的访问。
} catch {
//如果您对错误数据不感兴趣,您可以直接使用 catch { ... } (甚至作为唯一的catch子句)来代替前面的子句。
}
try
后跟随的是需要执行的外部调用或新合约创建表达式。returns
子句(可选)指定了期望从外部调用中接收的返回值类型和变量。- 第一个
catch
块用于捕获由revert
或require
引起的高级别错误,并可以获取错误的描述信息。 - 第二个
catch
块(通常省略错误类型)用于捕获低级别的错误,这些错误不会提供详细的描述信息,而是提供一个包含错误数据的字节串。
注意事项
try/catch
不能用于内部函数调用或当前合约的函数调用,只能用于外部合约函数调用和合约创建操作。- 使用
try/catch
可以优雅地处理错误,避免因为外部调用失败导致整个交易被回滚。 catch
块内的代码可以包含日志记录、状态更新或其他恢复措施,以在发生错误时保持合约的一致性和稳定性。
使用例子
正确的try/catch使用示例
外部函数调用
假设有一个外部合约ExternalContract,里面有 4 个函数,分别为,正常方法(又返回值),错误方法, 低级错误。算术错误
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract ExternalContract {
function ok() public pure returns (string memory) {
return "ok";
}
function reqError() public pure {
require(false, unicode"调用失败");
}
function ASSError() public pure {
assert(false);
}
function calsError() public pure {
uint8 a = 255;
uint8 b = 255;
uint8 c = a + b;
}
}
contract Name {
ExternalContract ad;
constructor() {
ad = ExternalContract(0xB9e2A2008d3A58adD8CC1cE9c15BF6D4bB9C6d72);
}
function name() public view returns (string memory) {
try ad.ok() returns (string memory aa) {
return aa;
} catch {
return "error";
}
}
function name2() public view returns (string memory) {
try ad.reqError() {
return "ok";
} catch Error(string memory reason) {
return reason;
} catch {
return "error";
}
}
function name3() public view returns (string memory) {
try ad.ASSError() {
return "ok";
} catch Error(string memory reason) {
return reason;
} catch Panic(uint256 errorCode) {
return "Panic";
} catch {
return "error";
}
}
function name5() public view returns (string memory) {
try ad.calsError() {
return "ok";
} catch Error(string memory reason) {
return reason;
} catch Panic(uint256 errorCode) {
return "Panic";
} catch {
return "error";
}
}
function name6() public view returns (string memory) {
try ad.calsError() {
return "ok";
} catch {
return "error";
}
}
}
合约创建调用
当使用new关键字创建另一个合约的实例时,也可以使用try/catch来捕获构造函数中可能发生的异常:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Eontract {
constructor(bool f) {
require(f);
}
}
contract ExternalContract {
function ok() public pure returns (string memory) {
return "ok";
}
function reqError() public pure {
require(false, unicode"调用失败");
}
function ASSError() public pure {
assert(false);
}
function calsError() public pure {
uint8 a = 255;
uint8 b = 255;
uint8 c = a + b;
}
}
contract Name {
ExternalContract ad;
constructor() {
ad = ExternalContract(0xB9e2A2008d3A58adD8CC1cE9c15BF6D4bB9C6d72);
}
function name() public view returns (string memory) {
try ad.ok() returns (string memory aa) {
return aa;
} catch {
return "error";
}
}
function name2() public view returns (string memory) {
try ad.reqError() {
return "ok";
} catch Error(string memory reason) {
return reason;
} catch {
return "error";
}
}
function name3() public view returns (string memory) {
try ad.ASSError() {
return "ok";
} catch Error(string memory reason) {
return reason;
} catch Panic(uint256 errorCode) {
return "Panic";
} catch {
return "error";
}
}
function name5() public view returns (string memory) {
try ad.calsError() {
return "ok";
} catch Error(string memory reason) {
return reason;
} catch Panic(uint256 errorCode) {
return "Panic";
} catch {
return "error";
}
}
function name6() public view returns (string memory) {
try ad.calsError() {
return "ok";
} catch {
return "error";
}
}
function name7(bool f) public returns (string memory) {
try new Eontract(f) {
return "ok";
} catch {
return "error";
}
}
}
错误的try/catch使用示例
1, 不能用于处理非外部函数调用的异常:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Test {
function internalFunction() internal {
require(false, "Function failed");
}
// 不可以在内部使用 try
function tryToCatchInternal() public {
// TypeError:Try只能用于外部函数调用和合约创建调用。
// TypeError: Try can only be used with external function calls and contract creation calls.
try internalFunction() {
// 错误:`try`不能用于内部函数调用
// ...
} catch Error(string memory reason) {
// ...
}
}
}
2,不能使用try/catch来捕获普通语句(如变量赋值、算术运算等)中的异常:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Test {
function tryToCatchStatement() public {
try uint x = 1/0 { // 错误:`try`不能用于普通语句
// ...
} catch Error(string memory reason) {
// ...
}
}
}