エラー処理
巻き戻しの意味と 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 の門番は二段構えです。
0以下 → require が止める(メッセージ: value must be positive)1000超 → custom errorTooBigが止める(ダメだった値も一緒に返す)- 1〜1000 → 通過して記録
今回覚えたこと
- 書き込みは「全部成功 or 全部巻き戻し(revert)」。中途半端は起きない
- require = 入り口チェックの基本 / revert + custom error = 名前と情報つきの巻き戻し / assert = バグ検出の最終防衛線
- custom error は
error 名前(情報);で宣言、revert 名前(値);で発動
次回はいよいよ総復習——Lab の実例コントラクト PulseCheck を、1 行ずつ読み解きます。ここまでの 8 回分の知識で、実物のコードが全部読めるはずです。
:::tip Lab でやってみる
SafeBox をデプロイして三段階を体験: ①setValue に 500 → 成功、value() が 500 ②0 → ❌ require のメッセージ ③2000 → ❌ TooBig。②③のあと value() が 500 のままであること(巻き戻しの証拠)を必ず確認しましょう。操作方法: コントラクトを動かす
:::