跳转到主要内容
历史账户状态功能目前处于测试阶段。参数、定价、响应格式和数据覆盖范围可能会发生变化。

什么是历史账户状态?

标准 Solana RPC 方法(如 getAccountInfo 和 getTokenAccountsByOwner)仅返回账户的当前状态。验证者只保留每个账户的最新版本,因此每次写入都会替换上一次写入。交易历史记录也无法弥补这一缺失:getTransaction 和 getBlock 返回指令、日志和余额变化,而不是程序写入的账户数据。 Helius 的历史账户方法会返回账户在过去某个时隙时的状态,或者该时隙内某笔特定交易执行后的即时状态。由于你可以读取同一时隙内两笔交易之间的状态,因此即使一个账户在单个时隙中被多次写入,也能准确查看某笔交易所做的更改。你可以使用这些方法:
  • 检查钱包在特定时间点的代币余额,用于审计、会计或税务报告
  • 通过读取交易执行前后所涉及的账户来调试交易
  • 读取协议在过去某个时隙的账户,例如资金池、金库或预言机,而无需自行重放账本
这些是 Helius 独有的 RPC 方法,不属于标准 Solana RPC。

我应该使用哪种方法?

两者都接受相同的三个参数:地址、用于选择历史位置的查询对象,以及配置对象。

可用性和定价

  • 套餐:Business 套餐及更高级别的套餐。
  • 费用:测试期间每次调用消耗 0 积分(定价可能会发生变化)。
  • 速率限制:getHistoricalAccountInfo 每秒允许 100 个请求,最大突发请求数为 100。getHistoricalTokenAccountsByOwner 每秒允许 10 个请求,最大突发请求数为 10。这些限制与你的套餐 RPC 速率限制相互独立,超出限制的请求会返回 HTTP 429。请参阅速率限制。
  • 网络:仅限主网。请通过标准主网端点 https://mainnet.helius-rpc.com 或 Gatekeeper 端点 https://beta.helius-rpc.com 调用这些方法。开发网会返回错误 -32601。

数据覆盖范围

历史数据从时隙 444,959,999(2026 年 9 月 7 日)开始提供。这是纪元 1029 的最后一个时隙,因此它保存的是纪元 1030 开始时的状态。请求更早的时隙会返回错误 -32018(“账户历史记录不可用”)。

选择历史位置

第二个参数是一个查询对象,包含两个可选字段:slot 和 transactionIndex: 使用 transactionIndex 时必须提供 slot。 纪元奖励等非交易写入会在时隙结束时应用。包含 transactionIndex 的查询永远不会包含这些写入,即使查询的是该时隙中的最后一笔交易。要包含这些写入,请查询 slot,但不要提供 transactionIndex。

读取交易前后的状态

要检查一笔交易更改了什么,请读取该账户两次:
  • 交易后:使用该交易自身的 slot 和 transactionIndex 进行查询。
  • 交易前:查询相同的 slot,并将 transactionIndex 减 1。如果该交易是其所在时隙中的第一笔交易(索引为 0),请查询前一个时隙且不提供 transactionIndex。
getTransactionsForAddress 会返回每笔交易的 slot 和 transactionIndex,因此你可以在其中找到交易的位置,并将其直接传递给这些方法。

了解 lastUpdatedAt

每个返回的账户都包含 lastUpdatedAt,它表示生成所接收状态的写入位置:
  • slot 和 transactionIndex 指向查询位置或该位置之前的最后一次写入。在上面的示例中,对时隙 445,000,000 的查询返回了最后由时隙 444,999,837 中索引为 700 的交易写入的状态。
  • transactionIndex: null 表示写入发生在交易之外,例如纪元奖励。
  • slot: 444959999 与 transactionIndex: null 一起出现 表示自历史数据覆盖开始以来,该账户从未发生变化。这并不表示该账户是在这个时隙中写入的。

null 值的含义

当账户在你查询的位置不存在时,value 为 null。该账户可能尚未创建,也可能已经关闭。
  • 返回 null 的响应不包含 lastUpdatedAt,因此无法说明账户何时创建或关闭。要查找这些交易,请使用 getTransactionsForAddress。
  • 从未存在过的地址也会返回 null,而不是错误。
  • 历史数据覆盖范围之前的时隙会对每个地址返回错误 -32018,无论账户是否曾经存在。

向前回溯账户历史记录

使用 lastUpdatedAt 作为游标,按从新到旧的顺序列出账户曾有过的所有状态。每次读取后,查询所接收写入之前的位置: 当 value 为 null 时停止,这表示你刚刚越过的写入创建了该账户。当 lastUpdatedAt.slot 到达历史数据覆盖起点 444,959,999 时,也应停止。
  • 当前已关闭的账户在最新时隙会返回 null。请传入该账户仍然存在时的 start 位置。
  • 非交易写入会回退到前一个时隙。如果同一账户在该时隙更早时也曾由交易写入,则回溯过程会跳过该状态。
  • 要按时间向前遍历,请使用 getTransactionsForAddress 和 sortOrder: "asc",按从旧到新的顺序列出账户的交易。然后读取每笔交易的 slot 和 transactionIndex 所对应的状态。

响应上下文

每个响应中的 context.slot 都是 Helius 处理请求时已索引的最新时隙,而不是你查询的时隙。请使用请求中的 slot 或 lastUpdatedAt 来理解所收到的数据。

保持分页一致性

getHistoricalTokenAccountsByOwner 每次返回一页结果。每个响应都包含 value.accounts 和 value.paginationKey。
  • 在配置对象中回传 paginationKey,以获取下一页。
  • 仅当 paginationKey 为 null 时才停止。 即使某一页结果较少或为空,其后仍可能有更多结果。
  • 在所有页面中固定 slot。 如果省略 slot,每一页都会读取最新的已索引时隙,因此不同页面的数据可能来自不同时间点。
  • 将 limit 设置为 1 到 5,000 之间的值。默认值为 5,000。

编码和解析后的数据

  • 支持的编码包括 base64(默认值)、base64+zstd 和 jsonParsed。不支持 base58 和 binary。
  • jsonParsed 代币账户使用铸币账户在查询位置时的小数位数,因此即使该铸币账户此后发生了变化,金额仍然准确。如果无法读取该位置的铸币账户,则该账户会回退到 base64。
  • dataSlice 的工作方式与 getAccountInfo 中的相同。

承诺级别

commitment 接受 finalized(默认值)和 confirmed。不支持 processed。如果 slot 比所请求承诺级别允许的时隙更新,则会返回错误 -32004。

错误

常见问题

如何获取 Solana 账户在特定时隙的状态?

使用地址和 { "slot": S } 调用 getHistoricalAccountInfo。它会以与 getAccountInfo 相同的格式,返回账户在时隙 S 结束时的状态。

可以读取同一时隙中两笔交易之间的账户状态吗?

可以。将 transactionIndex 添加到查询中,即可获取该交易执行后的账户即时状态。请参阅读取交易前后的状态。

可以查询多久以前的数据?

历史数据从 2026 年 9 月 7 日的时隙 444,959,999 开始。更早的时隙会返回错误 -32018。

哪些套餐包含历史账户方法?

Business 及更高级别的套餐。测试期间调用消耗 0 积分;价格可能会发生变化。这些方法仅在主网上可用。

速率限制是多少?

getHistoricalAccountInfo 每秒允许 100 个请求,getHistoricalTokenAccountsByOwner 每秒允许 10 个请求。这两项限制都与你的套餐 RPC 速率限制相互独立。请参阅可用性和定价。

如何获取账户过去的每一种状态?

使用 lastUpdatedAt,从一次写入反向回溯到前一次写入。请参阅向前回溯账户历史记录。

是否需要新的 SDK?

不需要。这些是 JSON-RPC 方法,因此任何 HTTP 客户端都可以使用。标准 Solana 客户端库不包含这些方法,因此请像上面的示例一样直接发送请求。

后续步骤

getHistoricalAccountInfo

读取过去某个时隙或交易位置的单个账户

getHistoricalTokenAccountsByOwner

列出钱包在过去某个时隙的代币账户

getTransactionsForAddress

查找你要检查的交易对应的时隙和 transactionIndex

Historical data overview

交易历史记录、转账和其他归档方法