2023-05-09|閱讀時間 ‧ 約 7 分鐘

【開發智能合約 — Solidity系列】實作篇Ep.5 - 錯誤處理的機制(Error Handling)

Solidity語言的錯誤檢查提供了Require()、Revert()、Assert(),這三種方便的API調用,而這三種用途分別不同,畢竟牽涉到瓦斯費的問題,因此才會與過往的程式語言有些許的差異,

Require

require()通常會被使用在輸入值的驗證檢查,因為它的特性主要是能夠退回剩餘的Gas fee,我們也知道瓦斯費在區塊鏈的成本是昂貴的,因此這些檢查都有助於不必要的浪費,主要使用方式為:
require(判斷式, 訊息)
...
contract Example {
   ...    function withdraw(uint amount) public pure {
       uint total = 100;
       /// @dev 提款的數量必須低於總額度
       /// @notice 使用require(), 當條件不滿足時將退回剩下的瓦斯費,所以通常會放在前面
       require(amount <= total, "amount must less then total");       ...
   }
}

Revert

revert()顧名思義為撤銷的意思,也就是通常不符合某些條件時則進行撤銷,與reqiure一樣是會退回剩餘的Gas fee,那看到這邊可能心裡會產生一些疑問,比如說為什麼不用require就好了呢? 兩者語意事實上是有差異的。
  • require表述的是「必須要滿足什麼條件」。
  • revert則表述「當什麼條件未被滿足」時進行撤回。
/// @dev 自訂錯誤類型: 資金不足
/// @param requested 要求的資金
/// @param available 可用的資金
error NotEnoughFunds(uint requested, uint available);function ... {
/// @dev 提款的金額不能大於存款
/// @notice 若提出的金額超過存款總額,則退回撤銷並退回狀態。
if (amount > total) {
revert NotEnoughFunds(amount, total);
}
}
另外revert使用的方式有以下三種:
  • revert CustomError(…)
  • revert(”message”)
  • revert()
剛開始可能會有些霧裡看花,這麼多不同的承接方式與參數,但撰寫一段時間過後就會相當熟練,且在合適的時機使用正確的方式。

Assert

assert()跟require()一樣,算是進行檢核的機制,必須滿足某些條件才能繼續執行,但最大的差異在於當條件不滿足時,會耗盡Gas fee,因此通常用來處理比較嚴重且不易發生的錯誤,例如邊界值、特殊條件…等程式內部錯誤,也是最不常被使用的錯誤處理方法。
/// @dev 提款的額度必須是正整數
assert(amount > 0);

Try … Catch

相信這種語法如果有在開發其他程式的朋友應該非常熟悉,沒錯,Solidity亦提供這種錯誤處理機制,但值得注意的是在智能合約的世界中僅適合外部調用,白話來說就是A合約去嘗試使用B合約的功能,語法結構主要為:
try ...使用A合約進行什麼操作 {
  ...成功之後的處理
} catch ...什麼類型的錯誤 {
...進行什麼處理
} catch ...什麼類型的錯誤 {
...進行什麼處理
}
而錯誤的捕捉類型又分為以下四種方式,分別說明:
contract Example {
   ...
}contract Runner {
   Example public example;    constructor() {
       example = new Example();
   }    function exec() public view {
       uint errorCount = 0;
       try example.withdraw(101) {
           // 提款成功之後...
       }  catch Error(string memory /*reason*/) {
           // 這種錯誤類型主要是處理帶有錯誤訊息的錯誤處理函數: require(..., "錯誤訊息")、revert("撤銷訊息")
           errorCount++;
       } catch Panic(uint /*errorCode*/) {
           // 這種錯誤類型主要處理assert(...)這種內部錯誤
           errorCount++;
       } catch (bytes memory /*lowLevelData*/) {
           // 這種錯誤通常發生在更低階的處理,像是在解譯(decode)的階段
           errorCount++;
       } catch {
         // 如果不想知道錯誤訊息或者原因時,可以直接用catch { ... }進行錯誤的對應處理。
       }
   }
}

結語

原來一個簡單的錯誤處理背後其實並不簡單…,與「金錢」有關的產品或服務最重要的莫過於嚴謹的審查機制,因此很多錯誤都是不能被容忍的,故而錯誤處理的機制就顯得非常重要,處理方式也相較於大部分程式語言有所不同,主要是因為納入了Gas fee的緣故,以此為鑑,當開發者進行開發智能合約時的錯誤處理也要非常小心,哪個使用情境要用哪一種錯誤處理的技巧並且不浪費Gas fee的狀況下,真的是對於智能合約開發者來說是一大挑戰,相信我們只要讀懂這些錯誤處理的概念後,未來開發時就會特別注意。
今天的範例都在這裡「📦 solidity-remix-toturial/Ep5」歡迎自行取用。

資源參考

分享至
成為作者繼續創作的動力吧!
主軸圍繞於軟體科技, 除了過往經驗成章以外也持續學習新技能, 並將學習心法記錄與分享, 以期幫助相同道路之夥伴。 裡面包含著各種程式語言的疑難雜症解題技巧, 也提供資料庫、AI、認證與授權、工具庫...等技巧, 讓您自由找出您想要的解答, 如果您想要系統化的教學課程也歡迎至「🔒 阿Han的軟體心法實戰營」。
© 2024 vocus All rights reserved.