読了時間:19分
このガイドには、現在は積極的にメンテナンスが行われていないGoerliテストネットに関する記述が含まれています。このチェーンに関連する具体的な手順は適用できない場合がありますが、全体的なプロセスは他のチェーンでも有効である可能性があります。実装にあたっては、現在の代替案を検討することをお勧めします。このガイドの最新版をご希望の場合は、ぜひお知らせください!
概要
スマートコントラクトは決定論的であり、その動作を予測できるため、ブロックチェーン上でランダム性を生成するのは難しい場合があります。しかし、Chainlinkはこの問題を解決します。この記事では、Chainlink VRFとその仕組みについて詳しく解説します。 さらに、Chainlink VRFの2つの異なる手法、すなわち「ダイレクトファンディング方式」と「サブスクリプション方式」についても解説します。記事の後半では、Remix.IDEを使用してカスタム抽選スマートコントラクトをデプロイし、Chainlink VRFを用いて乱数を生成する手順を実際に体験します。
**必要なもの
**
- Solidityの基礎知識
- Remix IDE
- MetaMaskウォレット
- Goerliテストネット上のETH(Quicknodeのファウセットで入手可能です)
- テストネットのLINKトークン
**主な業務内容
**
- Chainlink VRFとさまざまなリクエスト方法について学ぶ
- Chainlink VRF を使用して、カスタム抽選スマートコントラクトを作成・展開する
- Remix.IDE を使用して、Goerli テストネット上で宝くじのスマートコントラクトをテストする
Chainlink VRFとは何ですか?
Chainlink VRFは、セキュリティを損なうことなくスマートコントラクトがランダムな値にアクセスできるようにする、公正かつ検証可能な乱数生成器(RNG)です。リクエストごとに、Chainlink VRFは1つ以上のランダムな値と、オンチェーンの暗号学的証明を返します。
Chainlink VRF を使用することで、スマートコントラクト向けに「証明可能な公平性」を備えた検証可能な乱数を生成できます。主な活用例としては、次のようなものがあります:
- ブロックチェーンゲーム/メタバース
-PVPのマッチメイキング:操作が一切行われていないことをユーザーが確認できる、証明可能な公平なマッチメイキングシステムを構築します。
-予測不可能なゲーム:ゲームに証明可能な公平なランダム性を導入します(例:ゲーム中のランダムな報酬など) - NFT
-コミュニティやオンラインキャンペーンの参加者の中から、当選者をランダムに抽選する - DeFi
-ステーキングプールで有望なプロジェクトを見極める - 消費者向け広告・特典
-オンラインキャンペーンでの当選者の無作為抽選
-ロイヤリティ会員クラブでの当選者の無作為抽選 - 公正な確率(例:オンライン賭博)
-オンライン賭博ゲームやルートボックスなどにおける確率のランダム化 - セキュリティ
-認証フローに、予測不可能なランダム性を組み込む
これらはほんの一例に過ぎません。皆さんの創造力と経験を活かして、さまざまなアイデアを試してみてください!
ChainlinkのVRFはどのように機能するのでしょうか?
これまで説明してきたように、Chainlink VRF を利用すると、スマートコントラクトからランダムなデータを取得・返却することができます。しかし、これを利用する前に、Chainlink のネイティブトークンでありユーティリティトークンでもある LINK トークンをアカウントに入金しておく必要があります。メインネットにコントラクトをデプロイしたい場合は、取引所から LINK トークンを入手する必要があります。
ただし、開発目的であれば、Goerli または Mumbai テストネットワーク上のテストネット版 LINK トークンを使用することができます(こちらのファウセットから入手可能です)。次のステップとして、Chainlink VRF の実装をインポートし、契約内で適切に利用します。
Chainlink VRF をスマートコントラクトに組み込むと、ランダム性の生成が要求されるたびに、バックエンドでは Chainlink が次のような処理を行います:
Chainlinkノードは、現在のブロックからの一部のデータを使用して、ランダム性を生成します。
乱数を生成した後、暗号学的証明がVRFコントラクトに送信されます。この証明は、乱数がブロックチェーン上で公開される前に承認される必要があります。
こうしたオンチェーンおよびオフチェーンの通信はすべて、比較的短期間のうちに発生します。
ランダム性の生成方法
Chainlink VRF では、ランダム性を要求するための 2 つの方法が用意されています:
- サブスクリプションモデル
この方法を利用するには、サブスクリプションアカウントを作成し、そのアカウントにLINKトークンを入金する必要があります。そうすれば、ユーザーは各コントラクトに個別に資金を投入することなく、複数のコントラクトを接続することができます。
この方法により、単一のサブスクリプションから複数のコンシューマー契約への入札資金を供給することができます。コンシューマー契約がランダムな値をリクエストすると、そのリクエストが処理され、トランザクションコストが計算されます。その後、それに応じて残高から差し引かれます。
- 直接資金提供
この方法では、ユーザーがランダムな値をリクエストすると、コンシューマー契約がLINKトークンで支払いをします。そのため、コンシューマー契約に直接資金を投入し、ランダムなリクエストの支払いに十分なLINKトークンが確保されていることを確認する必要があります。あるいは、スマートコントラクトに追加のロジックを組み込むことで、LINKのコストをユーザーに転嫁することも可能です。
サブスクリプションモデルと直接資金提供モデルのうち、どちらが適しているかを見極める方法
どのモデルにもそれぞれの利点があり、どれが他より優れているというわけではありません。すべては、契約やDAppの具体的な利用ケース次第です。この点に関して、より適切な選択を行うために、以下の事実を参考にしてください:
- DAppで定期的なランダムなリクエストが必要な場合は、サブスクリプションモデルの方が優れており、費用対効果も高くなります。一方、1回限りのリクエストを処理するには、直接資金提供方式の方が適しています。
- VRFを消費する契約が複数ある場合は、サブスクリプションモデルの方が適しています。
- リクエストのガス代を最適化したい場合は、サブスクリプション方式の方が推奨されます。
- 直接資金提供方式とは異なり、サブスクリプションでは1回のリクエストで多数のランダムな単語を返すことができます。
Chainlink VRF を使用したカスタム宝くじコントラクトの作成
ここまで、本記事では、Chainlink VRFの仕組みについて、十分な概念的な理解を築き、整理してきました。そこで、開発の観点から、これまで説明してきた内容をすべて試してみるために、抽選コントラクトを作成していきましょう。
Remix.IDEで新しい空白のワークスペースを開き、game.sol という名前の Solidity ファイルを作成します。
ステップ 1:依存関係のインポート
チェーンリンクのインターフェースとやり取りを行うことになるため、契約内で3つの依存関係、すなわちVRFCoordinatorV2Interface、VRFConsumerBaseV2、およびConfirmedOwner契約をインポートし、継承する必要があります。
VRFCoordinatorV2Interface は、ランダム性の要求やサブスクリプションの管理を行う関数を持つ依存関係です。VRFConsumerBaseV2 は、コンシューマー契約との効率的な通信を行うためのものです。また、ConfirmedOwner 依存関係は、所有者にスタンプを押し、操作者を排除することでセキュリティを確保するためのものです。
game.solファイルを開き、先頭に以下のコードを追加してください:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import "https://github.com/smartcontractkit/chainlink/blob/develop/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol";
import "https://github.com/smartcontractkit/chainlink/blob/develop/contracts/src/v0.8/VRFConsumerBaseV2.sol";
import "@chainlink/contracts/src/v0.8/ConfirmedOwner.sol";
ステップ 2:状態変数の設定
次に、「Game」という名前のコントラクトを作成し、スマートコントラクトのロジック(イベント、構造体、マッピング、状態変数など)を定義します。game.sol ファイルの import 文の下に、以下のコードを追加してください:
contract Game is VRFConsumerBaseV2, ConfirmedOwner {
event RequestSent(uint256 requestId, uint32 numWords);
event RequestFulfilled(uint256 requestId, uint256[] randomWords);
struct RequestStatus {
bool fulfilled; // whether the request has been successfully fulfilled
bool exists; // whether a requestId exists
uint256[] randomWords;
}
mapping(uint256 => RequestStatus)
public s_requests; /* requestId --> requestStatus */
VRFCoordinatorV2Interface COORDINATOR;
// Your subscription ID.
uint64 s_subscriptionId;
// past requests Id.
uint256[] public requestIds;
uint256 public lastRequestId;
bytes32 immutable keyHash;
address public immutable linkToken;
uint32 callbackGasLimit = 150000;
uint16 requestConfirmations = 3;
uint32 numWords = 1;
uint public randomWordsNum;
ユーザーがランダムな値をリクエストすると、最初の2つのイベント(つまり、RequestSentとRequestFulfilled)がログに記録されます。1つは送信された値を、もう1つは処理完了した値を記録します。
また、リクエストが処理済みであるか、あるいはそもそも存在するかどうかにかかわらず、あらゆるリクエストのステータスに関するデータを表す構造体(つまり、RequestStatus)があります。この構造体を作成した以上、それを追跡する必要が生じるため、マッピングが必要となるのです。
その後、契約にkeyHashとlinkToken を導入しました。あとは、gaslimit、想定されるランダムな単語の数、およびリクエストの確認設定を行うだけでした。
それはさておき、抽選コントラクト用の状態変数も作成する必要があります。
game.sol に貼り付けた前のコードの下に、以下のコードを追加してください。
住所[] public プレイヤー;
uint maxPlayers;
bool public gameStarted;
uint public 参加料;
uint public gameId;
アドレス public recentWinner;
イベント GameStarted(uint ゲームID, uint maxPlayers, uint 参加費);
event PlayerJoined(uint gameId, address player);
event GameEnded(uint gameId, address 勝者);
まず、プレイヤー配列を作成し、その可視性を「public」に設定しました。次に、最大プレイヤー数「maxPlayers」を表す符号なし整数変数を作成しました。全員が参加できるようになるには抽選を開始する必要があるため、ゲームのステータスを追跡するためにブール型変数「gameStarted」を作成しました。
各参加者は参加費を支払い、各ゲームには固有のgameIdが割り当てられます。その後、recentWinner用のアドレス型データ型を作成しました。
イベントは、ゲームの開始、終了、およびプレイヤーが参加したタイミングを追跡します。これらのイベントのログは、eth_getLogsメソッドを使用して確認できます。
ステップ 3: コンストラクタでの初期化
最初にいくつかの依存関係をインポートしましたが、これらはコンストラクタ内で初期化されます。まず、いくつかのコンストラクタ引数(例:subscriptionId、_linkToken)を設定します。これらは、当コントラクトが資金調達に使用するサブスクリプションID、およびデプロイ元のネットワークのリンクトークンアドレスを指します。
次に、VRFConsumerBaseV2を初期化し、ConfirmedOwner関数を呼び出して、msg.senderを所有者として設定します。COORDINATOR変数をVRFCoordinatorV2Interfaceで設定した後、s_subscriptionIdを設定します。デプロイ先のネットワークに注意し、それに応じて正しい値を設定するようにしてください。 最後に、keyHash、リクエストに対して支払ってもよい最大ガス価格(Wei単位)、linkTokenアドレスを設定し、gameStarted変数をfalseに設定します。
このチュートリアルでは、Goerliテストネットを使用します。さらに重要な点として、所有者がゲームを開始するまでは、ゲームの開始状態をfalseに設定します。
game.sol に貼り付けた前のコードの下に、以下のコードを追加してください。
constructor(
uint64 subscriptionId,
address _linkToken
)
VRFConsumerBaseV2(0x2Ca8E0C643bDe4C2E08ab1fA0da3401AdAD7734D)
ConfirmedOwner(msg.sender)
{
COORDINATOR = VRFCoordinatorV2Interface(
0x2Ca8E0C643bDe4C2E08ab1fA0da3401AdAD7734D
);
s_subscriptionId = subscriptionId;
keyHash = 0x79d3d8832d904592c0bf9818b621522c988bb8b0c05cdc3b15aea1b6e8db0c15; // we alread set this
linkToken = _linkToken;
gameStarted = false;
}
ステップ4:startGame関数
game.sol に貼り付けた前のコードの下に、以下のコードを追加してください。
function startGame(uint _maxPlayers, uint _entryfee) public {
require(!gameStarted, "The Game has started");
players = new address[](0);
maxPlayers = _maxPlayers;
gameStarted = true;
entryfee = _entryfee; // entry fee in wei(18 zeros)
gameId += 1;
emit GameStarted(gameId, maxPlayers, entryfee);
}
上記でstartGame関数を作成し、public にしました。その後、players 配列と最大プレイヤー数の両方をリセットしました。
ゲームが開始されると、gameIDを1増やし、gameStartedをtrueに更新します。
ステップ 5: joinGame 関数
game.sol に貼り付けた前のコードの下に、以下のコードを追加してください。
function joinGame() public payable {
require(gameStarted, "The Game has not kicked off");
require(players.length < maxPlayers, "The Game is Filled Up!");
require(
msg.value == entryfee,
"The amount should be equal to the entry fee"
);
players.push(msg.sender);
emit PlayerJoined(gameId, msg.sender);
if (players.length == maxPlayers) {
getRandomWinner();
}
}
上記のjoinGame関数では、3つのチェックを設定する必要がありました。以下の条件に該当する場合、参加希望者は参加できません:
- 試合はまだ始まっていません
- プレイヤー数の上限に達しました
- 参加者は参加費を支払いません
プレイヤーが有効であると確認されると、その参加者はplayers配列に追加されます。次に、プレイヤーの数が設定されたmaxPlayers未満かどうかを確認します。最後のプレイヤーがゲームに参加すると(players.length == maxPlayers)、getRandomWinner関数が呼び出され、ランダムに勝者が選ばれます。
ステップ 6: pickWinner 関数
game.sol に貼り付けた前のコードの下に、以下のコードを追加してください。
function getRandomWinner() internal returns (address) {
uint256 requestId = requestRandomWords();
uint256 winnerIndex = randomWordsNum % players.length;
recentWinner = players[winnerIndex];
(bool success, ) = recentWinner.call{value: address(this).balance}("");
require(success, "Could not send ether");
gameStarted = false;
emit GameEnded(gameId, recentWinner);
return recentWinner;
}
上記のgetRandomWinner関数では、requestIdをrequestRandomWords に代入しました。その後、player 配列と randomWordsNum に対して剰余演算を用いてランダム性を生成しました。
それが成功すると、recentWinnerに転送リクエストを行い、そのアドレスを一般に公開しました。
ステップ 7:乱数を取得するためのロジックを設定する
乱数を生成するには、Chainlinkの以下の3つの主要な関数を実装する必要があります。それは、requestRandomWords関数、fulfillRandomWords関数、およびgetRequestStatus関数です。ここで、これらの関数がどのように動作して乱数を生成するのか、少し詳しく見てみましょう。
requestRandomWords関数を呼び出すと、Chainlink オラクルが乱数を生成するようリクエストを行います。
このリクエストには、requestIdという変数が含まれており、これを通じて、サブスクリプションID、リクエスト確認の制限数、コールバックのガス制限、および希望する乱数の数といったデータがVRFコーディネーターに伝達されます。処理が完了すると、オラクルはID(例:requestId)を返します。また、コントラクトは返り値をマッピング(例:s_requests)に格納します。
では、返される requestId の目的は何でしょうか?これは、コールバック関数fulfillRandomWords 内で使用されます。この関数では、(requestRandomWords 関数の呼び出しが完了した際に)requestId と、randomWords(つまり数値)の配列を受け取ります。なお、このコールバック関数内では、返された乱数を変更したり、状態変数を更新したりするロジックを実装することができます。
function requestRandomWords() public onlyOwner returns (uint256 requestId) {
requestId = COORDINATOR.requestRandomWords(
keyHash,
s_subscriptionId,
requestConfirmations,
callbackGasLimit,
numWords
);
s_requests[requestId] = RequestStatus({
randomWords: new uint256[](0),
exists: true,
fulfilled: false
});
requestIds.push(requestId);
lastRequestId = requestId;
emit RequestSent(requestId, numWords);
return requestId; // requestID is a uint.
}
function fulfillRandomWords(
uint256 _requestId,
uint256[] memory _randomWords
) internal override {
require(s_requests[_requestId].exists, "request not found");
s_requests[_requestId].fulfilled = true;
s_requests[_requestId].randomWords = _randomWords;
randomWordsNum = _randomWords[0]; // Set array-index to variable, easier to play with
emit RequestFulfilled(_requestId, _randomWords);
}
// to check the request status of random number call.
function getRequestStatus(
uint256 _requestId
) external view returns (bool fulfilled, uint256[] memory randomWords) {
require(s_requests[_requestId].exists, "request not found");
RequestStatus memory request = s_requests[_requestId];
return (request.fulfilled, request.randomWords);
}
requestRandomWords 関数内の numWords 変数を更新することで、1回の VRF リクエストから複数の乱数値を直接リクエストできる点にご注意ください。また、テストネットの現在の状況によっては、コールバックがリクエストされた乱数値をコントラクトに返すまでに数分かかる場合があります。vrf.chain.linkにアクセスすると、ご自身のサブスクリプションに対する保留中のリクエストの一覧を確認できます。
最終的に、game.sol内のコード全体は次のようになるはずです:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import "https://github.com/smartcontractkit/chainlink/blob/develop/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol";
import "https://github.com/smartcontractkit/chainlink/blob/develop/contracts/src/v0.8/VRFConsumerBaseV2.sol";
import "@chainlink/contracts/src/v0.8/ConfirmedOwner.sol";
contract Game is VRFConsumerBaseV2, ConfirmedOwner {
event RequestSent(uint256 requestId, uint32 numWords);
event RequestFulfilled(uint256 requestId, uint256[] randomWords);
struct RequestStatus {
bool fulfilled; // whether the request has been successfully fulfilled
bool exists; // whether a requestId exists
uint256[] randomWords;
}
mapping(uint256 => RequestStatus)
public s_requests; /* requestId --> requestStatus */
VRFCoordinatorV2Interface COORDINATOR;
// Your subscription ID.
uint64 s_subscriptionId;
// past requests Id.
uint256[] public requestIds;
uint256 public lastRequestId;
bytes32 immutable keyHash;
address public immutable linkToken;
uint32 callbackGasLimit = 150000;
uint16 requestConfirmations = 3;
uint32 numWords = 1;
uint public randomWordsNum;
address[] public players;
uint maxPlayers;
bool public gameStarted;
uint public entryfee;
uint public gameId;
address public recentWinner;
event GameStarted(uint gameId, uint maxPlayers, uint entryfee);
event PlayerJoined(uint gameId, address player);
event GameEnded(uint gameId, address winner);
constructor(
uint64 subscriptionId,
address _linkToken
)
VRFConsumerBaseV2(0x2Ca8E0C643bDe4C2E08ab1fA0da3401AdAD7734D)
ConfirmedOwner(msg.sender)
{
COORDINATOR = VRFCoordinatorV2Interface(
0x2Ca8E0C643bDe4C2E08ab1fA0da3401AdAD7734D
);
s_subscriptionId = subscriptionId;
keyHash = 0x79d3d8832d904592c0bf9818b621522c988bb8b0c05cdc3b15aea1b6e8db0c15; // we alread set this
linkToken = _linkToken;
gameStarted = false;
}
receive() external payable {}
function startGame(uint _maxPlayers, uint _entryfee) public {
require(!gameStarted, "The Game has started");
players = new address[](0);
maxPlayers = _maxPlayers;
gameStarted = true;
entryfee = _entryfee; // entry fee in wei(18 zeros)
gameId += 1;
emit GameStarted(gameId, maxPlayers, entryfee);
}
function joinGame() public payable {
require(gameStarted, "The Game has not kicked off");
require(players.length < maxPlayers, "The Game is Filled Up!");
require(
msg.value == entryfee,
"The amount should be equal to the entry fee"
);
players.push(msg.sender);
emit PlayerJoined(gameId, msg.sender);
if (players.length == maxPlayers) {
getRandomWinner();
}
}
function getRandomWinner() internal returns (address) {
uint256 requestId = requestRandomWords();
uint256 winnerIndex = randomWordsNum % players.length;
recentWinner = players[winnerIndex];
(bool success, ) = recentWinner.call{value: address(this).balance}("");
require(success, "Could not send ether");
gameStarted = false;
emit GameEnded(gameId, recentWinner);
return recentWinner;
}
function requestRandomWords() public onlyOwner returns (uint256 requestId) {
requestId = COORDINATOR.requestRandomWords(
keyHash,
s_subscriptionId,
requestConfirmations,
callbackGasLimit,
numWords
);
s_requests[requestId] = RequestStatus({
randomWords: new uint256[](0),
exists: true,
fulfilled: false
});
requestIds.push(requestId);
lastRequestId = requestId;
emit RequestSent(requestId, numWords);
return requestId; // requestID is a uint.
}
function fulfillRandomWords(
uint256 _requestId,
uint256[] memory _randomWords
) internal override {
require(s_requests[_requestId].exists, "request not found");
s_requests[_requestId].fulfilled = true;
s_requests[_requestId].randomWords = _randomWords;
randomWordsNum = _randomWords[0]; // Set array-index to variable, easier to play with
emit RequestFulfilled(_requestId, _randomWords);
}
// to check the request status of random number call.
function getRequestStatus(
uint256 _requestId
) external view returns (bool fulfilled, uint256[] memory randomWords) {
require(s_requests[_requestId].exists, "request not found");
RequestStatus memory request = s_requests[_requestId];
return (request.fulfilled, request.randomWords);
}
}
Chainlink VRF サブスクリプションの設定
Gameコントラクトの作成が完了したので、次はChainlinkのサブスクリプションの作成に進みましょう。ただし、その前に、Goerliテストネット上にETHが一定量あることを確認してください。Quicknodeのファウセットから入手できます。
次に、Chainlinkのサブスクリプションページにアクセスして、サブスクリプションを作成します。MetaMaskウォレットを接続し、「サブスクリプションを作成」ボタンをクリックしてください。最後のステップは、MetaMaskウォレットでトランザクションに署名することです(Goerliテストネットに接続されていることを確認してください)。

次のステップは、Chainlinkのサブスクリプションに資金を入金することです。「Add Funds」をクリックし、10 LINKトークンを追加してください。完了すると、ダッシュボードは次のように表示されるはずです:

参加型契約の作成
抽選の参加者として機能するコントラクトを作成し、デプロイする必要があります。このコントラクトの主なロジックは、デポジットを行い、joinGame関数を呼び出すことです。
「GameParticipant.sol」というファイルを作成し、そのファイルに以下のコードを追加してください:
pragma solidity 0.8.0;
contract joinGameContract {
constructor() payable {}
receive() external payable {}
function joinGame(address _addr) public payable {
(bool success, ) = _addr.call{value: address(this).balance}(
abi.encodeWithSignature("joinGame()")
);
require(success, "Tx failed send");
}
}
このコントラクトは、joinGame関数を呼び出し、送金処理を実行し、イーサを受け取るための受信フォールバック関数を備えています。
宝くじ契約のテスト
ここで、まず抽選コントラクト(例:game.sol)を展開することから始めますが、その前に、スマートコントラクトをコンパイルする必要があります。「Solidity Compiler」タブで、game.sol ファイルを選択し、「Compile」ボタンをクリックします。次に、GameParticipant.sol についても同様に操作を行います。
「自動コンパイル」オプションが有効になっている場合、コントラクトを再度コンパイルする必要はありませんのでご注意ください。
その後、コントラクトのコンパイルが完了し、デプロイの準備が整ったら、Remix.IDEの「DEPLOY & RUN TRANSACTIONS」に移動し、[Environment] ドロップダウンから [Injected Provider] オプションを選択します。次に、[Contract]ドロップダウンからゲーム用コントラクト(例:game.sol)を選択します。
Chainlink Subscriptionダッシュボードに記載されているサブスクリプションIDと、LINKトークンのアドレス(0x326C977E6efc84E512bB9C30f76E30c160eD06FB)を、Remix.IDEのコンストラクタの引数として入力する必要があります。

展開が完了したら、そのコントラクトアドレスをコンシューマーとして追加し、MetaMaskウォレットでトランザクションに署名する必要があります。

次のステップは、startGame関数とやり取りすることです。プレイヤー数を2に設定し、参加費を1 weiに設定してください。このトランザクションには、MetaMaskウォレットで署名を行う必要があります。

次に、joinGameContractコントラクト(つまり GameParticipant.sol)をデプロイする必要があります。Remix.IDE でそのコントラクトを選択し、「DEPLOY & RUN TRANSACTIONS」タブに移動して、コントラクトをデプロイしてください。

トランザクションに署名してコントラクトをデプロイしたら、Remix.IDEの「value」フィールドに1 weiを入力し、joinGame関数を呼び出してください。引数としてgame.solコントラクトのアドレスを指定し、MetaMaskでトランザクションに署名する必要があります。

GameParticipant コントラクトが抽選に参加したら、game.sol コントラクトに戻り、joinGame関数を呼び出し、トランザクションに 1 wei を渡してください。

最大人数のプレイヤーが参加すると、契約はChainlink VRFにリクエストを送信し、乱数を取得して勝者のアドレスを返します。勝者を確認するには、recentWinner変数をクリックしてその状態を読み取ってください。

結論
おめでとうございます! これで、Chainlink VRFについて、そしてスマートコントラクトから乱数を要求する方法について学びました。Chainlinkは、開発者に「証明可能な公平性」を持つ乱数生成器へのアクセスを提供するため、ブロックチェーン技術にとって大きな進歩となっています。
Chainlink VRFの活用例は、この記事で紹介したような宝くじの当選者をランダムに決定することにとどまりません。今後開発するあらゆるスマートコントラクトにおいて、ランダム性を生成するために活用することができます。
皆さんがどんな作品を作っているのか、ぜひ拝見したいです!DiscordやTwitterで、ぜひアイデアを共有してください。このガイドに関するご意見やご質問がございましたら、ぜひお聞かせください!