ある関数(またはメソッド)を実行する前に必ず実行しておきたい処理があるケースがたまにあります。
Railsで言えばbefore_actionのようなものでしょうか。
Solidityでこれを実現するには関数修飾子(modifier)を使用します。
関数修飾子は直接呼び出すことはできず、関数定義の最後に関数修飾子の名前を指定することで、その関数が実行される前に指定した関数修飾子が実行されることになる。
と言っても分かりにくいので、例を以下に記載します。
引数がない場合
まずは関数修飾子(modifier)を定義します。
こちらはOpenZeppelinというSolidityライブラリのOwnableコントラクトの一部を抜粋したものです。
contract Ownable {
address public owner;
// これはコンストラクタ。コントラクトが最初に作成された時に、1度だけ実行される
function Ownable() public {
owner = msg.sender;
}
// 関数修飾子の定義。関数を実行したアドレスがオーナーのものであるかをチェックしている
modifier onlyOwner() {
require(msg.sender == owner);
// ここに到達すると呼び出し元の関数に戻ってコードが実行される
_;
}
}
関数修飾子が定義されたので、この関数修飾子を使用してみます。
contract MyContract is Ownable {
mapping(address => uint) mynumbers;
// この関数のコードを実行する前に関数修飾子'onlyOwner'が実行される
function setMyNumber(uint _number) external onlyOwner {
mynumbers[msg.sender] = _number;
}
}
ここではsetMyNumber関数に関数修飾子'onlyOwner'を付けています。
そのため、setMyNumber関数が呼ばれる前にOwnableコントラクトの関数修飾子onlyOwnerが実行されます。
よってこの例ではsetMyNumber関数はOwnableコントラクトを作成したオーナーしか実行できないことになります。
このように関数を実行できる人を限定したいとか、関数実行前に実行しておきたい処理は関数修飾子として定義しておくと何かと便利です。
引数がある関数修飾子
関数修飾子は引数を受け取ることもできます。
引数を受け取るパターンも例を記載しておきます。
// ユーザーの年齢を格納する
mapping (uint => uint) public age;
// ユーザーの年齢が一定の年齢以上であることをチェックする関数修飾子
modifier checkAge(uint _age, uint _userId) {
require (age[_userId] >= _age);
_;
}
// checkAgeで20歳以上であることをチェックしてから関数のロジックを実行する
function canDrink(uint _userId) public checkAge(20, _userId) {
// 関数のロジック
}
この例の通り、canDrinkで受け取った引数を関数修飾子checkAgeの引数にそのまま渡すことができます。
事前に引数の値チェックをしておきたいケースなどでも使えそうですね。