data について
Stellar RPCとHorizonを使い分け、Stellarのチェーンデータを調べる開発者向けリファレンスです。現在の状態やイベント、トランザクション処理に加え、より古い履歴データの取得方法も扱います。
主な機能
- 01アカウントとレジャー状態の照会
- 02トランザクションのシミュレーションと送信
- 03イベントとトランザクション結果の取得
- 04旧来の用途や履歴データでHorizonを利用
- 05履歴データの取得元を確認
活用シーン
- 01RPCでStellarの新規アプリを開発
- 02Horizonに依存するアプリを保守
- 03最近のコントラクトイベントと状態を確認
- 04RPCの保持期間を超えるデータを検索
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ stellar/ stellar-dev-skill --skill dataインストール後、Claude Code で/dataを入力して読み込まれたか確認します。
同梱ファイルhorizon.md
詳細
Stellar Data:RPC + Horizon
チェーンの状態を読み取るための API アクセスです。新しいプロジェクトでは Stellar RPC を優先的な入口として使用してください。Horizon は、レガシー用途や過去データのクエリを行うワークフロー向けに引き続き利用できます。RPC の 7 日間の期間を超える、より深い履歴データには Hubble/Galexie を使用してください。
このスキルを使う場面
- Stellar RPC メソッド(
getLatestLedger、getLedgerEntries、getEvents、simulateTransaction、sendTransaction)を呼び出す場合 - Horizon エンドポイント(アカウント、トランザクション、オペレーション、エフェクト、レジャー)にクエリを実行する場合
- ライブイベントやオペレーションをストリーミングする場合
- RPC の 7 日間の期間を超える過去データを取得する場合(Hubble、Galexie)
- 特定のワークフローで RPC と Horizon のどちらを使うか選択する場合
関連スキル
- 送信するトランザクションの構築 →
../dapp/SKILL.md - スマートコントラクトのシミュレーションとイベントの発行 →
../smart-contracts/SKILL.md - アセット残高とトラストラインの照会 →
../assets/SKILL.md - 標準規格(SEP-7 ディープリンク、SEP-10 認証) →
../standards/SKILL.md
概要
Stellar には 2 つの API パラダイムがあります。
| API | ステータス | ユースケース |
|---|---|---|
| Stellar RPC | 推奨 | スマートコントラクト、リアルタイムの状態、新しいプロジェクト |
| Horizon | レガシー用途向け | 過去データ、レガシーアプリケーション |
推奨事項:新しいプロジェクトではすべて Stellar RPC を使用してください。Horizon は主に過去データのクエリとレガシーとの互換性が必要な経路で使用してください。
タスクに合ったファイルを読む
| タスク | ファイル |
|---|---|
| RPC メソッドと使い方 | Stellar RPC(以下) |
| Horizon エンドポイント、一般的な操作、ストリーミング、ページネーション | horizon.md |
| 移行戦略 | Migration: Horizon to RPC(以下) |
| データ履歴/インデックス作成の選択肢 | Historical Data Access(以下) |
| 環境設定とエンドポイント | Network Configuration(以下) |
Stellar RPC
エンドポイント
注:SDF は Futurenet の公開 RPC を直接提供しています。Mainnet RPC には、RPC providers directory からプロバイダーを選択してください。
| ネットワーク | RPC URL |
|---|---|
| Mainnet | プロバイダー固有のエンドポイント(RPC providers directory を参照) |
| Testnet | https://soroban-testnet.stellar.org |
| Futurenet | https://rpc-futurenet.stellar.org |
| Local | http://localhost:8000/soroban/rpc |
セットアップ
import * as StellarSdk from "@stellar/stellar-sdk";
const rpc = new StellarSdk.rpc.Server("https://soroban-testnet.stellar.org");
主なメソッド
アカウントの取得
const account = await rpc.getAccount(publicKey);
// Returns account with sequence number for transaction building
ヘルス状態の取得
const health = await rpc.getHealth();
// { status: "healthy" }
最新レジャーの取得
const ledger = await rpc.getLatestLedger();
// { id: "...", sequence: 123456, protocolVersion: 25 }
レジャーエントリの取得
// Read contract storage
const key = StellarSdk.xdr.LedgerKey.contractData(
new StellarSdk.xdr.LedgerKeyContractData({
contract: new StellarSdk.Address(contractId).toScAddress(),
key: StellarSdk.xdr.ScVal.scvSymbol("Counter"),
durability: StellarSdk.xdr.ContractDataDurability.persistent(),
})
);
const entries = await rpc.getLedgerEntries(key);
if (entries.entries.length > 0) {
const value = StellarSdk.scValToNative(
entries.entries[0].val.contractData().val()
);
}
トランザクションのシミュレーション
const simulation = await rpc.simulateTransaction(transaction);
if (StellarSdk.rpc.Api.isSimulationError(simulation)) {
console.error("Simulation failed:", simulation.error);
} else if (StellarSdk.rpc.Api.isSimulationSuccess(simulation)) {
console.log("Cost:", simulation.cost);
console.log("Result:", simulation.result);
}
トランザクションの送信
const response = await rpc.sendTransaction(signedTransaction);
if (response.status === "PENDING") {
// Poll for result
let result = await rpc.getTransaction(response.hash);
while (result.status === "NOT_FOUND") {
await new Promise(r => setTimeout(r, 1000));
result = await rpc.getTransaction(response.hash);
}
if (result.status === "SUCCESS") {
console.log("Success:", result.returnValue);
} else {
console.error("Failed:", result.status);
}
}
トランザクションの取得
const tx = await rpc.getTransaction(txHash);
// status: "SUCCESS" | "FAILED" | "NOT_FOUND"
// returnValue: ScVal (for contract calls)
// ledger: number
イベントの取得
const events = await rpc.getEvents({
startLedger: 1000000,
filters: [
{
type: "contract",
contractIds: [contractId],
topics: [
["*", StellarSdk.xdr.ScVal.scvSymbol("transfer").toXDR("base64")],
],
},
],
});
for (const event of events.events) {
console.log("Event:", event.topic, event.value);
}
RPC の制限
- ほとんどのメソッドで履歴は 7 日間:
getTransaction、getEventsなどで取得できるのは最近のデータのみです getLedgersの例外:データレイクを利用するプロバイダーでは、「Infinite Scroll」によって保持期間をさかのぼり、そのプロバイダーのデータレイクが到達する範囲(場合によってはジェネシスまで)を取得できます。通常の RPC インスタンスではgetHealth().oldestLedgerまでに制限され、それより古いリクエストは-32600で失敗します。履歴の深さを想定する前に確認してください。- ストリーミングなし:更新をポーリングしてください(WebSocket なし)
- コントラクト中心:従来の Stellar データは限定的です
Horizon から RPC への移行
アカウントの読み込み
// Horizon (old)
const account = await horizonServer.loadAccount(publicKey);
// RPC (new)
const account = await rpc.getAccount(publicKey);
// Note: RPC returns less data, just what's needed for transactions
トランザクションの送信
// Horizon (for classic transactions)
const result = await horizonServer.submitTransaction(tx);
// RPC (for smart contract transactions)
const response = await rpc.sendTransaction(tx);
const result = await pollForResult(response.hash);
過去データ
// Horizon - full history
const allTxs = await horizonServer
.transactions()
.forAccount(publicKey)
.call();
// RPC - most methods limited to the retention window (~7 days)
// Exception: getLedgers can page further back (Infinite Scroll), but only as far
// as the chosen provider's retention or data-lake integration reaches.
// Always check the floor of the instance you're talking to first:
const { oldestLedger } = await rpc.getHealth();
// For guaranteed full history, use:
// 1. Hubble (SDF's BigQuery dataset)
// 2. Galexie (data pipeline)
// 3. Your own indexer
ストリーミングの代替
// Horizon - native streaming
server.payments().stream({ onmessage: handlePayment });
// RPC - polling (no native streaming)
async function pollForUpdates() {
const lastLedger = await rpc.getLatestLedger();
// Check for new events/transactions
// Repeat on interval
}
setInterval(pollForUpdates, 5000);
過去データへのアクセス
RPC の保持期間(約 7 日間)より古いデータについて(ほとんどの RPC メソッドでは利用できません。getLedgers は、データレイクを利用するプロバイダーでのみ、さらに古いデータを取得できます。以下の Data Lake を参照してください):
Hubble(BigQuery)
-- Query Stellar data in BigQuery
SELECT *
FROM `crypto-stellar.crypto_stellar.history_transactions`
WHERE source_account = 'G...'
ORDER BY created_at DESC
LIMIT 100
Galexie
Stellar のレジャーデータを処理するためのセルフホスト可能なデータパイプライン:
データレイク
RPC の「無限スクロール」は、クラウドベースのオブジェクトストアである Stellar データレイク(SEP-0054 形式)によって実現されています。getLedgers で遡れる履歴の深さはメソッドではなく、プロバイダーの特性です。インスタンスが保持期間を超える履歴を提供するのは、運営者がデータレイクを接続している場合に限られます。データレイクのないインスタンス(パブリック SDF テストネット RPC を含む)では、古い開始台帳が JSON-RPC -32600 で拒否されます。ページを遡る前に、対象の台帳と getHealth().oldestLedger を比較してください。
- パブリックアクセス:
s3://aws-public-blockchain/v1.1/stellar/ledgers/pubnet(AWS Open Data) - セルフホスト:Galexie を使用して AWS S3 または Google Cloud Storage にエクスポートします
- ホステッド:Quasar (Lightsail Network) は、ホステッド Galexie データレイクと Archive RPC エンドポイントを提供します
- サイズ:約 3.8TB、年間約 0.5TB 増加
- コスト:セルフホストの場合、月額約 160 ドル(コンピューティング 60 ドル+ストレージ 100 ドル)
- ドキュメント:https://developers.stellar.org/docs/data/apis/rpc/admin-guide/data-lake-integration
サードパーティのインデクサー
RPC/Horizon では対応できない複雑なクエリ、イベントストリーミング、カスタムデータパイプラインには、次のものを利用できます。
- Mercury — Retroshades を備えた Stellar ネイティブのインデクサー、GraphQL API(https://mercurydata.app)
- SubQuery — Stellar に対応したマルチチェーンインデクサー、イベントハンドラー(https://subquery.network)
- Goldsky — リアルタイムのデータレプリケーションパイプラインとサブグラフ(https://goldsky.com)
- StellarExpert API — アセット、アカウント、台帳の解決に対応する、認証不要の無料 REST API(https://stellar.expert/openapi.html)
インデクサーの一覧は、https://developers.stellar.org/docs/data/indexers をご覧ください。
ネットワークの設定
React/Next.js 固有のセットアップについては、dapp スキルをご覧ください。 メインネット RPC については、RPC プロバイダーディレクトリにあるプロバイダーを使って
STELLAR_MAINNET_RPC_URLを設定してください。
環境変数を使ったセットアップ
// lib/stellar-config.ts
import * as StellarSdk from "@stellar/stellar-sdk";
type NetworkConfig = {
rpcUrl: string;
horizonUrl: string;
networkPassphrase: string;
friendbotUrl: string | null;
};
const requireEnv = (name: string): string => {
const value = process.env[name];
if (!value) throw new Error(`Missing required env var: ${name}`);
return value;
};
// Lazy per-network factories: requireEnv only runs for the selected network,
// so testnet/local work without the mainnet env var set.
const configs: Record<string, () => NetworkConfig> = {
mainnet: () => ({
rpcUrl: requireEnv("STELLAR_MAINNET_RPC_URL"),
horizonUrl: "https://horizon.stellar.org",
networkPassphrase: StellarSdk.Networks.PUBLIC,
friendbotUrl: null,
}),
testnet: () => ({
rpcUrl: "https://soroban-testnet.stellar.org",
horizonUrl: "https://horizon-testnet.stellar.org",
networkPassphrase: StellarSdk.Networks.TESTNET,
friendbotUrl: "https://friendbot.stellar.org",
}),
local: () => ({
rpcUrl: "http://localhost:8000/soroban/rpc",
horizonUrl: "http://localhost:8000",
networkPassphrase: "Standalone Network ; February 2017",
friendbotUrl: "http://localhost:8000/friendbot",
}),
};
const network = process.env.STELLAR_NETWORK || "testnet";
const makeConfig = configs[network];
if (!makeConfig) throw new Error(`Unknown network: ${network}`);
export const config = makeConfig();
export const rpc = new StellarSdk.rpc.Server(config.rpcUrl);
export const horizon = new StellarSdk.Horizon.Server(config.horizonUrl);
ベストプラクティス
RPC を使用するケース:
- 新しいアプリケーションの開発
- スマートコントラクトとのやり取り
- トランザクションのシミュレーションと送信
- リアルタイムのアカウント状態
Horizon を使用するケース:
- 過去のトランザクションのクエリ
- ペイメントのストリーミング
- レガシーアプリケーションの保守
- 詳細なアカウントメタデータ
エラー処理
// RPC errors
try {
const result = await rpc.sendTransaction(tx);
} catch (error) {
if (error.code === 400) {
// Invalid transaction
} else if (error.code === 503) {
// Service unavailable
}
}
// Horizon errors
try {
const result = await horizon.submitTransaction(tx);
} catch (error) {
const extras = error.response?.data?.extras;
if (extras?.result_codes) {
// Detailed error codes
console.log("Transaction:", extras.result_codes.transaction);
console.log("Operations:", extras.result_codes.operations);
}
}
レート制限
RPC と Horizon の両方にレート制限があります。
- リトライには指数バックオフを使用します
- 適宜、レスポンスをキャッシュします
- 大量のリクエストを処理するアプリケーションでは、独自のノードの運用を検討します
async function withRetry<T>(fn: () => Promise<T>, maxRetries = 3): Promise<T> {
let lastError: Error;
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
lastError = error;
if (error.response?.status === 429) {
// Rate limited - exponential backoff
await new Promise(r => setTimeout(r, Math.pow(2, i) * 1000));
} else {
throw error;
}
}
}
throw lastError;
}