Solidity极简入门#30. Try Catch

try-catch是现代编程语言几乎都有的处理异常的一种标准方式,solidity0.6版本也添加了它。这一讲,我们将介绍如何利用try-catch处理智能合约中的异常。

try-catch

在solidity中,try-catch只能被用于external函数或创建合约时constructor(被视为external函数)的调用。基本语法如下:

try externalContract.f() {
  // call成功的情况下 运行一些代码
} catch {
  // call失败的情况下 运行一些代码
}

其中externalContract.f()是某个外部合约的函数调用,try模块在调用成功的情况下运行,而catch模块则在调用失败时运行。

同样可以使用this.f()来替代externalContract.f(),this.f()也被视作为外部调用,但不可在构造函数中使用,因为此时合约还未创建。

如果调用的函数有返回值,那么必须在try之后声明returns(returnType val),并且在try模块中可以使用返回的变量;如果是创建合约,那么返回值是新创建的合约变量。

try externalContract.f() returns(returnType val){
  // call成功的情况下 运行一些代码
} catch {
  // call失败的情况下 运行一些代码
}

另外,catch模块支持捕获特殊的异常原因:

try externalContract.f() returns(returnType){
  // call成功的情况下 运行一些代码
} catch Error(string memory reason) {
  // 捕获失败的 revert() 和 require()
} catch (bytes memory reason) {
  // 捕获失败的 assert()
}

try-catch实战

OnlyEven

我们创建一个外部合约OnlyEven,并使用try-catch来处理异常:

contract OnlyEven{
  constructor(uint a){
    require(a != 0, "invalid number");
    assert(a != 1);
  }

  function onlyEven(uint256 b) external pure returns(bool success){
    // 输入奇数时revert
    require(b % 2 == 0, "Ups! Reverting");
    success = true;
  }
}

OnlyEven合约包含一个构造函数和一个onlyEven函数。

  • 构造函数有一个参数a,当a=0时,require会抛出异常;当a=1时,assert会抛出异常;其他情况均正常。
  • onlyEven函数有一个参数b,当b为奇数时,require会抛出异常。

处理外部函数调用异常

首先,在TryCatch合约中定义一些事件和状态变量:

// 成功event
event SuccessEvent();

// 失败event
event CatchEvent(string message);
event CatchByte(bytes data);

// 声明OnlyEven合约变量
OnlyEven even;

constructor() {
  even = new OnlyEven(2);
}

SuccessEvent是调用成功会释放的事件,而CatchEvent和CatchByte是抛出异常时会释放的事件,分别对应require/revert和assert异常的情况。even是个OnlyEven合约类型的状态变量。

然后我们在execute函数中使用try-catch处理调用外部函数onlyEven中的异常:

// 在external call中使用try-catch
function execute(uint amount) external returns (bool success) {
  try even.onlyEven(amount) returns(bool _success){
      // call成功的情况下
      emit SuccessEvent();
      return _success;
    } catch Error(string memory reason){
      // call不成功的情况下
      emit CatchEvent(reason);
    }
}

在remix上验证

当运行execute(0)的时候,因为0为偶数,满足require(b % 2 == 0, "Ups! Reverting");,没有异常抛出,调用成功并释放SuccessEvent事件。

Solidity极简入门#30. Try Catch_solidity

当运行execute(1)的时候,因为1为奇数,不满足require(b % 2 == 0, "Ups! Reverting");,异常抛出,调用失败并释放CatchEvent事件。

Solidity极简入门#30. Try Catch_web3_02

处理合约创建异常

这里,我们利用try-catch来处理合约创建时的异常。只需要把try模块改写为OnlyEven合约的创建就行:

// 在创建新合约中使用try-catch (合约创建被视为external call)
// executeNew(0)会失败并释放`CatchEvent`
// executeNew(1)会失败并释放`CatchByte`
// executeNew(2)会成功并释放`SuccessEvent`
function executeNew(uint a) external returns (bool success) {
  try new OnlyEven(a) returns(OnlyEven _even){
      // call成功的情况下
      emit SuccessEvent();
      success = _even.onlyEven(a);
    } catch Error(string memory reason) {
      // catch失败的 revert() 和 require()
      emit CatchEvent(reason);
    } catch (bytes memory reason) {
      // catch失败的 assert()
      emit CatchByte(reason);
    }
}

在remix上验证

当运行executeNew(0)时,因为0不满足require(a != 0, "invalid number");,会失败并释放CatchEvent事件。

Solidity极简入门#30. Try Catch_solidity_03

当运行executeNew(1)时,因为1不满足assert(a != 1);,会失败并释放CatchByte事件。

Solidity极简入门#30. Try Catch_智能合约_04

当运行executeNew(2)时,因为2满足require(a != 0, "invalid number");和assert(a != 1);,会成功并释放SuccessEvent事件。

Solidity极简入门#30. Try Catch_智能合约_05

总结

在这一讲,我们介绍了如何在solidity使用try-catch来处理智能合约运行中的异常:

  • 只能用于外部合约调用和合约创建。
  • 如果try执行成功,返回变量必须声明,并且与返回的变量类型相同。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值