> ## Documentation Index
> Fetch the complete documentation index at: https://www.helius.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Solana 历史账户 API（测试版）

> 使用 Helius getHistoricalAccountInfo 和 getHistoricalTokenAccountsByOwner（测试版），读取 Solana 账户在过去任意时隙或交易时的状态。

> [!NOTE]
>
> 历史账户状态功能目前处于测试阶段。参数、定价、响应格式和数据覆盖范围可能会发生变化。

## 什么是历史账户状态？

标准 Solana RPC 方法（如 `getAccountInfo` 和 `getTokenAccountsByOwner`）仅返回账户的当前状态。验证者只保留每个账户的最新版本，因此每次写入都会替换上一次写入。交易历史记录也无法弥补这一缺失：`getTransaction` 和 `getBlock` 返回指令、日志和余额变化，而不是程序写入的账户数据。

Helius 的历史账户方法会返回账户在过去某个时隙时的状态，或者该时隙内某笔特定交易执行后的即时状态。由于你可以读取同一时隙内两笔交易之间的状态，因此即使一个账户在单个时隙中被多次写入，也能准确查看某笔交易所做的更改。你可以使用这些方法：

* 检查钱包在特定时间点的代币余额，用于审计、会计或税务报告
* 通过读取交易执行前后所涉及的账户来调试交易
* 读取协议在过去某个时隙的账户，例如资金池、金库或预言机，而无需自行重放账本

这些是 Helius 独有的 RPC 方法，不属于标准 Solana RPC。

## 我应该使用哪种方法？

| 你的需求 | 使用此方法 | 返回内容 |
| - | - | - |
| 过去某个位置的单个账户 | [`getHistoricalAccountInfo`](https://www.helius.dev/docs/zh/api-reference/rpc/http/gethistoricalaccountinfo) | 该账户；如果当时不存在，则返回 `null` |
| 过去某个位置的钱包代币账户 | [`getHistoricalTokenAccountsByOwner`](https://www.helius.dev/docs/zh/api-reference/rpc/http/gethistoricaltokenaccountsbyowner) | 一页 SPL Token 和 Token-2022 账户，可选择仅返回某个铸币账户的结果 |

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

## 可用性和定价

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

## 数据覆盖范围

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

## 选择历史位置

第二个参数是一个查询对象，包含两个可选字段：`slot` 和 `transactionIndex`：

| 查询 | 返回的状态 |
| - | - |
| 省略或 `{}` | 最新已索引时隙的状态，但不超过所请求的承诺级别 |
| `{ "slot": 445000000 }` | 时隙 445,000,000 结束时的状态，包含所有交易和任何非交易写入（如纪元奖励）执行后的结果 |
| `{ "slot": 445000000, "transactionIndex": 10 }` | 时隙 445,000,000 中索引为 10 的交易执行后的即时状态 |

使用 `transactionIndex` 时必须提供 `slot`。

纪元奖励等非交易写入会在时隙结束时应用。包含 `transactionIndex` 的查询永远不会包含这些写入，即使查询的是该时隙中的最后一笔交易。要包含这些写入，请查询 `slot`，但不要提供 `transactionIndex`。

### 读取交易前后的状态

要检查一笔交易更改了什么，请读取该账户两次：

* **交易后**：使用该交易自身的 `slot` 和 `transactionIndex` 进行查询。
* **交易前**：查询相同的 `slot`，并将 `transactionIndex` 减 1。如果该交易是其所在时隙中的第一笔交易（索引为 0），请查询前一个时隙且不提供 `transactionIndex`。

[`getTransactionsForAddress`](https://www.helius.dev/docs/zh/rpc/gettransactionsforaddress) 会返回每笔交易的 `slot` 和 `transactionIndex`，因此你可以在其中找到交易的位置，并将其直接传递给这些方法。

```bash theme={"system"}
# State of the account right after the transaction at index 10 in slot 445000000
curl https://mainnet.helius-rpc.com/?api-key=YOUR_API_KEY \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": "1",
    "method": "getHistoricalAccountInfo",
    "params": [
      "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
      { "slot": 445000000, "transactionIndex": 10 },
      { "encoding": "jsonParsed" }
    ]
  }'
```

## 了解 lastUpdatedAt

每个返回的账户都包含 `lastUpdatedAt`，它表示生成所接收状态的写入位置：

```json theme={"system"}
"lastUpdatedAt": { "slot": 444999837, "transactionIndex": 700 }
```

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

## null 值的含义

当账户在你查询的位置不存在时，`value` 为 `null`。该账户可能尚未创建，也可能已经关闭。

* **返回 `null` 的响应不包含 `lastUpdatedAt`**，因此无法说明账户何时创建或关闭。要查找这些交易，请使用 [`getTransactionsForAddress`](https://www.helius.dev/docs/zh/rpc/gettransactionsforaddress)。
* **从未存在过的地址**也会返回 `null`，而不是错误。
* **历史数据覆盖范围之前的时隙**会对每个地址返回错误 `-32018`，无论账户是否曾经存在。

## 向前回溯账户历史记录

使用 `lastUpdatedAt` 作为游标，按从新到旧的顺序列出账户曾有过的所有状态。每次读取后，查询所接收写入之前的位置：

| 你收到的 `lastUpdatedAt` | 下一次查询 |
| - | - |
| `{ "slot": S, "transactionIndex": N }`，其中 N 大于 0 | `{ "slot": S, "transactionIndex": N - 1 }` |
| `{ "slot": S, "transactionIndex": 0 }` | `{ "slot": S - 1 }` |
| `{ "slot": S, "transactionIndex": null }` | `{ "slot": S - 1 }` |

当 `value` 为 `null` 时停止，这表示你刚刚越过的写入创建了该账户。当 `lastUpdatedAt.slot` 到达历史数据覆盖起点 444,959,999 时，也应停止。

```typescript theme={"system"}
const url = `https://mainnet.helius-rpc.com/?api-key=${process.env.HELIUS_API_KEY}`;
const COVERAGE_START_SLOT = 444959999;

type Position = { slot?: number; transactionIndex?: number };

async function getAccountHistory(address: string, start: Position = {}, maxStates = 100) {
  const states = [];
  let position = start;

  while (states.length < maxStates) {
    const response = await fetch(url, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        jsonrpc: "2.0",
        id: "1",
        method: "getHistoricalAccountInfo",
        params: [address, position, { encoding: "base64" }],
      }),
    });
    const { result, error } = await response.json();
    if (error) throw new Error(`${error.code}: ${error.message}`);

    const account = result.value;
    if (!account) break; // The account didn't exist before this write

    states.push(account);
    const { slot, transactionIndex } = account.lastUpdatedAt;
    if (slot <= COVERAGE_START_SLOT) break; // Reached the start of history coverage

    // Step to the position just before this write
    position = transactionIndex ? { slot, transactionIndex: transactionIndex - 1 } : { slot: slot - 1 };
  }

  return states; // Newest first
}
```

* **当前已关闭的账户**在最新时隙会返回 `null`。请传入该账户仍然存在时的 `start` 位置。
* **非交易写入**会回退到前一个时隙。如果同一账户在该时隙更早时也曾由交易写入，则回溯过程会跳过该状态。
* **要按时间向前遍历**，请使用 [`getTransactionsForAddress`](https://www.helius.dev/docs/zh/rpc/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`。

## 错误

| 代码 | 含义 | 处理方式 |
| - | - | - |
| `-32018` | 该时隙早于历史数据覆盖起点 | 查询时隙 444,959,999 或更晚的时隙 |
| `-32004` | 该时隙比所请求承诺级别允许的时隙更新 | 使用 `confirmed`，或查询更早的时隙 |
| `-32602` | 参数无效 | 查看错误消息，确定具体字段 |
| `-32601` | 找不到方法 | 调用你的 Helius 主网或 Gatekeeper 端点。历史账户方法在开发网上不可用 |

## 常见问题

### 如何获取 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 客户端库不包含这些方法，因此请像上面的示例一样直接发送请求。

## 后续步骤

> [card] [getHistoricalAccountInfo](https://www.helius.dev/docs/zh/api-reference/rpc/http/gethistoricalaccountinfo.md)
>
> 读取过去某个时隙或交易位置的单个账户

> [card] [getHistoricalTokenAccountsByOwner](https://www.helius.dev/docs/zh/api-reference/rpc/http/gethistoricaltokenaccountsbyowner.md)
>
> 列出钱包在过去某个时隙的代币账户

> [card] [getTransactionsForAddress](https://www.helius.dev/docs/zh/rpc/gettransactionsforaddress.md)
>
> 查找你要检查的交易对应的时隙和 transactionIndex

> [card] [Historical data overview](https://www.helius.dev/docs/zh/rpc/historical-data.md)
>
> 交易历史记录、转账和其他归档方法


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.