可視性と修飾子
持ち主だけが操作できるボタンの作り方
前回までの自動販売機には問題があります。誰でも setPrice で値段を変えられてしまうのです。今回は「持ち主だけが操作できるボタン」の作り方を学びます。
可視性 = 誰が使えるか
変数や関数に毎回付けてきた public。これは可視性——「誰がそれを使えるか」の指定です。4 種類あります。
| 指定 | 意味 |
|---|---|
public | 誰でも外から使える |
private | このコントラクトの中からしか使えない |
internal | このコントラクトと、その子ども(第7回の継承)から使える |
external | 外からしか使えない(中からは使わない前提のボタン) |
まずは「外に見せていいものは public、中だけの部品は private」と覚えれば十分です。
require = 門番
require(リクワイア)は、「条件を満たさなければ、そこで処理を中止して巻き戻す」命令です。関数の入り口に立つ門番だと思ってください。
require(条件, "条件を満たさないときのメッセージ");
条件が成り立てばなにもなかったように次へ進み、成り立たなければその操作全体が無かったことになります(途中まで書き換えた記録も巻き戻ります)。
modifier = 使い回せる門番
同じ門番を何個ものボタンに立てたいとき、毎回 require を書くのは大変です。そこでmodifier(モディファイア)——「名前をつけた門番」を作ります。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract VendingMachine {
address public owner;
uint256 public price = 120;
constructor() {
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner, "not owner");
_;
}
function setPrice(uint256 newPrice) public onlyOwner {
price = newPrice;
}
}
modifier onlyOwner()= 「onlyOwner という名前の門番」を定義しています。中身は「操作している人(msg.sender)が持ち主(owner)でなければ止める」_;は「門番のチェックを通ったら、ここから本来の手順を実行する」という差し込み位置の印ですfunction setPrice(...) public onlyOwnerのように、関数に門番の名前を添えるだけで、そのボタンに門番が立ちます
== は「左と右が等しいか?」の質問です。=(入れる)と別物なので注意。
:::note Lab では onlyOwner が「全員通って」しまいます Lab のボタン操作は、みなさんの代わりに Lab の代行送信者(運営側のアカウント)がブロックチェーンへ送る仕組みです。デプロイも実行も同じ代行送信者なので、owner チェックは常に成立します(=「他人だから弾かれる」体験は Lab ではできません)。書き方と意味を覚えるのが今回のゴールです。本物の世界では、ウォレットごとに msg.sender が異なるので、この門番が資産を守る要になります。 :::
門番に止められる体験をする
せっかくなので、require に止められる体験もしましょう。「値段 0 円はおかしい」という門番を足します。
function setPrice(uint256 newPrice) public onlyOwner {
require(newPrice > 0, "price must be positive");
price = newPrice;
}
これをデプロイして、実行画面で setPrice に 0 を入れて実行すると——ログに ❌ とエラーが出て、price() は元の値のまま。巻き戻しが起きた証拠です。
今回覚えたこと
- 可視性 = 誰が使えるか。まずは public(公開)と private(内部用)
- require(条件, "メッセージ") = 門番。条件を満たさなければ操作全体を巻き戻す
- modifier = 名前をつけた使い回せる門番。
_;が本体の差し込み位置
次回は、自動販売機に「売れたら音を鳴らす」——イベントを付けます。
:::tip Lab でやってみる
上の require 入りコードをデプロイし、①setPrice に 150 → 成功、price() が 150 に ②setPrice に 0 → ❌ で巻き戻り、price() は 150 のまま、を確かめましょう。エラーもまた正しい動作です。操作方法: コントラクトを動かす
:::