広告

solidity modifierとrequireの違いと使い方

Solidityのmodifierとrequireの違い

概要

はい、Solidityにおけるmodifierとrequireの違いと使い方について解説します。

簡単に言うと、modifierは関数の振る舞いを「修飾」するための再利用可能なコードで、requireは関数内で特定の条件を「検証」するためのステートメントです。

modifierrequire の違い

特徴modifier (修飾子)require (検証)
目的関数の実行前後に共通のチェックや処理を追加する(コードの再利用)関数内の特定の時点で、条件が真であるか検証する
使い方関数定義に付与して使用する関数本体の中に記述する
主な用途アクセス制御(例:onlyOwner)、状態チェックの共通化入力値の検証、関数の実行条件チェック
コード構造独立したコードブロックとして定義関数内の1行のステートメント
エラー処理内部でrequireなどを用いて条件をチェックし、失敗するとリバート条件がfalseの場合、トランザクションをリバート(実行を取り消す)

modifier の使い方

modifierは、特に関数の実行権限をチェックするような、複数の関数で繰り返し使われるロジックをまとめるのに非常に便利です。これにより、コードがDRY (Don't Repeat Yourself) になり、可読性と保守性が向上します。

modifierの重要な要素は _ (アンダースコア) です。これは「修飾された関数の本体コード」を表し、modifier内のロジックのどこで元の関数を実行するかを示します。

具体例:onlyOwner

コントラクトの所有者だけが実行できる関数を定義する際によく使われます。

Solidity

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

contract OwnerExample {
    address public owner;

    // イベントを定義
    event OwnerChanged(address indexed oldOwner, address indexed newOwner);

    // modifierを定義
    modifier onlyOwner() {
        // msg.senderがownerでなければエラーメッセージと共にリバート
        require(msg.sender == owner, "Caller is not the owner");
        _; // このmodifierが付いた関数の本体がここで実行される
    }

    // コンストラクタで初期のownerを設定
    constructor() {
        owner = msg.sender;
    }

    // 新しいownerを設定する関数。onlyOwner modifierが付いている
    function changeOwner(address _newOwner) public onlyOwner {
        address oldOwner = owner;
        owner = _newOwner;
        emit OwnerChanged(oldOwner, _newOwner);
    }

    // 誰でも呼び出せる関数
    function getOwner() public view returns (address) {
        return owner;
    }
}

この例では、changeOwner関数にonlyOwner modifierが付与されています。これにより、changeOwnerが呼び出されると、まずonlyOwner内のrequireが実行され、呼び出し元がownerであることが確認されます。条件を満たした場合にのみ、_の部分、つまりchangeOwner関数本体のロジックが実行されます。


require の使い方

requireは、関数の実行を続けるための前提条件をチェックするために使います。条件が満たされない(falseになる)場合、トランザクションは即座に停止(リバート)され、残りのガスはユーザーに返還されます。

require(条件, "エラーメッセージ"); の形式で使います。

具体例:入金額のチェック

ユーザーがETHを入金する際に、その額が0より大きいことを保証する場面で使われます。

Solidity

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

contract VendingMachine {
    mapping(address => uint) public balances;

    // ETHを入金する関数
    function deposit() public payable {
        // requireを使って、入金額が0より大きいことを検証
        require(msg.value > 0, "Deposit amount must be greater than 0.");
        balances[msg.sender] += msg.value;
    }

    // 残高を引き出す関数
    function withdraw(uint _amount) public {
        // requireを使って、引き出し額が残高以下であることを検証
        require(_amount <= balances[msg.sender], "Insufficient balance.");
        
        balances[msg.sender] -= _amount;
        payable(msg.sender).transfer(_amount);
    }
}

この例では、deposit関数はmsg.value(送られてきたETHの量)が0より大きいことをrequireでチェックします。もし0ETHでこの関数を呼び出した場合、トランザクションは"Deposit amount must be greater than 0."というメッセージと共に失敗します。同様にwithdraw関数では、引き出し額が残高を超えていないかをチェックしています。


まとめ

  • modifier: 複数の関数にまたがる共通の事前・事後チェック(特にアクセス制御)に使います。コードをすっきりと保つための「飾り付け」のようなものです。
  • require: ある関数内での一度きりの特定の条件検証(入力値の正当性チェックなど)に使います。関数の実行を許可するための「関所」のようなものです。

これらを適切に使い分けることで、安全で読みやすいスマートコントラクトを記述することができます。

-EVM