Nobiq
Nobiq

エラー処理

巻き戻しの意味と require の仲間たち

第4回で「門番 require」を覚えました。今回はエラー処理を一段くわしく——巻き戻しの意味と、門番の仲間たち(revert・custom error・assert)をまとめます。

巻き戻し(revert)= 全部なかったことに

ブロックチェーンの書き込みは「全部成功するか、全部なかったことになるか」の二択です。途中で門番に止められたら、そこまでに書き換えた記録もすべて元に戻ります。

自販機でいえば「お金を入れたのにジュースが出なかったら、お金がそのまま返ってくる」。中途半端な状態(お金は取られたのにジュースなし)が起きないよう、仕組みで保証されているのです。この巻き戻しを revert(リバート)と呼びます。

ひとつだけ正確に: 本物の世界では、巻き戻っても手数料(ガス代)だけは戻りません。「処理を試した」こと自体への支払いだからです。Lab では手数料を運営が負担しているので(第3回の note 参照)、みなさんの目には見えませんが、頭の片隅に置いておいてください。

門番たちの使い分け

require — 入り口チェックの基本

require(newValue > 0, "value must be positive");

「利用者の入力や状況が正しいか」を関数の入り口で確かめる、いちばんよく使う門番。条件を満たさなければメッセージ付きで巻き戻します。

revert + custom error — 名前と情報つきの巻き戻し

error TooBig(uint256 given);          // 宣言(contract 直下)

if (newValue > 1000) {
    revert TooBig(newValue);          // 発動
}

custom error は「自分で名前をつけたエラー」です。イベントの宣言(event 名前(値))とそっくりですね——error 名前(持たせる情報); で宣言し、revert 名前(実際の値); で発動します。

require のメッセージ(ただの文字列)より、どんな値でダメだったかを構造的に伝えられるのが強み。新しめの書き方で、実際の開発ではこちらが主流になりつつあります。

assert — 「起きたら設計ミス」の最終防衛線

assert(sold <= 1000000);

assert は「プログラムが正しければ絶対に成り立つはず」の条件に使います。もし引っかかったら、利用者の入力ミスではなく作った人のバグ。初心者のうちは「そういう最終チェック用の道具がある」と知っていれば十分で、ふだんは require と revert を使います。

全部入りの SafeBox

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

contract SafeBox {
    error TooBig(uint256 given);

    uint256 public value;

    function setValue(uint256 newValue) public {
        require(newValue > 0, "value must be positive");
        if (newValue > 1000) {
            revert TooBig(newValue);
        }
        value = newValue;
    }
}

setValue の門番は二段構えです。

  1. 0 以下 → require が止める(メッセージ: value must be positive)
  2. 1000 超 → custom error TooBig が止める(ダメだった値も一緒に返す)
  3. 1〜1000 → 通過して記録

今回覚えたこと

  • 書き込みは「全部成功 or 全部巻き戻し(revert)」。中途半端は起きない
  • require = 入り口チェックの基本 / revert + custom error = 名前と情報つきの巻き戻し / assert = バグ検出の最終防衛線
  • custom error は error 名前(情報); で宣言、revert 名前(値); で発動

次回はいよいよ総復習——Lab の実例コントラクト PulseCheck を、1 行ずつ読み解きます。ここまでの 8 回分の知識で、実物のコードが全部読めるはずです。

:::tip Lab でやってみる SafeBox をデプロイして三段階を体験: ①setValue500 → 成功、value() が 500 ②0 → ❌ require のメッセージ ③2000 → ❌ TooBig。②③のあと value() が 500 のままであること(巻き戻しの証拠)を必ず確認しましょう。操作方法: コントラクトを動かす :::