本文へスキップ
BecomeCoder

Solidityコース · 第4章 制御・お金・安全性 ― require・payable・event · レッスン17

require と modifier ― ガードを付ける

ブラウザで完結

導入

お金を扱うコントラクトでは、「条件を満たさなければ処理そのものを取り消す」ことが安全の要です。それを担うのが 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 が巻き戻すと、その関数の中で行った状態変更はすべてなかったことになります(オール・オア・ナッシング)。「途中まで残高を減らしたのに送金で失敗して不整合」という事故が起きないのは、この巻き戻しのおかげです。

実際に動かしてみよう

下のエディタのコントラクトを「コンパイル & デプロイ」すると、ブラウザ内で本物のSolidityコンパイラ(solc)がコンパイルし、EVM(@ethereumjs)にデプロイされます。デプロイ後は public な関数がボタンになって並ぶので、引数を入れて呼び出すと戻り値・イベント・require のエラー(revert理由)まで確認できます。本文のコントラクトを書き換えて動きを試しながら進めましょう(コンパイラ本体は初回だけ読み込みに数秒かかります)。

Solidity — ブラウザ内でコンパイル & 実行

ブラウザ内で本物のSolidityコンパイラ(solc)とEVM(@ethereumjs)を動かす環境を読み込みます(コンパイラ本体は初回のコンパイル時に読み込むため、最初の実行だけ数秒かかります)。
スクロールして表示された時点でも自動で読み込まれます。