阿Han
阿Han

文字是留下記錄的一種媒介,將知識吸收轉化後輸出成文字進行保存。 ☕️ https://liker.land/willhanchen/civic

【開發智能合約 — 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」歡迎自行取用。

下一篇我們來談談可視範圍吧:

⏭️【開發智能合約 — Solidity系列】實作篇Ep.6 — 關於可視範圍(Visibility)

📚 更多關於Solidity的文章請看這裡…


喜歡撰寫文章的你,不妨來了解一下:

Web3.0時代下為創作者、閱讀者打造的專屬共贏平台 — 為什麼要加入?

歡迎加入一起練習寫作,賺取知識,累積財富!

資源參考

CC BY-NC-ND 2.0 版权声明

喜欢我的文章吗?
别忘了给点支持与赞赏,让我知道创作的路上有你陪伴。

加载中…
加载中…

发布评论