本篇文章介紹 Solidity 中的 Custom Error 用法,包含定義錯誤、用法和錯誤處理的方式。
用法
Solidity v0.8.4 之後新增這個功能,能夠用自訂的錯誤來回傳錯誤原因。在之前我們一般使用以下方式丟出錯誤:
1 2 3 4 5 6 7
| require(shouldPass, "MyError");
if (!shouldPass) { revert("MyError"); }
|
使用 Custom Error 用法如下:
1 2 3 4 5 6 7 8 9 10 11 12
| error MyError();
contract MyContract { function test() external { if (!shouldPass) { revert MyError(); } } }
|
Custom Error 也可以帶參數
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| error MyErrorWithArgs(uint status);
contract MyContract { function test() external { if (case1) { revert MyErrorWithArgs(0); } else if (case2) { revert MyErrorWithArgs(1); } } }
|
其實可以下面的用法想成是一樣的
1 2 3 4 5
| revert("MyError");
// 相當於 // error Error(string reason); // revert Error("MyError");
|
不過實際上 Error 和 Panic 的名稱是特殊名稱,不可以使用。
例外處理
在合約要使用 Try Catch 來捕捉 Custom Error 比較複雜,目前可以用以下方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| contract TryCatch { MyContract myContract = new MyContract();
function execute() external { try myContract.callSomeMethod() { } catch (bytes memory reason) { bytes4 selector = bytes4(reason); if (selector == bytes4(MyError.selector)) { } else if (selector == bytes4(MyError.selector)) {
bytes32 hash = keccak256(reason); if (hash == keccak256(abi.encodeWithSelector(MyErrorWithArgs.selector, 0))) { } else if (hash == keccak256(abi.encodeWithSelector(MyErrorWithArgs.selector, 1))) { } } else { } } } }
|
優缺點
優點
- 編譯的合約大小比較小。
- 執行消耗的 Gas 低一點。
- 可以帶參數。
缺點
- 需要節點支援:例如本地開發用的 Ganache 不支援,RPC 回傳的內容無法解析是什麼錯誤。
- 需要區塊瀏覽器支援顯示:目前瀏覽器似乎無法解析這類型錯誤,無法知道是什麼錯誤。
- web3.js 和 ether.js 支援不夠完整,需要額外處理。
延伸閱讀
使用 Ethers.js 解析 Custom Error
使用 Web3.js 解析 Custom Error