Solidity

是否可以在 Solidity 中執行 try-catch?

  • April 12, 2021

Solidity 是否支持try-catch功能?如果是這樣,它是如何工作的?有什麼限制?

Solidity 0.6.0 及更高版本(2020 年更新)

從 Solidity 0.6.0 開始,語言中內置了 try-catch 功能。語法類似於現有語言,但功能目前僅限於try表示外部函式呼叫或合約創建的表達式。

try部分try-catch功能只能是上述表達方式。不會擷取塊**的****反轉。try**這意味著以下程式碼將導致交易恢復(完整範例見下文):

try feed.getData(token) returns (uint v) {
   require(1 == 2, "Will revert");  // This will cause the transaction to revert!!
} catch (bytes memory /*lowLevelData*/) {
   return (0);  // Will not get here
}

目前,Solidity 支持兩種類型的catch塊:

  1. catch Error (string memory /*reason*/)
  • 如果錯誤是由revert("reasonString")or require(false, "reasonString") (或導致此類異常的內部錯誤)引起的,則將執行catch該類型的子句catch Error(string memory reason)
  1. catch (bytes memory /*lowLevelData*/)
  • catch塊處理所有其他情況。如果錯誤簽名與任何其他子句不匹配,則執行此塊,在解碼錯誤消息期間出現錯誤,如果在外部呼叫中存在失敗的斷言(例如由於被零除或失敗assert()),或者如果沒有提供異常數據。

Solidity 計劃在未來支持其他類型的錯誤數據。

如果您對錯誤數據不感興趣,您可以使用catch { ... }(甚至作為唯一的 catch 子句)。

注意:這篇文章比我在這裡的文章更深入。

例子

簡化範例

pragma solidity ^0.6.0;
   
   interface AddNumbers { function add(uint256 a, uint256 b) external returns (uint256 c); }

contract Example {
   AddNumbers addContract;
   event StringFailure(string stringFailure);
   event BytesFailure(bytes bytesFailure);

   function exampleFunction(uint256 _a, uint256 _b) public returns (uint256 _c) {

       try addContract.add(_a, _b) returns (uint256 _value) {
           return (_value);
       } catch Error(string memory _err) {
           // This may occur if there is an overflow with the two numbers and the `AddNumbers` contract explicitly fails with a `revert()`
           emit StringFailure(_err);
       } catch (bytes memory _err) {
           emit BytesFailure(_err):
       }
   }
}

完整範例(來自 Solidity 文件)

pragma solidity ^0.6.0;

interface DataFeed { function getData(address token) external returns (uint value); }

contract FeedConsumer {
   DataFeed feed;
   uint errorCount;
   function rate(address token) public returns (uint value, bool success) {
       // Permanently disable the mechanism if there are
       // more than 10 errors.
       require(errorCount < 10);
       try feed.getData(token) returns (uint v) {
           return (v, true);
       } catch Error(string memory /*reason*/) {
           // This is executed in case
           // revert was called inside getData
           // and a reason string was provided.
           errorCount++;
           return (0, false);
       } catch (bytes memory /*lowLevelData*/) {
           // This is executed in case revert() was used
           // or there was a failing assertion, division
           // by zero, etc. inside getData.
           errorCount++;
           return (0, false);
       }
   }
}


堅固性 < 0.6.0

對於 0.6.0 之前的 Solidity 版本,try-catch語言中沒有內置功能。但是,考慮到當時可用的語言特性,有一些方法可以執行類似的功能。

通過遵循Polymath 的 Mudit Gupta 的本教程try,您可以在沒有andcatch關鍵字的情況下非常接近地模仿 try-catch 功能:

pragma solidity ^0.5.0;

import "./Token.sol";

/**
* @dev This contract showcases a simple Try-catch call in Solidity
*/
contract Example {
   Token public token;
   uint256 public lastAmount;

   constructor(Token _token) public {
       token = _token;
   }

   event TransferFromFailed(uint256 _amount);

   function tryTransferFrom(address _from, address _to, uint256 _amount) public returns(bool returnedBool, uint256 returnedAmount) {
       lastAmount = _amount; // We can query this after transferFrom reverts to confirm that the whole transaction did NOT revert
       // and the changes we made to the state are still present.

       (bool success, bytes memory returnData) =
           address(token).call( // This creates a low level call to the token
               abi.encodePacked( // This encodes the function to call and the parameters to pass to that function
                   token.transferFrom.selector, // This is the function identifier of the function we want to call
                   abi.encode(_from, _to, _amount) // This encodes the parameter we want to pass to the function
               )
           );
       if (success) { // transferFrom completed successfully (did not revert)
           (returnedBool, returnedAmount) = abi.decode(returnData, (bool, uint256));
       } else { // transferFrom reverted. However, the complete tx did not revert and we can handle the case here.
           // I will emit an event here to show this
           emit TransferFromFailed(_amount);
       }
   }
}

引用自:https://ethereum.stackexchange.com/questions/78562