読了時間:10分
Solana でのリアルタイムな変化を監視するには、いくつかの方法があります。ご自身のユースケースに適したアプローチがわからない場合は、各オプションの比較をまとめた当社のブログ記事をご覧ください。Solana Web3js(バージョン 1.x)を使用して WebSockets による開発を始める準備ができている方は、このまま読み進めてください(バージョン 2.x をご希望の場合は、こちらのガイドをご覧ください)!
概要
イベントリスナーを作成することは、監視対象に変化があったことをアプリやユーザーに通知する効果的な方法です。Solanaには、サブスクリプションとも呼ばれる便利な組み込みのイベントリスナーがいくつか用意されており、Solanaブロックチェーン上の変化を簡単に監視することができます。その使い方がわからないという方もいらっしゃるでしょうか? 以下に、これが役立つと思われる例をいくつか挙げます。オンチェーンプログラム(例:販売ボット)とのやり取りを監視するDiscordボット、ユーザーのトランザクションにエラーがないかを確認するdApp、あるいはユーザーのウォレットの残高が変化した際にスマートフォンに通知を送る機能などです。
動画による解説をご希望ですか?サヒルと一緒に、わずか4分でSolanaブロックチェーンへのWebSocketサブスクリプションを作成する方法を学びましょう。
主な業務内容
このガイドでは、SolanaのイベントリスナーメソッドとQuicknodeのWebSocketエンドポイント(WSS://)を使用して、チェーン上の変更を監視する方法について学びます。具体的には、アカウント(またはウォレット)の変更を追跡するシンプルなTypeScriptアプリケーションを作成します。その後、Solanaのunsubscribeメソッドを使用して、アプリケーションからリスナーを削除する方法についても学びます。 また、Solanaのその他のリスナーメソッドに関する基本事項についても解説します。
必要なもの
- Node.js がインストールされていること(バージョン 16.15 以上)
- npm または yarn がインストールされていること(プロジェクトの初期化や必要なパッケージのインストールには yarn を使用します。お好みのパッケージマネージャーが npm の場合は、そちらを使用しても構いません)
- TypeScriptの使用経験があり、ts-nodeがインストールされている
- ソラナ Web3
環境の設定
ターミナルで、次のコマンドを実行して新しいプロジェクトディレクトリを作成します:
mkdir solana-subscriptions
cd solana-subscriptions
「app.ts」というファイルを作成します:
echo > app.ts
新しいパッケージにデフォルト値を使用するには、「yes」フラグを指定してプロジェクトを初期化してください:
yarn init --yes
#or
npm init --yes
Solana Web3 の依存関係をインストールします:
yarn add @solana/web3.js@1
#or
npm install @solana/web3.js@1
お好みのコードエディタでapp.tsを開き、1 行目で Solana Web3 ライブラリからConnection、PublicKey、およびLAMPORTS_PER_SOLをインポートします:
import { Connection, PublicKey, LAMPORTS_PER_SOL } from "@solana/web3.js";
よし!準備は万端だ。
Quicknodeのエンドポイントを設定する
Solana上で開発を行うには、ネットワークに接続するためのAPIエンドポイントが必要です。パブリックノードを利用することも、独自のインフラストラクチャをデプロイして管理することも可能です。ただし、応答時間を8倍高速化したい場合は、その手間を私たちにお任せください。
Solana上のプロジェクトの50%以上がQuicknodeを選んでいる理由をご覧ください。こちらからアカウント登録を行ってください。
Solana Devnet ノードを使用します。HTTP プロバイダーとWSS プロバイダーのリンクをコピーしてください:

app.jsの 3 行目と 4 行目に、これらの URL を格納するための新しい変数を 2 つ作成してください:
const WSS_ENDPOINT = 'wss://example.solana-devnet.quiknode.pro/000/'; // ご自身のURLに置き換えてください
const HTTP_ENDPOINT = 'https://example.solana-devnet.quiknode.pro/000/'; // 自分のURLに置き換えてください
Solana への接続を確立する
5行目で、Solanaへの新しい接続を作成します:
const solanaConnection = new Connection(HTTP_ENDPOINT,{wsEndpoint:WSS_ENDPOINT});
過去にSolanaへのConnectionインスタンスを作成したことがある方は、パラメータに違いがあること、特に {wsEndpoint:WSS\_ENDPOINT}. もう少し詳しく見ていきましょう。
Connectionクラスの コンストラクタでは、オプションのcommitmentOrConfigを渡すことができます。これには、いくつかの興味深いオプションを含めることができます。 ConnectionConfigにはいくつかの興味深いオプションがありますが、今回はオプションパラメータである `wsEndpoint` に焦点を当てます。これは、フルノードの JSON RPC PubSub WebSocket エンドポイントへのエンドポイント URL を指定するためのオプションです。今回のケースでは、それは先ほど定義した WSS エンドポイント、つまり`WSS_ENDPOINT` になります。
wsEndpoint を指定しなかった場合はどうなるのでしょうか?実は、Solana にはmakeWebsocketUrlという関数が用意されており、これによってエンドポイント URL の「https」を 「wss」に、「http」を 「ws」に置き換えてくれます(出典)。Quicknode のすべての HTTP エンドポイントには、同じ認証トークンを持つ対応する WSS エンドポイントが存在するため、WebSocket クエリ用に別のエンドポイントを使用したい場合を除き、このパラメータを省略しても問題ありません。
さあ、サブスクリプションを作ってみましょう!
アカウントの作成 定期購読
Solana上でウォレットを追跡するには、solanaConnectionのonAccountChange メソッドを呼び出す必要があります。この際、ACCOUNT_TO_WATCH、追跡対象のウォレットの公開鍵、およびコールバック関数を引数として渡します。イベントが検出されたことを通知し、新しいアカウント残高をログに記録する簡単なログ処理を作成しました。このスニペットを、7行目のsolanaConnectionの宣言の後にコードに追加してください:
(async()=>{
const ACCOUNT_TO_WATCH = new PublicKey('vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg'); // Replace with your own Wallet Address
const subscriptionId = await solanaConnection.onAccountChange(
ACCOUNT_TO_WATCH,
(updatedAccountInfo) =>
console.log(`---Event Notification for ${ACCOUNT_TO_WATCH.toString()}--- \nNew Account Balance:`, updatedAccountInfo.lamports / LAMPORTS_PER_SOL, ' SOL'),
"confirmed"
);
console.log('Starting web socket, subscription ID: ', subscriptionId);
})()
簡単なテストを作成する
このコードはそのまま実行可能ですが、正常に動作しているかを確認するために、もう1つ機能を追加します。具体的には、スリープ関数(時間遅延を追加するため)とエアドロップリクエストを追加します。6行目の非同期コードブロックの前に、以下を追加してください:
const sleep = (ms:number) => {
return new Promise(resolve => setTimeout(resolve, ms));
}
そして、「Starting web socket」というログの後の非同期コードブロック内に、次のairdrop呼び出しを追加してください:
await 眠り(10000); //ソケットテストのために10秒待機
await solanaConnection.requestAirdrop(監視対象アカウント, LAMPORTS_PER_SOL);
このコードでは、ソケットが確立された後、実質的に10秒間待機してから、ウォレットへのエアドロップをリクエストします(注:これはdevnetおよびtestnetでのみ機能します)。
これで、コードは次のようになります:
import { Connection, PublicKey, LAMPORTS_PER_SOL, } from "@solana/web3.js";
const WSS_ENDPOINT = 'wss://example.solana-devnet.quiknode.pro/000/'; // replace with your URL
const HTTP_ENDPOINT = 'https://example.solana-devnet.quiknode.pro/000/'; // replace with your URL
const solanaConnection = new Connection(HTTP_ENDPOINT, { wsEndpoint: WSS_ENDPOINT });
const sleep = (ms:number) => {
return new Promise(resolve => setTimeout(resolve, ms));
}
(async () => {
const ACCOUNT_TO_WATCH = new PublicKey('vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg');
const subscriptionId = await solanaConnection.onAccountChange(
ACCOUNT_TO_WATCH,
(updatedAccountInfo) =>
console.log(`---Event Notification for ${ACCOUNT_TO_WATCH.toString()}--- \nNew Account Balance:`, updatedAccountInfo.lamports / LAMPORTS_PER_SOL, ' SOL'),
"confirmed"
);
console.log('Starting web socket, subscription ID: ', subscriptionId);
await sleep(10000); //Wait 10 seconds for Socket Testing
await solanaConnection.requestAirdrop(ACCOUNT_TO_WATCH, LAMPORTS_PER_SOL);
})()
さあ、ソケットを始めよう!
さっそく試してみましょう。ターミナルで「ts-node app.ts」と入力すると、WebSocketが起動します!約10秒後、次のようなコールバックログがターミナルに表示されるはずです:
solana-subscriptions % ts-node app.ts
Starting web socket, subscription ID: 0
---Event Notification for vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg---
New Account Balance: 88790.51694709 SOL
素晴らしい!イベント通知後もアプリケーションが起動したままであることに気づくはずです。これは、サブスクリプションが依然としてアカウントの変更を監視しているためです。このリスナーを解除する方法が必要です。Ctrl+Cを押してプロセスを停止してください。
アカウント変更リスナーの購読を解除する
Solana には、アカウント変更リスナーの登録を解除するための組み込みメソッド「removeAccountChangeListener」が用意されています。このメソッドは、有効なsubscriptionId(数値)を唯一のパラメータとして受け取ります。asyncブロック内で、エアドロップの実行後に、トランザクションが処理されるまでの時間を確保するために、もう 1 回sleep を追加してから、removeAccountChangeListener を呼び出してください:
await sleep(10000); //Wait 10 for Socket Testing
await solanaConnection.removeAccountChangeListener(subscriptionId);
console.log(`Websocket ID: ${subscriptionId} closed.`);
ここでコードをもう一度実行すると、同じ一連の処理が表示された後、WebSocketが閉じられるはずです:
solana-subscriptions % ts-node app.ts
Starting web socket, subscription ID: 0
---Event Notification for vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg---
New Account Balance: 88791.51694709 SOL
Websocket ID: 0 closed.
よくできました!ご覧の通り、何かが起きた後(例えば、時間が経過した、特定の閾値に達した、通知数が一定数に達したなど)にリスナーを無効にしたい場合に、この方法は役立ちます。
参考までに、このスクリプトの最終版コードをGitHubリポジトリに公開しました。
その他のSolana WebSocketサブスクリプション
Solanaには、他にも同様のWebSocketのサブスクライブ/アンサブスクライブメソッドがいくつかあり、これらも便利です。このセクションでは、それらについて簡単に説明します。
- onProgramAccountChange: 指定されたプログラムが所有するアカウントに変更があった際に呼び出されるコールバックを登録します。プログラムの PublicKey と、オプションでアカウントフィルタの配列を渡します。これは、とりわけユーザーのトークンアカウントの変更を追跡する際に便利です。登録を解除するには、removeProgramAccountChangeListener を使用します。
- onLogs: ログが出力されるたびに呼び出されるコールバックを登録します。有効なPublicKey を渡すことで、onAccountChangeと同様に使用できます。コールバックは最新のトランザクション ID を返します。removeOnLogsListener を使用して登録を解除します。
- onSlotChange:スロットの変更時に呼び出されるコールバックを登録します。このメソッドにはコールバック関数のみが渡されます。登録を解除するには、removeSlotChangeListener を使用してください。
- onSignature: 有効なトランザクション署名を渡すことで、署名の更新時に呼び出されるコールバックを登録します。このコールバックは、トランザクションでエラーが発生したかどうかを返します。登録を解除するには、removeSignatureListener を使用します。onRootChange: ルートが変更された際に呼び出されるコールバックを登録します。このメソッドにはコールバック関数のみが渡されます。登録を解除するには、removeRootChangeListener を使用します。
*注:すべての購読解除メソッドでは 、 subscriptionId (数値)パラメータ が必要です 。これらのメソッドに関する詳細情報は、quicknode.com/docs/solana のドキュメントでご確認いただけます。 app.jsを少し変更して、他の購読機能を試してみるなど、ぜひ自由に試してみてください。
課金と最適化
WebSocketメソッドの課金対象は、サブスクリプションの数ではなく、受信したレスポンスの数に基づきます。たとえば、 accountChange サブスクリプションに登録して100件の回答を受け取った場合、アカウントには5,000クレジットが請求されます(1回の回答につき50クレジット X 100件の回答)。
購読内容を最適化し、不要な購読や関係のない返信による課金を防ぐためには、以下の点をご検討ください:
- データの受信をもう必要としなくなった場合は、unsubscribeメソッドを使用してリスナーを削除し、
- 該当するメソッドのフィルターを活用し、関連するデータのみを受け取るようにします。
配信停止の方法
サブスクリプションからリスナーを削除するには、以下のメソッドを使用します。
アカウントの登録解除: アカウントの更新情報の受信を解除します。logsUnsubscribe: ログの受信を解除します。programUnsubscribe: プログラムアカウントの更新情報の受信を解除します。slotUnsubscribe: スロットの更新情報の受信を解除します。署名・購読解除: 署名の更新情報の受信を解除します。rootUnsubscribe: ルート更新情報の配信登録を解除します。
logsSubscribeでのフィルターの使用
その ログの購読 このメソッドを使用すると、トランザクションログをフィルタリングすることができ、これにより返されるレスポンスの数を大幅に減らすことができます。以下に例を示します:
// To subscribe to all logs (expensive and not recommended):
const subscriptionId = await connection.onLogs(
'all',
(logs) => {
console.log('New log:', logs);
}
);
// To subscribe only to logs mentioning a specific address:
const EXAMPLE_PUBLIC_KEY = new PublicKey('YOUR_ADDRESS_HERE');
const specificAddressSubscriptionId = await connection.onLogs(
EXAMPLE_PUBLIC_KEY,
(logs) => {
console.log('Log mentioning specific address:', logs);
}
);
programSubscribe でのフィルターの使用
その プログラムを購読する このメソッドは強力ですが、多数のレスポンスが生成される可能性があります。フィルターを使用して、受け取るデータを絞り込んでください:
const filters = [
{
memcmp: {
offset: 0, // Specify the offset in the account data
bytes: 'base58_encoded_bytes_here' // Specify the bytes to compare
}
},
{
dataSize: 128 // If your account data is fixed, you can filter by data size - update this value to match your account's data size
}
];
const subscriptionId = await connection.onProgramAccountChange(
new PublicKey('Your_Program_ID_Here'),
(accountInfo) => {
console.log('Account changed:', accountInfo);
},
'confirmed',
filters
);
この例では、両方を併用しています memcmp そして dataSize フィルター。その memcmp filter は、アカウントデータの特定のオフセットにある一連のバイトを比較しますが、 dataSize アカウントのデータサイズに基づいてアカウントを絞り込みます。この組み合わせにより、特定の条件に合致するアカウントのみに焦点を当て、受け取る回答数を大幅に減らすことができます。使用方法の詳細については、 GetProgramAccountsFilter、については、 Solana RPC ドキュメント.
最適化のためのベストプラクティス
-
フィルターは具体的に設定しましょう。フィルターを具体的に設定すればするほど、関係のない回答が少なくなるでしょう。
-
ユースケースに応じて適切なコミットレベルを設定してください。コミットレベルを高く設定すると(「finalized」など)、「更新の回数は減りますが、確実性が高まります」。
-
定期的に利用していないサブスクリプションを確認・解約しましょう:有効なサブスクリプションを点検し、不要になったものは解約してください。
-
利用状況の確認:受信している返信数を把握し、必要に応じてフィルタや購読戦略を調整してください(Quicknodeダッシュボード)。
これらの最適化戦略を実施することで、必要なデータのみを受け取るようにすることができ、Solanaアプリケーションの有効性を維持しつつ、不要なクレジット消費を最小限に抑えることができます。
代替案
Quicknodeでは、Solanaからリアルタイムデータを取得するためのいくつかのソリューションを提供しています。以下のオプションを確認し、ご自身のユースケースに適したツールを見つけてください:
- WebSockets:このガイドで説明したように、WebSockets を使用すると、Solana ノードに直接接続してリアルタイムの更新情報を取得できます。これは、シンプルなアプリケーションや迅速な開発に最適です。Solana Kit で Solana WebSockets を使用したい場合は、Solana Kit を使用してSolana で WebSocket サブスクリプションを設定する方法について解説したガイド「WebSockets と Solana Kit を使用した Solana アカウントの監視」をご覧ください。
- Solana gRPC(Yellowstone互換のGeyser gRPC):Solana gRPCは、Solanaデータのストリーミングを行うための強力なgRPCインターフェースを提供し、フィルタリング機能や履歴データのサポートが組み込まれています。
- Streams:Solanaデータの処理および複数の宛先へのルーティングを行うマネージドソリューション。フィルタリング機能と履歴データのサポートが組み込まれています。
詳細については、当社のブログ記事をご覧ください。
結論
よくできました!これで、SolanaでのWebSocketサブスクリプションの使い方を理解できたはずです。皆さんはSolanaのWebSocketをどのように活用していますか?皆さんがどんなものを作っているのか、ぜひ見せてください!Discordや Twitterでアプリをシェアしてください。
詳細については、こちらの他のSolanaチュートリアルをご覧ください。また、WebSocketのサブスクリプションを楽しく体験できた方は、ぜひ当サイトのニュースレターへの登録をご検討ください。
皆様からのフィードバックを心よりお待ちしております!❤️
ご意見や新しいトピックに関するご要望などがありましたら、ぜひお知らせください。皆様からのご連絡を心よりお待ちしております。
