关于 data
面向开发者的参考技能,介绍如何选择 Stellar RPC 或 Horizon 来查询 Stellar 链上数据,涵盖实时状态、事件、交易流程及更深入的历史数据选项。
主要功能
- 01查询账户和账本状态
- 02模拟并提交交易
- 03读取事件和交易结果
- 04使用 Horizon 处理旧版需求与历史数据
- 05了解历史数据来源
适用场景
- 01使用 RPC 构建 Stellar 新应用
- 02维护依赖 Horizon 的应用
- 03检查近期合约事件和状态
- 04查找超出 RPC 保留期限的数据
安装
复制下面的命令,在终端中运行。
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 提供两种 API 范式:
| API | 状态 | 使用场景 |
|---|---|---|
| Stellar RPC | 首选 | 智能合约、实时状态、新项目 |
| Horizon | 面向传统应用 | 历史数据、传统应用 |
建议:所有新项目都使用 Stellar RPC。Horizon 主要用于历史查询和传统兼容路径。
阅读与任务相匹配的文件
| 任务 | 文件 |
|---|---|
| RPC 方法和用法 | Stellar RPC(下文) |
| Horizon 端点、常见操作、流式传输、分页 | horizon.md |
| 迁移策略 | 迁移:从 Horizon 到 RPC(下文) |
| 历史数据/索引选项 | 历史数据访问(下文) |
| 环境设置和端点 | 网络配置(下文) |
Stellar RPC
端点
注意:SDF 直接提供 Futurenet 公共 RPC。对于 Mainnet RPC,请从 RPC 提供商目录 中选择提供商。
| 网络 | RPC URL |
|---|---|
| Mainnet | 提供商专属端点(参见 RPC 提供商目录) |
| 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例外:对于由数据湖支持的提供商,“无限滚动”可回溯到保留期限之前,最远可达该提供商的数据湖范围(可能追溯至创世账本)。对于普通 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 才能查询更早的数据,参见下文的数据湖):
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 数据湖和归档 RPC 端点
- 大小:约 3.8TB,每年增长约 0.5TB
- 成本:自托管约每月 $160($60 计算资源 + $100 存储)
- 文档:https://developers.stellar.org/docs/data/apis/rpc/admin-guide/data-lake-integration
第三方索引器
对于超出 RPC/Horizon 能力范围的复杂查询、事件流或自定义数据管道:
- Mercury — Stellar 原生索引器,支持 Retroshades 和 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 skill。 关于主网 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;
}