Solidity
是否可以在 Solidity 中執行 try-catch?
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
塊:
catch Error (string memory /*reason*/)
- 如果錯誤是由
revert("reasonString")
orrequire(false, "reasonString")
(或導致此類異常的內部錯誤)引起的,則將執行catch
該類型的子句catch Error(string memory reason)
。
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); } } }