> ## 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钱包的历史代币余额

> 查询在过去某个时间戳、日期时间或槽位，某钱包的某代币或原生SOL余额。适用于损益、成本基础、税务报告和重建钱包状态。

<Note>
  钱包API处于测试阶段。端点和响应格式可能会发生变化。
</Note>

## 概览

历史余额端点回答：**在过去某个特定时间点，此钱包的特定代币（或原生SOL）的余额是多少？** 虽然[余额](/zh/wallet-api/balances)端点报告*当前*持有情况，`balance-at`报告在任何时间戳、日期时间或槽位的持有情况。

它会找到**在指定时间点或之前涉及钱包和代币的单个最近交易**，然后读取该交易的**交易后余额**。交易的交易后余额是从该交易到下一个交易所持有的余额，因此“时间T的余额”是最后一个相关交易在T时间点（或槽位）或之前的区块时间的交易后余额。对于典型钱包而言，这是一个准确的值，而不是估计。

* **代币（SPL / Token-2022）**：从交易的代币余额后读取，汇总钱包的代币账户中的此铸造代币。
* **原生SOL**：从交易的lamport后余额中读取。用伪铸币`So11111111111111111111111111111111111111111`表示原生SOL。

## 使用时机

使用历史余额API的情境：

* **损益计算**：确定某时期的起始和结束持有情况。
* **成本基础和税务批次**：重建收购或处置事件时的余额。
* **争议解决**：证明某钱包在特定时刻持有的内容。
* **快照验证**：检查钱包在空投或治理快照时的余额。
* **会计和审计**：在时期边界重建钱包状态。

## 快速开始

### 查询特定时间的代币余额

获取钱包在某个Unix时间戳的USDC余额：

<Tabs>
  <Tab title="JavaScript">
    ```javascript theme={"system"}
    const getBalanceAt = async (wallet, mint, time) => {
      const url = `https://api.helius.xyz/v1/wallet/${wallet}/balance-at?mint=${mint}&time=${time}&api-key=YOUR_API_KEY`;

      const response = await fetch(url);

      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }

      const result = await response.json();

      if (result.asOf === null) {
        console.log('Wallet had no activity for this token by that time — balance is 0');
        return result;
      }

      console.log(`Balance: ${result.balance}`);
      console.log(`Raw amount: ${result.balanceRaw} (${result.decimals} decimals)`);
      console.log(`As of slot ${result.asOf.slot}, signature ${result.asOf.signature}`);

      return result;
    };

    // USDC balance on 2025-01-10 19:20:00 UTC
    getBalanceAt(
      "5tzFkiKscXHK5ZXCGbXZxdw7gTjjD1mBwuoFbhUvuAi9",
      "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
      1736536800
    );
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={"system"}
    import requests

    def get_balance_at(wallet: str, mint: str, time: int):
        url = f"https://api.helius.xyz/v1/wallet/{wallet}/balance-at"
        headers = {"X-Api-Key": "YOUR_API_KEY"}
        params = {"mint": mint, "time": time}

        response = requests.get(url, headers=headers, params=params)
        response.raise_for_status()
        result = response.json()

        if result["asOf"] is None:
            print("Wallet had no activity for this token by that time — balance is 0")
            return result

        print(f"Balance: {result['balance']}")
        print(f"Raw amount: {result['balanceRaw']} ({result['decimals']} decimals)")
        print(f"As of slot {result['asOf']['slot']}, signature {result['asOf']['signature']}")

        return result

    # USDC balance on 2025-01-10 19:20:00 UTC
    get_balance_at(
        "5tzFkiKscXHK5ZXCGbXZxdw7gTjjD1mBwuoFbhUvuAi9",
        "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
        1736536800
    )
    ```
  </Tab>

  <Tab title="cURL">
    ```bash theme={"system"}
    curl "https://api.helius.xyz/v1/wallet/5tzFkiKscXHK5ZXCGbXZxdw7gTjjD1mBwuoFbhUvuAi9/balance-at?mint=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v&time=1736536800&api-key=YOUR_API_KEY"
    ```
  </Tab>
</Tabs>

### 查询特定日期时间的代币余额

传递可读日期时间而不是时间戳。记得将空格URL编码为 `%20`：

```bash theme={"system"}
curl "https://api.helius.xyz/v1/wallet/5tzFkiKscXHK5ZXCGbXZxdw7gTjjD1mBwuoFbhUvuAi9/balance-at?mint=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v&datetime=2025-01-10%2019:20:00&api-key=YOUR_API_KEY"
```

### 特定插槽的原生SOL余额

对于原生SOL，使用伪造的`So11111111111111111111111111111111111111111`。基于插槽的查询是精确且确定性的：

```bash theme={"system"}
curl "https://api.helius.xyz/v1/wallet/5tzFkiKscXHK5ZXCGbXZxdw7gTjjD1mBwuoFbhUvuAi9/balance-at?mint=So11111111111111111111111111111111111111111&slot=313000000&api-key=YOUR_API_KEY"
```

## 查询参数

| 参数         | 必需 | 类型     | 描述                                                              |
| ---------- | -- | ------ | --------------------------------------------------------------- |
| `mint`     | 是  | string | 代币铸币地址。对于原生SOL，使用`So11111111111111111111111111111111111111111`。 |
| `time`     | 单选 | int    | Unix时间戳（以秒为单位）。此时间的余额。                                          |
| `datetime` | 单选 | string | 日期时间字符串，例如`2025-01-10 19:20:00`。默认UTC。                          |
| `slot`     | 单选 | int    | 插槽编号。此插槽的余额。精确且确定性。                                             |

`time`, `datetime`, 或 `slot` 中必须提供一个。提供零个或多个则返回`400`错误。

### 日期时间格式

接受的格式：

* 只有日期： `2025-01-10` → UTC午夜
* 日期+时间： `2025-01-10 19:20:00` 或 `2025-01-10T19:20:00` （秒可选）→ UTC
* 带明确时区：`2025-01-10T19:20:00Z`, `2025-01-10T19:20:00+02:00`, `2025-01-10T19:20:00-05:00` → 按给定时区处理

无效或不支持的格式（`01/10/2025`, `2025-13-10`, `2025-02-30`）返回`400`错误。

<Warning>
  日期时间默认解释为UTC。像`2025-01-10 19:20:00`这样的裸日期时间被视为UTC，而不是您的本地时间。如果您表示其他时区，请包括明确的时区偏移量。响应中的`requested.time`字段显示解析的纪元秒，以便您验证解释。
</Warning>

## 响应格式

```json theme={"system"}
{
  "wallet": "5tzFkiKscXHK5ZXCGbXZxdw7gTjjD1mBwuoFbhUvuAi9",
  "mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
  "isNative": false,
  "balance": "284961463.392936",
  "balanceRaw": "284961463392936",
  "decimals": 6,
  "requested": {
    "time": 1736536800,
    "slot": null,
    "datetime": null
  },
  "asOf": {
    "slot": 313000000,
    "blockTime": 1736536794,
    "signature": "5Cyy7Mh9nVgFq3T8wJp2sKxR4dE6bA1uZoNcLrXmYqUpon"
  }
}
```

### 字段说明

* **`wallet`**: 查询的钱包地址的回显。
* **`mint`**: 查询的铸币（当为本地时为 SOL 伪铸币）的回显。
* **`isNative`**: 当结果是本地 SOL 时为 `true`。
* **`balance`**: 以**十进制字符串**形式表示的人类可读金额——字符串而不是数字，因此大额余额不会失去精度。尾随的零被去除（`"1.5"`，而不是 `"1.500000"`）。
* **`balanceRaw`**: 以最小单位（SOL 的 lamports）表示的确切金额，作为字符串。
* **`decimals`**: 代币小数位（SOL 为 9）。
* **`requested`**: 查询的回显。当使用 `datetime` 时，`time` 也会填充为已解析的纪元秒数，使得 UTC 解释可以看到。
* **`asOf`**: 读取余额的交易（`slot`，`blockTime`，`signature`）。

`asOf: null` 表示为零，而不是错误。当钱包在请求时间点或之前没有匹配的交易时，端点返回 `200`，带有 `balance: "0"` 和 `asOf: null`——钱包当时没有持有该代币。

## 用例

### 一段时间内的余额变化

比较两个时间点的持有量：

```javascript theme={"system"}
const getBalanceChange = async (wallet, mint, startTime, endTime) => {
  const fetchBalance = (time) =>
    fetch(
      `https://api.helius.xyz/v1/wallet/${wallet}/balance-at?mint=${mint}&time=${time}&api-key=YOUR_API_KEY`
    ).then(r => r.json());

  const [start, end] = await Promise.all([
    fetchBalance(startTime),
    fetchBalance(endTime)
  ]);

  // balanceRaw is an exact integer string — use BigInt for precise arithmetic
  const delta = BigInt(end.balanceRaw) - BigInt(start.balanceRaw);
  const human = Number(delta) / 10 ** end.decimals;

  console.log(`Start: ${start.balance}`);
  console.log(`End: ${end.balance}`);
  console.log(`Change: ${human > 0 ? '+' : ''}${human}`);

  return { start, end, delta };
};
```

### 快照资格检查

验证钱包在快照槽中持有代币：

```javascript theme={"system"}
const heldAtSnapshot = async (wallet, mint, snapshotSlot, minimumRaw) => {
  const result = await fetch(
    `https://api.helius.xyz/v1/wallet/${wallet}/balance-at?mint=${mint}&slot=${snapshotSlot}&api-key=YOUR_API_KEY`
  ).then(r => r.json());

  const eligible = BigInt(result.balanceRaw) >= BigInt(minimumRaw);
  console.log(`${wallet}: ${result.balance} at slot ${snapshotSlot} — ${eligible ? 'eligible' : 'not eligible'}`);

  return eligible;
};
```

## 最佳实践

* **使用 `slot` 获取确定性结果。** `time` 和 `datetime` 通过验证器报告的区块时间解析，可能会漂移几秒。当精确的可重复性很重要时（快照、审计），通过 `slot` 查询。
* **将余额解析为字符串。** `balance` 和 `balanceRaw` 是字符串以保持精度。使用 `BigInt(balanceRaw)`（或您的语言的任意精度整数）进行算术运算——不要强制转换为浮点数。
* **将 `asOf: null` 视为零。** `null` `asOf` 是一个成功的响应，表示钱包在请求点之前对该代币没有活动。不要将其处理为错误。
* **缓存历史结果。** 在过去某一点的余额从不改变。永久缓存结果以避免重复的 API 调用。

## 常见错误

| 错误代码 | 描述                                                                 | 解决方案                    |
| ---- | ------------------------------------------------------------------ | ----------------------- |
| 400  | 缺少`mint`，无效的mint，或者`time`/`datetime`/`slot`的零或多个值，或无法解析的`datetime` | 提供一个有效的mint和仅一个时间点参数    |
| 401  | 缺少或无效的API密钥                                                        | 确保在请求中包含您的API密钥         |
| 404  | 路径中无效的钱包地址                                                         | 确认地址是有效的base58 Solana地址 |
| 429  | 超出速率限制                                                             | 减少请求频率或升级您的计划           |
| 502  | 上游RPC错误或超时                                                         | 使用指数回退重试                |

## 限制

* **多代币账户钱包可能会少计。** 余额从最近的匹配交易中读取。常见情况是每个mint一个关联代币账户是准确的。一个钱包持有多个代币账户的相同mint，其中最新交易只涉及其中一些账户，可能会少计。
* **非常大余额的本机SOL精度。** 对于超过约9,007,199 SOL (2⁵³ lamports)的SOL余额，精度可能在上游丢失。代币金额不受影响。
* **`time`/`datetime`精度取决于验证器报告的区块时间**，可能会有几秒钟的漂移。使用`slot`获得精确、确定的结果。
* **每次请求单个代币。** 没有多mint或“在时间T的所有余额”批量形式。

## 下一步

<CardGroup cols={3}>
  <Card title="钱包余额" icon="scale-balanced" href="/zh/wallet-api/balances">
    获取钱包当前的代币和NFT持有量及其美元价值。
  </Card>

  <Card title="钱包API概览" icon="wallet" href="/zh/wallet-api/overview">
    所有钱包API端点和共享约定。
  </Card>

  <Card title="API参考" icon="code" href="/zh/api-reference/wallet-api/balance-at">
    历史余额的请求和响应模式。
  </Card>
</CardGroup>
