solidity异常俘获(非常实用)

用于捕获并处理合约调用或创建过程中可能发生的异常。

但只适用于 外部函数调用 和合约创建调用。 可以使用 恢复状态 来创建错误。

英语部分

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块用于捕获由revertrequire引起的高级别错误,并可以获取错误的描述信息。
  • 第二个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) {
      // ...
    }
  }
}

  • 7
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值