導入
お金を扱うコントラクトでは、「条件を満たさなければ処理そのものを取り消す」ことが安全の要です。それを担うのが require と、条件チェックを再利用する modifier です。
説明
require(条件, メッセージ) は、条件が false なら処理全体を巻き戻します(revert)。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract Guarded {
address public owner;
constructor() {
owner = msg.sender;
}
// 条件チェックを名前付きで再利用する修飾子
modifier onlyOwner() {
require(msg.sender == owner, "owner only");
_; // ここに、修飾子を付けた関数の本体が入る
}
uint256 public price;
// onlyOwner が通った人だけが実行できる
function setPrice(uint256 _price) public onlyOwner {
require(_price > 0, "price must be positive");
price = _price;
}
}
require(条件, "メッセージ")… 条件が偽なら処理を巻き戻し、消費前の状態に戻す(=台帳は一切変わらない)。使ったガスは戻らない。modifier onlyOwner() { ... _; ... }… よく使う条件チェックに名前を付けて使い回す。_;の位置に、修飾子を付けた関数の本体が展開される。function setPrice(...) public onlyOwner… 実行前にonlyOwnerのチェックが自動で挟まる。
flowchart TD
A[setPrice を呼ぶ] --> B{onlyOwner:<br/>msg.sender == owner?}
B -- いいえ --> R["revert 'owner only'<br/>状態は元のまま"]
B -- はい --> C{require:<br/>_price > 0?}
C -- いいえ --> R2["revert 'price must be positive'"]
C -- はい --> D[price を更新]
「チェックを最初に置き、通らなければ何もなかったことにする」――この Fail-fast な作りが、コントラクトの資産を守ります。エラー処理には他に revert(条件式なしで直接巻き戻す)、assert(本来起きてはならない不変条件の検査)もあります。
やってみよう
require が巻き戻すと、その関数の中で行った状態変更はすべてなかったことになります(オール・オア・ナッシング)。「途中まで残高を減らしたのに送金で失敗して不整合」という事故が起きないのは、この巻き戻しのおかげです。