• Solidity 小白教程:15. 异常


    Solidity 小白教程:15. 异常

    这一讲,我们介绍solidity三种抛出异常的方法:errorrequireassert,并比较三种方法的gas消耗。

    异常

    智能合约经常会出bugsolidity中的异常命令帮助我们debug

    Error

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

    error TransferNotOwner(); // 自定义error
    
    • 1

    我们也可以定义一个携带参数的异常,来提示尝试转账的账户地址

    error TransferNotOwner(address sender); // 自定义的带参数的error
    
    • 1

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

    function transferOwner1(uint256 tokenId, address newOwner) public {
            if(_owners[tokenId] != msg.sender){
                revert TransferNotOwner();
                // revert TransferNotOwner(msg.sender);
            }
            _owners[tokenId] = newOwner;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

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

    Require

    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;
        }
    
    • 1
    • 2
    • 3
    • 4

    Assert

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

    function transferOwner3(uint256 tokenId, address newOwner) public {
            assert(_owners[tokenId] == msg.sender);
            _owners[tokenId] = newOwner;
        }
    
    • 1
    • 2
    • 3
    • 4

    在 remix 上验证

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

    三种方法的 gas 比较

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

    1. error 方法 gas 消耗:24457 (加入参数后 gas 消耗:24660)
    2. require 方法 gas 消耗:24755
    3. assert 方法 gas 消耗:24473

    我们可以看到,error方法gas最少,其次是assertrequire方法消耗gas最多!因此,error既可以告知用户抛出异常的原因,又能省gas,大家要多用!(注意,由于部署测试时间的不同,每个函数的gas消耗会有所不同,但是比较结果会是一致的。)
    备注: Solidity 0.8.0 之前的版本,assert抛出的是一个 panic exception,会把剩余的 gas 全部消耗,不会返还。更多细节见官方文档

    总结

    这一讲,我们介绍solidity三种抛出异常的方法:errorrequireassert,并比较了三种方法的gas消耗。结论:error既可以告知用户抛出异常的原因,又能省gas

  • 相关阅读:
    NAS数据存储之NFS搭建和使用
    玄机科技闪耀中国国际动漫节,携手百度共绘 AI 国漫新篇章
    RAID5同时挂2块磁盘,你见过吗?
    第三章 搜索与图论(三)
    关于Windows11系统,使用适用于Android的windows子系统 ,安装应用后无法连接网络的问题
    IDC第一的背后,阿里云在打造怎样的一朵“视频云”?
    json数组能不能放到hashmap中
    从一个表格render方法问题看React函数组件的更新
    【算法05】合并两个有序链表
    线程安全问题
  • 原文地址:https://blog.csdn.net/weixin_52148451/article/details/132781486