メインコンテンツへスキップ

概要

Solanaブロックチェーンはデータを順序付きの追記専用台帳に格納します。これはデータの完全性とトランザクションスループットにおいて優れていますが、重大なコストが伴いま: 履歴データのクエリが非常に非効率で遅いということです。 複雑な操作は、複数のソースからのデータのフィルタリング、集約、または結合を伴うことがよくあります。このような場合、多くの実世界のアプリケーションでSolanaに直接クエリを行うことは現実的でありません。 これを解決するため、ほとんどの企業はSolanaの履歴データのプライベートインデックスを構築します。 このガイドでは、履歴データをgetTransactionsForAddressや他のアーカイブRPCメソッドを用いてバックフィルし、データベースを選び、リアルタイムストリーミングでインデックスを最新に保つ全ライフサイクルをカバーします。

これを使用する時

インデックスを構築する時:
  • あなたの製品が高速なフィルタリングクエリを必要としているが、直接のRPCコールが遅すぎる場合(例:ウォレットのトークンアカウントや残高、取引ペアの完全な履歴)
  • オンチェーンデータをフィルタリング、集約、結合、またはオフチェーンデータ(CEX価格、KYC、ラベル)と組み合わせる必要がある場合
  • PnL、ホルダー分析、またはNFT販売履歴のような事前処理およびクエリ可能なデータが必要な計算を行う場合
  • 多くのユーザーにサービスを提供しており、チェーンへのリクエストごとの遅延を許容できない場合
標準のウォレットまたは資産データのみが必要な場合、独自のインデックスを運用するよりもマネージドAPIの方が簡単かもしれません。以下の補完的オプションを参照してください。

Solanaデータのインデックス化とは何ですか?

インデックス化とは、Solanaブロックチェーンからのデータをクエリし、それをバックエンドデータベース(例:PostgreSQL、ClickHouse)に格納するプロセスです。これにより、Solana RPCコールを使用して直接ブロックチェーンにクエリを行うことなく、顧客のリクエストにすぐに対応できます。 インデクサーは通常、次の4つのことを行います:
  1. 履歴データのバックフィル: アーカイブRPCメソッドを使用してすべての履歴データをクエリする
  2. 新しいデータをストリーム: ネットワークによって確認されたときに新しいブロックを処理する
  3. データの解析と変換: 確認済みブロックから関連データを抽出 (例:トランザクション、状態の変化など)
  4. データをデータベースに整理: 新しいデータでインデックスを更新する

ほとんどの企業がSolanaインデックスを構築する理由

企業は、ネイティブRPCが提供しない特定目的のブロックチェーンデータへの迅速なリアルタイムアクセスを提供することがビジネスに依存しているため、Solanaインデックスを構築します (例:NFT販売履歴)。 企業はまた、オンチェーンデータとオフチェーンデータ(例:中央集権取引所の価格、KYC情報など)を組み合わせるためにカスタムインデックスを活用します。

ウォレットの例

例えば、Solanaウォレットがすぐにユーザーのトークンアカウントと残高を返す必要がある場合、getTokenAccountsByOwnerおよびgetTokenAccountBalanceでSolanaに直接クエリを行うことは遅すぎて製品が使えなくなる可能性があります。これに代わり、ウォレットは通常、顧客アドレス、トークン、およびアカウント残高の独自のインデックスを維持します。

トレーディングの例

同様に、暗号取引会社は特定の取引ペア(例:SOL-USDC)または特定の市場で発生するすべての取引活動をログに記録し、トレーディングアルゴリズムをバックテストしたいと考えるかもしれません。 このデータを取得するためにブロックチェーンを直接クエリすることは、実用的な取引分析にはあまりにも遅すぎます。代わりに、量的トレーダーはSOL-USDC市場のインデックスを構築し、LaserStreamなどのリアルタイムストリーミング製品を使用して最新の取引で更新することを選ぶかもしれません。

フィルタリングの例

ユーザーがフロントエンドアプリケーションで特定の基準でトランザクションをフィルタリングしたいとしましょう(例:トークンタイプ、転送額、日付、ウォレットアドレス)。 インデクサーがなければ、アプリケーションは何百万ものトランザクションを調べ、フィルタ基準に基づいて各1つを確認する必要があります。 このプロセスは、現代の製品ユーザー体験には遅すぎます。

PnLの例

トレーダーの利益と損失(PnL)を計算するには、次のことを行う必要があります:
  • 特定の期間におけるウォレットに関連するすべてのトランザクションを見つける
  • スワップトランザクションをフィルタリングして、買いまたは売りとしてラベルを付ける
  • 各スワップでユーザーが支払った手数料を判定する
  • 各トレード時点での各トークンの過去価格データを取得する
  • 各トランザクションのPnLを集計して、トレーダーの合計PnLを計算する
これをリアルタイムで計算することは実用的でなく、より高速でスケーラブルなソリューションを必要とします。 インデックスを使用すると、このすべての情報がすでに処理されてクエリ可能なデータベースに格納されています。したがって、トレーダーのPnLを計算するのが瞬時に提供される単一のAPIコールになります。 Solanaインデックスをバックフィルし、それを最新の状態に保つための3つのアプローチについて見ていきます。

ステップ1: 履歴データを取得する

Solanaインデックスを構築する最初のステップは、関心のあるすべての履歴データを取得することです。 これを行うには主に3つの方法があります:
  1. getTransactionsForAddress (推奨)
  2. getSignaturesForAddress および getTransaction
  3. getBlock

方法1: getTransactionsForAddress(推奨)

getTransactionsForAddress RPCメソッドを使用すると、任意のブロックチェーンデータセグメントの完全なトランザクション詳細を取得できます。その強力なフィルタリング能力により、インデックスに必要のないデータを取得するために時間を無駄にすることがなく、逆検索機能によりトランザクションを時系列順に取得できます。

この方法を使用する手順

  • 必要なデータの時間範囲を決定し、フィルタを設定する
  • transactionDetailsfullに設定して、すべてのトランザクション詳細を取得する
  • 必要に応じて、関連するトークンアカウントトランザクションを含めるようtokenAccountsフィルタを設定する
  • paginationTokenを使用して結果をページネーションする
  • 各イテレーションで、必要なデータを抽出し、データベースに保存する

getTransactionsForAddressを使用する利点

gTFAエンドポイントを使用する主な利点は、速度とシンプルさです。スロットおよび時間ベースのフィルタ、トークンアカウントサポート、逆検索、ページネーションを使用すると、複雑なループやリトライロジックなしで、Solanaの歴史上の任意の時間から任意のデータを取得できます。getSignaturesForAddressと異なり、アドレスが所有する関連トークンアカウントを含むトランザクションも含めることができます。 あなたのインデックスが特にトークンとネイティブSOLの移動(支払い、台帳、残高調整)に関するものである場合、getTransfersByAddressは、完全なトランザクションの代わりにパースされた、調整可能な転送行を返すため、パースのステップを省けることがあります。

方法2: getSignaturesForAddressとgetTransaction

gTFAのリリース前、履歴データをクエリする標準的なアプローチは、getSignaturesForAddressを使用して(最新から最も古いものまで)再帰的に署名をループし、getTransactionを呼び出して完全なトランザクション詳細を抽出することでした。

この方法を使う手順

この方法を使うための基本的な手順は次のとおりです:
  • getSignaturesForAddressを呼び出す
  • このコールで受け取った最後のトランザクションの署名を保存する
  • 次のgetSignaturesForAddressの呼び出しには、この署名をbeforeパラメータに設定する
  • 必要に応じてこのままループする
  • この方法で取得した各トランザクション署名について、getTransactionを呼び出して、その完全なトランザクション詳細を取得する
  • 関連データをデータベースに挿入する

この方法の欠点

残念ながら、この方法を使用するには以下が必要です:
  • 最新のトランザクションから始めて後戻りする
  • 各トランザクションに対して追加のRPCを呼び出す
  • 同時処理を扱うスレッドセーフなキューを構築する
  • データを見逃したりレート制限されないようにリトライとバックオフのためのロジックを構築する
  • アドレスが所有する関連トークンアカウントを含むトランザクションは含まない
この方法は機能しますが、より複雑で柔軟性がなく、より多くのクレジットを消費します。トークンアカウントを含む完全なウォレット履歴には、getTransactionsForAddressを代わりに使用してください。

方法3: getBlockを使用

getBlock方法は、ターゲットブロック内のトランザクションの高い割合が分析に関連する場合、特に効果的です。たとえば、DFlowのアグリゲーターやPump.funプログラム、またはSolanaのトークンプログラムなどの頻繁に使用されるSolanaプログラムのトランザクションをインデックスする場合です。

この方法を使う手順

getBlockで履歴データをクエリするための基本的なプロセスは次のとおりです:
  • クエリする時間範囲を決定する
  • この時間範囲をスロット番号に変換する
  • 対応するブロックを順次(順方向か逆方向か)取得する
  • 各ブロックでインデックスに関連するトランザクションをフィルタする
  • それらからの関連情報をインデックスに保存する
ほとんどのユースケースでは、この方法は本質的に無駄です。というのも、通常、ブロック内のすべてのトランザクションを取得するためです。 この方法を使用するのは、頻繁に使用されるプログラムのトランザクションを調べる場合や、アドレスベースのフィルタリングで対象データをキャプチャできない場合のみです。

ステップ2: Solanaデータをデータベースと同期

履歴データを取得した後、それを変換し、効率的にデータベースに格納する必要があります。 ストレージの選択は特定のユースケースに合わせるべきです — 万能なソリューションはありません。適切なデータベースは、データセットのサイズ、遅延要求、クエリパターン、チームの専門知識に依存します。

オプション1: SQLデータベース

Solanaデータのリレーショナルデータベース(PostgreSQLなど)への格納は、ほとんどのユースケースで推奨されます。SQLは柔軟で普及しており、学びやすいです。現代のリレーショナルデータベースは、1億行以上にも拡張可能でありながら、ACIDへの準拠、複雑な結合、強力な二次インデックスの利点を提供します。 SQLiteをプロトタイピング、ローカル開発、または単一ファイルのデータベースでゼロ構成を希望する場合に使用してください。データセットが数ギガバイト以下に保たれている場合に理想的です。 PostgreSQLを使用して、複数のクライアントからのデータレプリケーションまたは同時アクセスが必要な本番アプリケーションに使用してください。 ほとんどのプロダクションレベルのSolanaインデクサーには、PostgreSQLが私たちの推奨する選択です。

実装例:

例として、PostgreSQLデータベースにトークン転送を格納する方法を示します。 最初にテーブルを作成します:
次に、頻繁にクエリされる列にインデックスを追加します:
頻繁にクエリされるデータのサブセットにのみインデックスを作成する場合に部分インデックスを作成することもできます。 高価値の転送のインデックスを作成する方法は次のとおりです:
データをバックフィルする際、最適な書き込み速度のためにバルクINSERTと準備済みステートメントを使用してください。

オプション2: 列指向データベース

列指向データベースは分析クエリ、集約、高容量タイムシリーズデータのために最適化されています。数十億のトランザクションをインデックス化する必要がある場合、ClickHouseやCassandraなどの列指向データベースが最適です。 ClickHouseを使用して、大規模データセットのリアルタイム分析クエリが必要な場合 — 高速な読み取り、集約、タイムシリーズ分析に最適化されています。 Cassandraを使用して、非常に高い書き込みスループット、無労力の水平スケーリング、高い耐障害性が必要な場合です。これにより、Solanaデータの大量連続インジェストに理想的です。

実装例:

ClickHouseデータベースにトークン転送を格納する方法を示します。 この目的のために、MergeTreeテーブルエンジンを使用するテーブルを作成します。これは高いインジェストレートのために設計されているので、インデックス化に最適です。 このコマンドを使用します:
このセットアップでは、(token_mint, date)が主キーおよびソートキーの両方として設定されています。ClickHouseはデータをディスク上でソートキーに基づいて順序付けます。最適なのは単一のtoken mintをクエリし、日付範囲で応答を絞り込むことです。 サンプルクエリはこちらです:
トランザクション署名とアドレスは[FixedString(N)]データフォーマットを使用して保存され、ちょうどNバイトを保存します。ClickHouseは自動的にデータを圧縮し、これがストレージコストを10から20倍削減し、クエリパフォーマンスを改善します。 クエリパフォーマンスを最適化するために、一般的な集約を事前に計算するMaterialized Viewsを使用します。 例えば、ダッシュボードのボリューム関連グラフで使用するトークンの日次転送ボリュームを事前計算できます。

オプション3: データレイク

データレイクは、大量の生データおよび処理済みブロックチェーンデータを長期的にアーカイブおよび分析クエリに保管するのに最適です。 シンプルな実装では、Amason Athenaと共にParquetデータフォーマットを使用します。 Parquetは効率的なデータストレージおよびリトリーバルのために設計された列指向データファイルフォーマットです。 Amazon Athenaは標準SQLを使用してAmazon S3に保存されたデータを分析するインタラクティブなクエリサービスで、インフラストラクチャを設定したりデータを別のデータベースにロードする必要はありません。
データレイクは、構造化されていない大規模なデータのクエリが必要な場合のみ推奨されます。ほとんどのユースケースには、SQLデータベース(オプション1)の使用をお勧めします。

実装例:

トークン転送のアーカイブを作成し、それをクエリしたいと考えています。 まず、S3にそれらを保管する必要があります:solana_indexという名前のバケットを作成し、このキー構造を使用してトークン転送データを時間で分割します:
各日の転送は対応する日付フォルダーの個別のParquetファイルに保存されます。 Solanaからの転送を処理する際、それらをParquetフォーマットに変換し、適切なS3オブジェクトに書き込みます。 その後、Athenaでテーブルを作成し、それをバケットに接続します。これにより、バケット内のデータに対してこのような直接クエリが可能になります:

インデックスフレームワークの使用

Carbon および同様のフレームワークを使用して、ボイラープレートコードを作成することなく、インデクサーを数日で設定します。

主な機能:

  • 人気のプログラム(トークンプログラム、DeFiプロトコル、Metaplex)のための事前構築されたデコーダー
  • 設定可能なデータソース(RPC, LaserStream, 強化WebSockets)
  • バックフィルとリアルタイムストリーミングの両方をサポート
  • 複数のストレージバックエンドへの出力(Postgresは標準で提供されます)
  • 完全にカスタマイズ可能:独自のデータソース、デコーダー、データシンクをセットアップできます

ステップ3: インデックスを最新の状態に保つ

履歴データをバックフィルした後、新しいブロックチェーンアクティビティでインデックスを最新に保つためのリアルタイムストリーミングソリューションが必要です。これがないと、インデックスは古くなります。

方法1: LaserStream(推奨)

すべてのプロダクションインデックスユースケースのデフォルトの選択肢としてLaserStream gRPCをお勧めします。これは信頼性が高く、超低遅延で耐障害のデータストリーミングを目的に設計されています。 LaserStreamを使用するいくつかの利点は次のとおりです:
  • 24時間の履歴再生:インデクサーが切断されても、LaserStreamは切断された場所からすべての未処理トランザクションを自動的に再生します
  • 自動再接続:私たちのLaserStream SDK(Rust、Go、JS/TS)はネットワークの中断をシームレスに処理します
  • **ノードフ
ェイルオーバー**:LaserStream接続は複数のノードから同時にデータを集約し、最大の稼働時間を保証します 速度と信頼性を兼ね備えているLaserStreamは、リアルタイムアプリケーション、ライブトランザクションフィード、取引ダッシュボード、インスタント残高更新に最適です。

インデックス化におけるLaserStreamの使用方法

ブロックチェーンイベントを購読するためにsubscribeメソッドを使用します。 いくつかのベストプラクティスを以下に示します:
  • フィルタをできるだけ絞り込む:索引に実際に必要なデータにのみ登録して、帯域幅の消費と必要な処理を最小限に抑える。
  • confirmedのコミットメントレベルを使用する:これにより遅延と最終化のバランスが取れます。 processedレベルは信頼性が低すぎ、 finalizedは約13秒の遅延を加えます。
  • failed: falseを設定する:失敗したトランザクションを追跡する必要がない場合。
  • 投票トランザクションを除外する (vote: false): インデックス化には関連しません。
例を見てみましょう。 すべての新しいトークン転送をインデックス化するために以下のサブスクリプションを使用します:

方法2: LaserStream WebSocketを使用

LaserStream WebSocket — LaserStreamのWebSocketバリアントで、Helius特有のtransactionSubscribe拡張を含みます — LaserStream gRPCと同じバックエンドで動作し、gRPCが必要でない場合のコスト効果の高いリアルタイムストリーミングの代わりです。 LaserStream WebSocketを使用するべき場合:
  • アプリケーションが時折のデータギャップを許容できる場合
  • リアルタイムアップデートが重要だが、ミッションクリティカルでない場合
  • 既存のインフラストラクチャがあり、欠落したデータの検出とバックフィルができる場合
  • 予算の制約が大きく、ストリーミングコストを最小限に抑える必要がある場合
  • LaserStreamの採用前にプロトタイプまたはテスト中の場合
ただし、WebSocketsを選ぶ際にはいくつかのトレードオフを考慮する必要があります:
  • 速度:LaserStream WebSocketはLaserStream gRPCと同じバックエンドで動作しますが、WebSocketプロトコルにはJSONフレーミングとメッセージごとのオーバーヘッドがあります。最低遅延を求めるならLaserStream gRPCを使用してください。
  • 信頼性:履歴の再生保証はありません。WebSocketが切断された場合、自動で検出し、RPCメソッドを使用して手動でギャップを埋める必要があります。
  • 複雑性:データ完全性を保証するために追加の監視インフラストラクチャが必要です。

WebSocketsを使ったインデックス化の方法

すべてのトークン転送を格納するインデックスを更新するには、transactionSubscribeに次のように購読します:

始める

堅牢なSolanaインデックスを構築し、データをバックフィルするためには、3つのコア課題を解決する必要があります:
  1. 効率的な履歴データの取得
  2. データを迅速に取得するための変換と保存
  3. インデックス化されたSolanaデータをリアルタイムで更新し続けること
私たちの新しい最先端のアーカイブシステムgetTransactionsForAddressのようなアーカイブコール、LaserStreamのような業界をリードするデータストリーミングソリューションを使用することで、Solanaインデックスの構築はこれまで以上に簡単で実用的になりました。

補完的オプション

独自のインデックスを運用することは完全な制御を提供しますが、それが常に必要であるわけではありません。一般的なニーズには、マネージドHelius APIがカスタムインデックスを置き換えるか、補完することがあります:
  • DAS API ー NFT、代替可能トークン、圧縮資産(メタデータ、所有権、残高、オーナー/コレクション/クリエイターごとのクエリ)をインデックス化せずにクエリします。
  • ウォレット API ー ウォレットの残高、履歴、転送、アイデンティティに関する高レベルRESTエンドポイント、USD換算値、シンプルな応答形。
多くのチームがこれらをポートフォリオ、トークン、ウォレットデータのために使用し、マネージドAPIsがカバーしない特定目的のデータにカスタムインデックスを使用しています。

次のステップ