メインコンテンツへスキップ
オンチェーンの変化に応答するアプリケーションを構築する際、アカウントの更新についてRPCエンドポイントをポーリングすることは非効率で遅いです。アカウントサブスクリプションは、アカウントの状態変化に関するリアルタイムの更新を直接アプリケーションに提供することでこれを解決します。 このガイドでは、アカウントサブスクリプションについて知っておくべきこと、動作原理、特定のユースケースに合わせて最適化する方法を解説します。

アカウントモデルのコンテキスト

Solanaのアカウントとその構造に詳しい場合、このセクションはスキップしてください。
Solanaはアカウントベースのモデルを使用しています。すべてのデータはアカウント内に存在し、データとメタデータを保持するコンテナとして機能します。各アカウントは次のものを持ちます:
  • データ: プログラムの状態、トークン残高、その他の情報を格納する実際のバイト
  • オーナー: このアカウントを制御し、そのデータを修正できるプログラム
  • Lamports: 家賃免除のためのアカウントのSOL残高
  • 実行可能: このアカウントがプログラムコードを含むかどうか
プログラムはステートレスであり、内部にデータを保存しません。代わりに、状態を保存するために別々のアカウントを作成し管理します。プログラムとやり取りする際には、読み取るまたは書き込むべきアカウントを渡します。 この設計により、アカウントサブスクリプションが強力になります。特定のアカウント、プログラムが所有するすべてのアカウント、または特定の基準に一致するアカウントの変更を監視できます。

基本的なアカウントサブスクリプション

トークンアカウントの変更を購読するシンプルな例から始めましょう。このスクリプトはトークンの残高が変わるたびに通知します:
この基本的なサブスクリプションを実行すると、リアルタイムのアカウント更新がコンソールにストリームされるのが分かります:
何が起こったのでしょうか? 私たちのサブスクリプションは完璧に機能しました! Laserstreamにトークンアカウントの変更を通知するよう依頼し、アカウントBKMHWYLAX4un3HUbR7a3u9jPmzCiLNa4mSj1RiX11eWFについて更新を受け取りました。 このアカウントに含まれるのは:
  • 2,039,280ラマポート(約0.002 SOLの残高 - これはこのトークンアカウントの家賃免除額です)
  • オーナープログラム TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA(これはSPLトークンプログラムです)
  • トランザクション署名 5C9Hr5nG2j8eQz6inxPmfyjbYdmXddzUDyR1iQgEnjYQ3RNvuP4Zzc8t1enLNy7Rk8KNCtQPEQztENYWxkt9GaVD(このアカウントの変更を引き起こした特定のトランザクションを示します)
  • スロット352366983(この更新がブロックチェーン上で発生したときのもの)
  • データフィールド(base58でエンコードされた165バイトのアカウントデータが含まれます)

データサイズを用いたアカウントフィルタリングの理解

データフィールドは重要です - 実際のトークンアカウントの構造を含んでいるからです。スマートアカウントフィルタリングのためにこの理解を活用しましょう。

なぜデータサイズフィルタリングを使うのか

フィルタリングが必要な理由を理解するために、まずトークンアカウントが何であるかを理解しましょう。ウォレットが保有する各トークンについて、オンチェーン上に別個のアカウントがあります。 ウォレットが3つの異なるトークンを持っている場合(USDC、BONK、SOL)、実際には1つのウォレットアカウント(メインのSOLアカウント)と3つのトークンアカウント(各トークンタイプごとに1つ)があります。各トークンアカウントはちょうど165バイトであり、どのトークンを保持しているか(ミントアドレス)、誰が所有しているか(ウォレットアドレス)、およびそのトークンの量(量)が格納されています。 トークンプログラムはSolana上に何百万ものアカウントを持っていますが、それらすべてがユーザーのバランスを保有する「トークンアカウント」ではありません。フィルタリングの有無で何が起こるかを見てみましょう: フィルタリングなし - 大洪水:
これはトークンプログラムが所有するすべてのアカウントを購読します、これには以下が含まれます:
  • トークンアカウント(165バイト)- ユーザーのバランス: 数百万のアカウント
  • ミントアカウント(82バイト)- トークン定義: 数十万のアカウント
  • マルチシグアカウント(355バイト)- 共有ウォレット制御: 数万のアカウント
  • 関連トークンプログラムアカウント(さまざまなサイズ)- 数百万のアカウント
結果: アプリケーションは大量のアカウント更新を常に受け取り、そのほとんどは重要ではありません。
スマートフィルタリングで精密:
これは165バイトのアカウントのみに絞り込み、具体的にはユーザートークン残高アカウントです。トークン転送、残高変動、ポートフォリオ更新を追跡するために正確に必要なものです。 違い:
  • フィルタリングなし: 数百万のアカウント更新(ミント作成、マルチシグの変更など)
  • データサイズフィルタリングあり: トークン残高の変更のみ
これは、ユーザートークン保有を実際に表すアカウントに焦点を絞ることでノイズを大幅に削減します。

なぜ165バイトなのか

これは魔法ではありません - SPLトークンプログラムのアカウント構造から来ています。ソースコードを見ると、Accountの構造体がちょうど165バイトで定義されていることがわかります:
この固定サイズにより、標準的なトークンアカウントを正確にフィルタリングし、以下を除外できます:
  • ミントアカウント(82バイト)
  • マルチシグアカウント(355バイト)
  • 関連トークンアカウントプログラムアカウント
  • 異なるサイズの他のトークン関連アカウント
他のプログラムでアカウントサイズを計算するには、アンカースペースリファレンスを参照してください - さまざまなデータ型がどれだけのスペースを占有するかを示しています(Pubkey = 32バイト、u64 = 8バイトなど)。

アカウント構造のデコード

165バイトをフィルタリングした理由を理解したうえで、例のアカウント内の内容をデコードしましょう:
165バイトは次のように分解されます:
  • バイト0-31: ミントアドレス(このアカウントが保有するトークン)
  • バイト32-63: オーナーアドレス(このトークンアカウントの所有者)
  • バイト64-71: トークン量(アカウントに含まれるトークン数)
  • バイト72-164: 追加メタデータ(代理人、状態、クローズオーソリティなど)
この構造化アプローチにより、標準トークンアカウントの更新のみを受信し、他のアカウントタイプからのノイズを排除します。

フィルタの組み合わせ: データサイズ+memcmpでレーザ精度

ミントアドレスがバイト0-31にあることがわかったので、さらに特定の情報に絞り込むことができます。例えば、USDCトークンアカウントのみを監視したい場合、datasizeフィルターをmemcmpフィルターと組み合わせて、特定のミントアドレスをターゲットにできます:
進行的なフィルタリング戦略:
  1. オーナーフィルター: “トークンプログラムが所有するアカウントを教えて”(数百万のアカウント)
  2. データサイズフィルター: “だが165バイトの標準トークンアカウントのみ”(数十万)
  3. Memcmpフィルター: “そしてUSDCを保有するもののみ”(数千)
この広範から特定へと進むことが効率的なアカウントモニタリングの鍵です。各フィルターは結果セットを絞り込み、必要な更新のみを受け取ります。 重要: すべてのフィルターはANDロジックを使用します - アカウント更新がトリガーされるためには、すべての条件を満たす必要があります。

USDCアカウント更新の読み取り: 誰が、どのくらい、どこで?

これらのフィルタリングされた更新には実際に何が含まれるかを見てみましょう。トークンアカウントが変わったときに重要な質問に答えるUSDC専用モニターを作成します:
  • 誰がこのトークンアカウントを所有していますか?
  • どのくらいの量のUSDCが含まれていますか?
  • どこに(どの特定のアカウントが)変わりましたか?
  • いつこの変化が起こりましたか?
  • どのトランザクションがこの変化を引き起こしましたか?
生のアカウント更新にはバイナリデータが含まれています。Solanaはアドレスと署名にbase58エンコーディングを使用しているため、バイナリバッファオブジェクトを可読の文字列に変換するためにbs58.encode()関数を使用します。
このUSDCモニターを実行すると、次のようなクリーンで構造化された出力が表示されます:
各ブロックは状態が変わったUSDCアカウントを表しています。最初のアカウントは現在1,500 USDCを保持しており、2番目のアカウントは0 USDCに空にされました。トランザクション後すぐに現在の残高が表示され、どの特定のアカウントが変わったかおよびいつそれが起こったかがわかります。 アカウントサブスクリプションは、各アカウントに何が起こったかの最終結果を示しますが、トランザクションの詳細は示しません。完全なトランザクションコンテキスト(誰が誰に送ったのか、手数料など)を理解する必要がある場合は、表示された署名を使用して完全なトランザクションを取得する必要があります。

完全なフィルタリングリファレンス

これまでに使用した基本的なownerdatasizememcmpフィルタに加えて、アカウントサブスクリプションは結果をさらに絞り込むための追加のフィルタリングオプションをサポートしています:

特定アカウントフィルタリング

公開鍵で正確なアカウントを監視します:
このアプローチは、アプリケーションにとって重要なアカウントを正確に知っている場合に有用です - たとえば、アプリケーションの財務アカウントや特定のユーザーアカウントを監視する場合など。 非常に大きなアカウントセットの場合、明示的な公開鍵リストは高コストになります — 購読リクエストでアカウントごとに32バイトです。約10,000アカウントを超える場合、圧縮されたcuckoo filter(アカウントあたり約3〜4バイト)を使用して、1つのストリームで数十万のアカウントを追跡します。RustおよびJavaScript SDKで利用可能です。

組み合わせたフィルタリング戦略

複数のフィルタタイプを組み合わせることによりパワーが発揮されます。こちらはメンタルモデルです:
  1. 広範なネットをキャストする owner - “このプログラムが管理するすべてのアカウントを教えて”
  2. 構造でフィルタリングする datasize - “だが特定のタイプのアカウントのみ”
  3. 特定のデータをターゲットにする memcmp - “そして特定の情報を含むもののみ”
  4. 既知のアカウントを監視する account - “または私が重要だと思う正確なアカウントを監視するだけ”
たとえば、高価値のUSDCアカウントを監視する場合:
各フィルターが受け取る更新の量を削減するキーインサイトです。フィルタリングしないと、圧倒的な量のアカウント更新を受ける可能性があります。スマートフィルタリングを使用すると、特定のユースケースにとって重要な更新のみを受信します。

大きな図を理解する

アカウントサブスクリプションはデータベースの変更のライブフィードを監視するようなものです。Solanaの状態は基本的に巨大なキー・バリューストアであり、各アカウントがエントリです。プログラムが実行されると、これらのアカウントが変更されます。サブスクリプションにより、特定のエントリがリアルタイムで変更されるのを監視できます。 フィルタリングシステムはデータベースインデックスのように機能します - 単に「すべての変更」を監視するのではなく、「これらの基準に一致するアカウントの変更」を監視しているのです。これにより、システムを無関係なデータで圧倒することなく、関連するオンチェーンイベントにすぐに応答するアプリケーションを構築することが可能になります。

このパターンを他のプログラムに適用する

習得したアプローチは、どのSolanaプログラムにも適用できます。こちらが一般的なパターンです:
  1. アカウント構造を調査する - プログラムのソースコードまたはドキュメントを確認
  2. オーナーフィルタリングから始める - アカウントを管理するプログラムをターゲットに
  3. 構造的フィルタを適用する - アカウントのサイズ、データパターン、またはその他の特徴を使用して特定のアカウントタイプに絞り込む
  4. ターゲットフィルタを追加する - アプリケーションにとって重要な特定のアカウント、状態、またはデータ値に集中する