玩以太坊链上项目的必备技能(错误处理以及异常-Solidity之旅十四)

这篇具有很好参考价值的文章主要介绍了玩以太坊链上项目的必备技能(错误处理以及异常-Solidity之旅十四)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

错误处理

作为开发者的我们知道,我们所编写出来的程序难免会出现 bug ,而要做的是捕获异常,给用户抛出一个友好地错误提示。

而在 Solidity 中,根据状态恢复异常来处理错误,该异常将撤销在当前调用中对状态所做的所有修改,与此同时,还向调用者标记错误。

它有许多功能来解决在编译时或运行时可能发生的潜在问题。即使语法错误检查发生在编译时,运行时错误也很难捕捉,主要发生在合约执行过程中。一些运行时错误的例子包括除以0的类型错误,数组超出索引错误,等等。

实际上,Solidity的错误处理确保了原子性这一属性。当一个智能合约调用因错误而终止时,所有的状态变化(即对变量、余额等的改变)都会被恢复,一直到合约调用链。

Solidity 有三种处理错误的方式:requireassert*revert*

require
require(condition);
require(condition, "description");

require 语句声明了运行该函数的先决条件,即它声明了在执行代码之前应该满足的约束条件。它接受一个参数并在评估后返回一个布尔值,它也有一个自定义的字符串信息选项。如果是false,就会产生异常并终止执行。未使用的gas被返回给调用者,状态被逆转为原始状态。以下是require类型的异常被触发的一些情况。

  • 1、当require(条件表达式)被调用时,其条件表达式结果为false
  • 2、当一个被消息调用的函数没有正确结束时。
  • 3、当使用 new 关键字创建一个合约,而这个过程没有正常结束。
  • 4、当一个无代码的合约被定位到一个外部函数时。
  • 5、当使用公共getter方法将以太坊发送到合约时。
  • 6、当.transfer()方法失败时。
    • 6.1、当一个断言被调用时,其结果是假的。
    • 6.2、当一个函数的零初始化变量被调用时。
    • 6.3、当一个大值或负值被转换为一个枚举值时。
    • 6.4、当一个值被零除或模数化的时候。
    • 6.5、当在一个索引中访问一个数组,这个数组太大或者是负数。
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;

contract requireContract{
    
    function requireTest(uint256 _number) pure public {
        require(_number < 100, "number too big");
    }
 }

玩以太坊链上项目的必备技能(错误处理以及异常-Solidity之旅十四)

引入require语句的原因是为了使代码更具可读性。require 语句应在以下情况下使用。

  • 1、验证用户输入,正如我们上面所做的。
  • 2、在任何行动之前验证状态条件
  • 3、验证来自外部合同的响应
  • 4、检查溢出和欠溢出的情况
assert

assert语句也可以撤销所有的状态变化,但它会消耗事务中提供的所有气体,即使它没有被使用。这使得它不如revert或require宽松,因此使用频率较低。Assert是为 "真正糟糕的事情 "而存在的,应该只用于内部错误。

assert应该在以下情况下使用。

  • 1、检查常量,如this.balance > totalSupply
  • 2、执行后验证状态
  • 3、溢出和下溢检查,如果revert/require不合适的话

Solidity 在某些情况下自动创建assert型异常。

  • 1、被零除法或模数
  • 2、访问一个超出其界限的数组
  • 3、将过大或负数转换为枚数
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;

contract assertContract{
    
    function assertDiv(int x,int y) pure public returns(int){
        assert(y == 0);
        return x / y;
    }
 }

玩以太坊链上项目的必备技能(错误处理以及异常-Solidity之旅十四)

revert

revert语句将停止执行并撤销所有的状态变化。剩余的gas将被退还给调用者(但到目前为止所使用的gas将消失)。revert允许返回一个值。这可以作为一个错误信息来澄清为什么执行revert语句。

有两种方式来触发revert

revert CustomError(arg1, arg2)
revert("description")

第一种是自定义的error类型(Solidity 0.8.4 出现的新类型)。

使用自定义错误实例通常会比传递一个字符串作为描述在消耗gas方面更便宜。

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;

contract revertContract{
    //自定义错误实例
    error WrongNumber(uint256 threshold, uint256 number);
    string public status;
    uint256 myThreshold = 100;

    function revertString(uint256 _number) public{
        status = "error";
        if(_number > myThreshold){
            revert("Number too big");
        }
        status = "success";
    }
    function revertCustomError(uint256 _number) public{
        status = "error";
        if(_number > myThreshold){
            revert WrongNumber({ threshold: myThreshold, number: _number});
        }
        status = "success";
    }

 }

玩以太坊链上项目的必备技能(错误处理以及异常-Solidity之旅十四)

玩以太坊链上项目的必备技能(错误处理以及异常-Solidity之旅十四)

三种方式处理错误消耗 gas 对比

  • 1、revert 语句,从示例可以看出最终耗费了 46607 gas

玩以太坊链上项目的必备技能(错误处理以及异常-Solidity之旅十四)

  • 2、revert 语句与自定义 error 相配合,反而比比上述消耗跟多的gas,它最终消耗了46733 gas

玩以太坊链上项目的必备技能(错误处理以及异常-Solidity之旅十四)

  • 3、 我们在示例中使用了assert语句,明显比revert语句消耗gas更少,它最终消耗了22077 gas

玩以太坊链上项目的必备技能(错误处理以及异常-Solidity之旅十四)

  • 4、 我们最后来看看equire语句。第一种情况是带了错误描述,而它却又比assert语句少耗费了gas,从示例可以看出它消耗了21898 gas

玩以太坊链上项目的必备技能(错误处理以及异常-Solidity之旅十四)

  • 5、而require第二种情况,便是只有条件表达式,可以看出在示例中它以上几种情况消耗gas最少的,它最终消耗了21605 gas

玩以太坊链上项目的必备技能(错误处理以及异常-Solidity之旅十四)

从上述的示例可以看出,使用require语句检查错误,最终消耗的gas是最少的,这也证明了很多项目使用require的原因了。

try catch 捕获异常

现在我们可以用它们来处理外部函数调用的失败,而不需要回滚整个事务(被调用函数中的状态变化仍然会被回滚,但调用函数中的变化不会)。

//CharitySplitter.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;

contract CharitySplitter {
    address public owner;
    constructor (address _owner) {
        require(_owner != address(0), "no-owner-provided");
        owner = _owner;
    }
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
import "./CharitySplitter.sol";

contract CharitySplitterFactory {
    mapping (address => CharitySplitter) public charitySplitters;
    uint public errorCount;

    event ErrorHandled(string reason);
    event ErrorNotHandled(bytes reason);

    function createCharitySplitter(address charityOwner) public {
        try new CharitySplitter(charityOwner)
            returns (CharitySplitter newCharitySplitter)
        {
            charitySplitters[msg.sender] = newCharitySplitter;
        } catch {
            errorCount++;
        }
    }
}

玩以太坊链上项目的必备技能(错误处理以及异常-Solidity之旅十四)

try 关键词后面必须有一个表达式,代表外部函数调用或合约创建( new ContractName())。

在表达式上的错误不会被捕获(例如,如果它是一个复杂的表达式,还涉及内部函数调用),只有外部调用本身发生的revert 可以捕获。 接下来的 returns 部分(是可选的)声明了与外部调用返回的类型相匹配的返回变量。 在没有错误的情况下,这些变量被赋值,合约将继续执行第一个成功块内代码。 如果到达成功块的末尾,则在 catch 块之后继续执行。

Solidity 根据错误的类型,支持不同种类的捕获代码块:

  • 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子句。

有计划在未来支持其他类型的错误数据。 ErrorPanic 字符串目前是按原样解析的,不作为标识符处理。

为了捕捉所有的错误情况,你至少要有子句 catch { ... }catch (bytes memory lowLevelData) { ... }.

returnscatch 子句中声明的变量只在后面的块的范围内有效。文章来源地址https://www.toymoban.com/news/detail-427987.html

到了这里,关于玩以太坊链上项目的必备技能(错误处理以及异常-Solidity之旅十四)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 在本地以太坊私链上,使用go调用智能合约,获取事件日志

    完整go项目文件目录      

    2024年02月11日
    浏览(58)
  • 谁在以太坊区块链上循环交易?TuGraph+Kafka的0元流图解决方案

    都在说数据已经成为新时代的生产资料。 但随着大数据和人工智能等技术的发展,即便人们都知道数据的价值日益凸显,却无法凭借一己之力获取和分析如此大规模的数据。 要想富,先修路。要想利用新时代的数据致富,也必须要有趁手的工具。只有合适的工具才能完成大

    2024年02月11日
    浏览(40)
  • Sui链上事务处理概述

    Sui通过其混合式交易处理方法,实现比其他区块链更快速和高效的性能。这种方法使得Sui的交易测试吞吐率达到每秒297,000次。从实际应用的角度来看,使用Sui的用户在apps和游戏中几乎能够获得实时响应。 在区块链世界中,交易是apps运作的基础,因为许多应用中的操作会引发

    2024年02月16日
    浏览(40)
  • OpenWrt新手必备之技能

    Openwrt 的生态相当复杂,初次接触的人会感受到不少困惑,应该选择什么样的硬件,从哪里找到合适的固件,需要安装哪些插件,自自己动手还是拿来主义,系统不小心搞坏了怎么重置,如何更新或切换其他的固件,下面我们就这些困惑逐步展开解释。 首先你需要一个支持

    2023年04月18日
    浏览(39)
  • 程序员必备技能之调试

    目录 前言 本期内容介绍 一、什么是Bug? 二、调试以及调试的重要性 2.1什么是调试? 2.2调试的基本步骤 ​三、Debug和Release介绍 Debug和Release 四、windows环境下的调试介绍 4.1调试环境 4.2一些调试常用的快捷键 4.3调试时查看当前程序的信息 a、查看临时变量的值 b、查看程序的

    2024年02月10日
    浏览(69)
  • 电源工程师必备技能汇总

    三、熟练运用Maxwell、JMAG、FLUX、Saber其中一种磁件仿真软件,能够利用仿真定性、定量分析,利用仿真指导磁性器件设计; . 负责开关电源产品的设计与开发; 2. 负责产品总体方案设计,包括方案选型、器件选型、可靠性设计、原理图/PCB设计等; 3. 负责产品的测试与验证,

    2024年02月07日
    浏览(49)
  • 软件测试必备7大技能

    1.在测试中最重要的文档,他是测试工作的核心,是一组在测试时输入输出的标准,是软件需求的具体对照。编写测试用例,是测试人员的基本功,真正能写好的人并不多。 2.测试用例包含的内容:用例编号,用例名称,测试背景,前置条件,优先级,测试数据,测试步骤,

    2024年02月08日
    浏览(40)
  • (必备技能)使用Python实现屏幕截图

    在csdn上有很多纯copy,这给我复现带来了很大的麻烦,所以我想根据我的个人找截图的经历记录下来,给未来的自己看,免得忘记了云云。 由于我比较喜欢用opencv处理图像,所以截屏最后都会附带一个使用opencv显示图片的一个步骤。 1、下载pyautogui包 注:使用pyautogui方法获取

    2024年02月07日
    浏览(46)
  • 性能测试必备监控技能windows篇

    在手头没有专门的第三方监控时,该怎么监控服务指标呢?本篇就windows下监控进行分享,也是我们在进行性能测试时,必须掌握的。下面我们就windows下常用的三种监视工具进行说明: 任务管理器 资源监视器 性能监视器 在[开始] - [开始搜索]框中输入 taskmgr 打开任务管理 r

    2024年02月14日
    浏览(33)
  • FPGA开发必备技能:MATLAB应用

    FPGA开发必备技能:MATLAB应用 FPGA作为一种高性能的可编程逻辑器件,在各个领域都有着广泛的应用。而MATLAB作为一个重要的科学计算软件,也能够在FPGA开发中发挥巨大的作用。本文将介绍如何使用MATLAB来进行FPGA开发。 首先,我们需要了解MATLAB的特点和功能。MATLAB是一款强大

    2024年03月16日
    浏览(51)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包