15. solidity 异常:error、require 与 assert

15. 异常:error、require 与 assert

写智能合约经常会出bugsolidity中的异常命令可以帮助我们debug。这里介绍solidity三种抛出异常的方法:errorrequireassert,并比较三种方法的gas消耗。

首选 error 搭配 revert。

error 搭配 revert

errorsolidity 0.8版本新加的内容,方便且高效(省gas)地向用户解释操作失败的原因。人们可以在contract之外定义异常。下面,我们定义一个TransferNotOwner异常,当用户不是代币owner的时候尝试转账,会抛出错误:

error TransferNotOwner(); // 自定义error

在执行当中,error必须搭配revert(回退)命令使用。

    function transferOwner1(uint256 tokenId, address newOwner) public {
        if(_owners[tokenId] != msg.sender){
            revert TransferNotOwner();
        }
        _owners[tokenId] = newOwner;
    }

我们定义了一个transferOwner1()函数,它会检查代币的owner是不是发起人,如果不是,就会抛出TransferNotOwner异常;如果是的话,就会转账。

require

require(检查条件,"异常的描述");,相对费 gas。

require命令是solidity 0.8版本之前抛出异常的常用方法,目前很多主流合约仍然还在使用它。它很好用,唯一的缺点就是gas随着描述异常的字符串长度增加,比error命令要高。使用方法:require(检查条件,"异常的描述"),当检查条件不成立的时候,就会抛出异常。

我们用require命令重写一下上面的transferOwner函数:

    function transferOwner2(uint256 tokenId, address newOwner) public {
        require(_owners[tokenId] == msg.sender, "Transfer Not Owner");
        _owners[tokenId] = newOwner;
    }

assert

require(检查条件);,写程序时debug用的。

assert命令一般用于程序员写程序debug,因为它不能解释抛出异常的原因(比require少个字符串)。它的用法很简单,assert(检查条件),当检查条件不成立的时候,就会抛出异常。

我们用assert命令重写一下上面的transferOwner函数:

扫描二维码关注公众号,回复: 14592982 查看本文章
    function transferOwner3(uint256 tokenId, address newOwner) public {
        assert(_owners[tokenId] == msg.sender);
        _owners[tokenId] = newOwner;
    }

在remix上验证

  1. 输入任意uint256数字和非0地址,调用transferOwner1,也就是error方法,控制台抛出了异常并显示我们自定义的TransferNotOwner13 1.png
  2. 输入任意uint256数字和非0地址,调用transferOwner2,也就是require方法,控制台抛出了异常并打印出require中的字符串。 13 2.png
  3. 输入任意uint256数字和非0地址,调用transferOwner3,也就是assert方法,控制台只抛出了异常。 13 3.png

三种方法的gas比较

我们比较一下三种抛出异常的gas消耗,通过remix控制台的Debug按钮,能查到每次函数调用的gas消耗分别如下:

  1. error方法gas消耗:24445
  2. require方法gas消耗:24743
  3. assert方法gas消耗:24446

我们可以看到,error方法gas最少,其次是assertrequire方法消耗gas最多!因此,error既可以告知用户抛出异常的原因,又能省gas,大家要多用!(注意,由于部署测试时间的不同,每个函数的gas消耗会有所不同,但是比较结果会是一致的。)

习题

  1. **error **可以带有参数

  2. 在有以下定义的前提下,以下实现中能够正确抛出异常的写法为:require方法

    无标题图片

    require方法

    img

    assert方法

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-363uOLke-1679941299859)(null)]

    error方法

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6t3Sr3NK-1679941299803)(null)]

猜你喜欢

转载自blog.csdn.net/qq_42465670/article/details/129806799