iT邦幫忙

2024 iThome 鐵人賽

DAY 13
0
自我挑戰組

Solidity 初學之路系列 第 13

DAY 13 - 異常

  • 分享至 

  • xImage
  •  

異常

Solidity 有三種拋出異常的方法:errorrequireassert,三種方法的 gas 消耗不同。寫智能合約常常會出 bug,Solidity 中的異常指令能幫我們 debug。

Error

error 是 solidity 0.8.4 版本新加的內容,方便且高效(省 gas)地向使用者解釋操作失敗的原因,同時還可以在拋出異常的同時攜帶參數,幫助開發者更好地 debug。人們可以在 contract 之外定義異常。
定義一個 TransferNotOwner 異常,當使用者不是代幣 owner 的時候嘗試轉賬,會拋出錯誤:

error TransferNotOwner(); // 自訂 error

我們也可以定義一個攜帶參數的異常,來提示嘗試轉帳的帳戶地址:

error TransferNotOwner(address sender); // 自訂帶參數的 error

在執行當中,error 必須搭配 revert 指令使用。

function transferOwner1(uint256 tokenId, address newOwner) public {
    if(_owners[tokenId] != msg.sender){
        revert TransferNotOwner();
        // revert TransferNotOwner(msg.sender);
    }
    _owners[tokenId] = newOwner;
}

我們定義了一個 transferOwner1() 函數,它會檢查代幣的 owner 是不是發起人,如果不是,就會拋出 TransferNotOwner 異常;如果是的話,就會轉帳。

Require

require 指令是 solidity 0.8 版本之前拋出異常的常用方法,目前許多主流合約仍然還在使用它。它很好用,唯一的缺點就是 gas 隨著描述異常的字串長度增加,比 error 指令要高。使用方法:require (檢查條件,"異常的描述"),當檢查條件不成立的時候,就會拋出異常。
require 指令重寫上面的 transferOwner1 函數:

function transferOwner2(uint256 tokenId, address newOwner) public {
    require(_owners[tokenId] == msg.sender, "Transfer Not Owner");
    _owners[tokenId] = newOwner;
}

Assert

assert 指令一般用於程式設計師寫程式 debug,因為它不能解釋拋出例外的原因(比 require 少個字串)。它的用法很簡單,assert (檢查條件),當檢查條件不成立的時候,就會拋出異常。
assert 指令重寫上面的 transferOwner1 函數:

function transferOwner3(uint256 tokenId, address newOwner) public {
    assert(_owners[tokenId] == msg.sender);
    _owners[tokenId] = newOwner;
}

程式碼

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

error TransferNotOwner(); // 自訂帶參數的 error
// error TransferNotOwner(address sender); // 自訂帶參數的 error

contract ErrorContract{
    mapping(uint256 => address) private _owners; // 紀錄每個 tokenId 的 owner

    // 1. Error 方法
    function transferOwner1(uint256 tokenId, address newOwner) public {
        if(_owners[tokenId] != msg.sender){
            revert TransferNotOwner();
            // revert TransferNotOwner(msg.sender);
        }
        _owners[tokenId] = newOwner;
    }

    // 2. require 方法
    function transferOwner2(uint256 tokenId, address newOwner) public {
        require(_owners[tokenId] == msg.sender, "Transfer Not Owner");
        _owners[tokenId] = newOwner;

    }

    function transferOwner3(uint256 tokenId, address newOwner) public {
        assert(_owners[tokenId] == msg.sender);
        _owners[tokenId] = newOwner;
    }

}

說明


Error:當地址不符合條件時會 revert 交易,並拋出 TransferNotOwner 的錯誤。耗費 27185 gas。


Require:當地址不符合條件時會 revert 交易,並印出錯誤字串,錯誤字串越長耗費的 gas 比 Error 多越多。耗費 27229 gas。


Assert:當地址不符合條件時只拋出了異常並 revert 交易,並不知道異常原因。耗費 27207 gas。

比較

理論上消耗的 gas 應該是:
require 方法 > assert 方法 > error 方法帶參數 > error 方法無參數


上一篇
DAY 12 - 抽象合約、介面
下一篇
DAY 14 - 函數重載、庫合約
系列文
Solidity 初學之路30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言