広告

第9話:ブロックチェーンに記録を残す!「イベント (event)」で何が起きたか通知しよう

2025年7月31日

第9話:ブロックチェーンに記録を残す!「イベント (event)」で何が起きたか通知しよう

概要

コントラクト内部の「変化」を、どうやって外部のWebサイトに伝えるのか?その答えが、Solidityの「イベント」です。重要な出来事を安価なログとしてブロックチェーンに記録し、フロントエンドへリアルタイムに通知する仕組みを徹底解説。

eventの定義からemitでの放出、indexedによる検索性の向上まで、DAppと外部世界を繋ぐ通信ブリッジの役割を果たす必須技術をマスターします。

目次
Solidity学習講座:最終版 目次(全20話)
Solidity学習講座:最終版 目次(全20話)

基礎編 第1話:未来のインターネットへようこそ!Solidityとスマートコントラクトの全体像 第2話:準備は1分!ブラウザだけで開発できる「Remix IDE」の基本操作 第3話:記念すべき初コント ...

続きを見る

はじめに

第8話で、あなたはstructmappingを組み合わせ、プロフェッショナルなデータ管理術を身につけました。ユーザー情報をきれいに整理し、本格的なアプリケーションのデータ基盤を設計できるようになったのです。

しかし、ここで一つ、根本的な疑問が浮かび上がります。

あなたのregister関数が呼ばれ、新しいユーザーが登録されたとします。その重要な変化は、ブロックチェーンの奥深くで静かに記録されるだけです。あなたのウェブサイトやモバイルアプリといった「外部のアプリケーション」は、どうやってその事実を知ればよいのでしょうか? データベースの変更をリアルタイムに検知するように、スマートコントラクトの変化を「購読」する方法はないのでしょうか?

この、スマートコントラクトと外部の世界とを繋ぐ通信ブリッジの役割を果たすのが、今回学ぶevent(イベント)です。

イベントは、あなたのコントラクトが発する「狼煙」や「放送」のようなもの。これをマスターすることは、あなたのスマートコントラクトを、単なるバックエンドのロジックから、ユーザーが実際に触れることができる、生き生きとしたフロントエンドを持つ完全なDAppへと昇華させることを意味します。

eventとは? コントラクトが発する放送 📢

eventとは、スマートコントラクトからブロックチェーンのログ領域へ、メッセージを放出(emit)するための仕組みです。このログはブロックチェーンに永続的に記録されますが、状態変数にデータを保存するよりも、はるかにガス代が安く済みます。

イベントには、主に2つの重要な役割があります。

  1. 安価なロギング(記録)機能 コントラクト内で起きた重要な出来事(例:トークンの送金、NFTの発行、オークションの落札)を、時系列に沿って記録できます。このログは改ざん不可能であるため、監査やデバッグ、あるいは過去の取引履歴の証明として絶大な信頼性を持ちます。
  2. オフチェーン世界への通知機能 これがイベントの最も強力な機能です。ウェブサイトなどのフロントエンドアプリケーションは、特定のイベントが放出されるのを「監視(listen)」することができます。そして、イベントが放出された瞬間に、UIを更新したり(例:「新しいユーザーが登録されました!」と表示)、ユーザーにプッシュ通知を送ったりといった、リアルタイムな応答が可能になるのです。

状態変数が「契約の現在の状態」を保存するのに対し、イベントは「過去に何が起きたか」という歴史を記録します。そして、その歴史の1ページが追加されたことを、外部の世界に知らせるための仕組みなのです。

eventの基本的な使い方

それでは、第8話で作成したUserManagementコントラクトに、イベント機能を追加していきましょう。

eventの定義

イベントは、structのように、まずどのような情報をログとして残したいかを定義することから始まります。キーワードeventに続けて、イベント名と、記録したいデータの型や名前を記述します。

Solidity

// ユーザーが登録された時に放出するイベント
event UserRegistered(
    uint id,
    address userAddress,
    string name,
    uint timestamp
);

ここで、indexedという非常に便利なキーワードを紹介します。イベントの引数のうち、最大3つまでにindexedを付けることができます。

Solidity

event UserRegistered(
    uint indexed id,
    address indexed userAddress,
    string name,
    uint timestamp
);

indexedを付けたパラメータは、データベースの「索引」のように機能します。これにより、後から大量のログの中から、「このuserAddressに関連するイベントだけ」や「このidのイベントだけ」を、高速かつ効率的に検索・フィルタリングすることが可能になります。特定のユーザーの行動履歴を追跡したい場合などに、絶大な効果を発揮します。

eventの放出(emit)

イベントを定義したら、次はそれを実際に「放出(emit)」する処理を関数内に記述します。そのためにはemitキーワードを使います。

emit イベント名(渡したい値1, 渡したい値2, ...);

第8話のregister関数を改良して、ユーザー登録が完了した直後にUserRegisteredイベントを放出するようにしてみましょう。

Solidity

function register(string memory _name) public {
    require(users[msg.sender].id == 0, "User already registered.");

    uint userId = nextUserId;
    users[msg.sender] = User(userId, _name, true);
    nextUserId++;

    // イベントを放出!
    emit UserRegistered(userId, msg.sender, _name, block.timestamp);
}

ここで、また新たなグローバル変数block.timestampが登場しました。これは、そのトランザクションが含まれるブロックのタイムスタンプ(Unixエポックタイム形式のuint)を返します。イベントに時刻情報を記録する際の標準的な方法です。

Remixでイベントを確認しよう 📡

定義したイベントが、本当にブロックチェーンに記録されているのか確認してみましょう。

  1. 改良したUserManagementコントラクトをRemixでデプロイします。
  2. register関数に適当な名前(例: "Satoshi")を入力して実行します。
  3. 成功すると、下部のターミナルにトランザクションのログが表示されます。そのログをクリックして詳細を開いてください。
  4. 詳細の中に「logs」という項目があるはずです。これがイベントのログです。

中身を見ると、イベント名がUserRegisteredであり、私たちが渡したid, userAddress, name, timestampの値がすべて正確に記録されていることが確認できます。

このログこそが、フロントエンドアプリケーションが待ち受けている「シグナル」なのです。実際のDApp開発では、Ethers.jsといったライブラリを使い、このログの発生を検知して様々な処理を実行します。

完成版:ユーザー情報の更新もイベントで通知

ユーザー名が変更された際にもイベントを放出するように、updateName関数も改良してみましょう。

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

contract UserManagement {
    // ... structの定義 ...
    // ... mappingと状態変数の定義 ...

    // イベントの定義
    event UserRegistered(uint indexed id, address indexed userAddress, string name, uint timestamp);
    event NameUpdated(address indexed userAddress, string newName, uint timestamp);

    // ユーザー登録関数
    function register(string memory _name) public {
        require(users[msg.sender].id == 0, "User already registered.");
        uint userId = nextUserId;
        users[msg.sender] = User(userId, _name, true);
        nextUserId++;
        emit UserRegistered(userId, msg.sender, _name, block.timestamp);
    }

    // ユーザー名更新関数
    function updateName(string memory _newName) public {
        require(users[msg.sender].id != 0, "User not registered.");
        users[msg.sender].name = _newName;
        // 名前の更新をイベントで通知
        emit NameUpdated(msg.sender, _newName, block.timestamp);
    }
}

これで、「状態を変更する処理」と「その変更を外部に通知する処理」がワンセットになり、より完成度の高いコントラクトになりました。

まとめ:外部世界とのコミュニケーションブリッジ

お疲れ様でした!あなたは今回、スマートコントラクトと外部アプリケーションとを繋ぐ、極めて重要な架け橋であるeventを学びました。

  • event は、コントラクト内で起きた出来事を記録・通知するための仕組みである。
  • 状態変数への書き込みよりガス代が安く、ロギングに最適。
  • indexed キーワードを使うことで、後からのログ検索が容易になる。
  • emit キーワードで、定義したイベントを任意のタイミングで放出できる。

このイベントの仕組みを理解したことで、あなたはブロックチェーンの内部だけで完結するプログラムではなく、ユーザーが実際に見て触れるフロントエンドと連携する、完全なDAppを構築するための道筋を理解したことになります。

さて、これまでの関数は、上から下へと一直線に処理が流れる単純なものでした。しかし、より複雑なロジックを組むためには、「もしAという条件ならXという処理を、Bという条件ならYという処理を」といった条件分岐が必要不可欠です。

次回、第10話「条件分岐と繰り返し!if文とforループでロジックを組む」では、あなたのコントラクトに知性を与える、if文やforループといった制御構文について学んでいきます。

目次
Solidity学習講座:最終版 目次(全20話)
Solidity学習講座:最終版 目次(全20話)

基礎編 第1話:未来のインターネットへようこそ!Solidityとスマートコントラクトの全体像 第2話:準備は1分!ブラウザだけで開発できる「Remix IDE」の基本操作 第3話:記念すべき初コント ...

続きを見る

-Solidity