> ## 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.

# Cách lấy số dư token trong quá khứ của ví Solana

> Truy vấn số dư của bất kỳ token nào hoặc SOL gốc trong ví tại một dấu thời gian, thời điểm hoặc slot trong quá khứ. Lý tưởng để tính PnL, giá vốn, báo cáo thuế và tái dựng trạng thái ví.

<Note>
  Wallet API đang ở giai đoạn Beta. Các endpoint và định dạng phản hồi có thể thay đổi.
</Note>

## Tổng quan

Endpoint Historical Balance trả lời câu hỏi: **số dư của một token cụ thể (hoặc SOL gốc) trong ví này tại một thời điểm cụ thể trong quá khứ là bao nhiêu?** Trong khi endpoint [Balances](/docs/vi/wallet-api/balances) báo cáo lượng nắm giữ *hiện tại*, `balance-at` báo cáo lượng nắm giữ tại bất kỳ dấu thời gian, thời điểm hoặc slot nào.

Endpoint này tìm **giao dịch gần nhất xảy ra tại hoặc trước thời điểm được yêu cầu** có liên quan đến ví và token, sau đó đọc **số dư sau giao dịch** của ví từ giao dịch đó. Số dư sau giao dịch là số dư được duy trì từ giao dịch đó cho đến giao dịch tiếp theo, vì vậy "số dư tại thời điểm T" là số dư sau giao dịch của giao dịch liên quan cuối cùng có thời gian khối (hoặc slot) tại hoặc trước T. Với ví thông thường, đây là giá trị chính xác, không phải ước tính.

* **Token (SPL / Token-2022)**: được đọc từ số dư token sau giao dịch, cộng gộp trên các tài khoản token của ví cho mint đó.
* **SOL gốc**: được đọc từ số dư lamport sau giao dịch. Xác định SOL gốc bằng pseudo-mint `So11111111111111111111111111111111111111111`.

## Khi nào nên sử dụng

Sử dụng Historical Balance API cho các mục đích sau:

* **Tính PnL**: xác định lượng nắm giữ ở đầu và cuối một kỳ.
* **Giá vốn và lô thuế**: tái dựng số dư tại các sự kiện mua hoặc bán.
* **Giải quyết tranh chấp**: chứng minh lượng tài sản mà ví nắm giữ tại một thời điểm cụ thể.
* **Xác minh ảnh chụp nhanh**: kiểm tra số dư ví tại thời điểm chụp nhanh cho airdrop hoặc quản trị.
* **Kế toán và kiểm toán**: tái dựng trạng thái ví tại ranh giới các kỳ.

## Bắt đầu nhanh

### Số dư token tại một dấu thời gian

Lấy số dư USDC của ví tại một dấu thời gian Unix:

<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>

### Số dư token tại một thời điểm

Truyền một thời điểm ở định dạng dễ đọc thay vì dấu thời gian. Hãy nhớ mã hóa URL cho dấu cách thành `%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"
```

### Số dư SOL gốc tại một slot

Đối với SOL gốc, hãy sử dụng pseudo-mint `So11111111111111111111111111111111111111111`. Truy vấn dựa trên slot cho kết quả chính xác và xác định:

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

## Tham số truy vấn

| Tham số    | Bắt buộc | Kiểu   | Mô tả                                                                                               |
| ---------- | -------- | ------ | --------------------------------------------------------------------------------------------------- |
| `mint`     | Có       | string | Địa chỉ mint của token. Đối với SOL gốc, hãy sử dụng `So11111111111111111111111111111111111111111`. |
| `time`     | Chọn một | int    | Dấu thời gian Unix tính bằng **giây**. Số dư tại thời điểm này.                                     |
| `datetime` | Chọn một | string | Chuỗi thời điểm, ví dụ: `2025-01-10 19:20:00`. Mặc định là UTC.                                     |
| `slot`     | Chọn một | int    | Số slot. Số dư tại slot này. Chính xác và xác định.                                                 |

Phải cung cấp chính xác **một** trong các tham số `time`, `datetime` hoặc `slot`. Nếu không cung cấp tham số nào hoặc cung cấp nhiều hơn một tham số, API sẽ trả về lỗi `400`.

### Định dạng thời điểm

Các định dạng được chấp nhận:

* Chỉ ngày: `2025-01-10` → nửa đêm UTC
* Ngày + giờ: `2025-01-10 19:20:00` hoặc `2025-01-10T19:20:00` (không bắt buộc có giây) → UTC
* Có múi giờ cụ thể: `2025-01-10T19:20:00Z`, `2025-01-10T19:20:00+02:00`, `2025-01-10T19:20:00-05:00` → được áp dụng theo giá trị đã cung cấp

Các định dạng không hợp lệ hoặc không được hỗ trợ (`01/10/2025`, `2025-13-10`, `2025-02-30`) sẽ trả về lỗi `400`.

<Warning>
  Theo mặc định, thời điểm được diễn giải theo UTC. Một thời điểm không có múi giờ như `2025-01-10 19:20:00` được coi là UTC, không phải giờ địa phương của bạn. Hãy thêm độ lệch múi giờ cụ thể nếu muốn sử dụng múi giờ khác. Trường `requested.time` trong phản hồi hiển thị số giây epoch đã phân giải để bạn có thể xác minh cách diễn giải.
</Warning>

## Định dạng phản hồi

```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"
  }
}
```

### Ghi chú về các trường

* **`wallet`**: lặp lại địa chỉ ví đã truy vấn.
* **`mint`**: lặp lại mint đã truy vấn (pseudo-mint SOL khi truy vấn SOL gốc).
* **`isNative`**: `true` khi kết quả là SOL gốc.
* **`balance`**: số lượng dễ đọc ở dạng **chuỗi thập phân** — là chuỗi, không phải số, để số dư lớn không bị mất độ chính xác. Các số 0 ở cuối bị loại bỏ (`"1.5"`, không phải `"1.500000"`).
* **`balanceRaw`**: số lượng chính xác theo đơn vị nhỏ nhất (lamport đối với SOL), ở dạng chuỗi.
* **`decimals`**: số chữ số thập phân của token (9 đối với SOL).
* **`requested`**: lặp lại truy vấn. Khi sử dụng `datetime`, `time` cũng được điền bằng số giây epoch đã phân giải, giúp hiển thị rõ cách diễn giải UTC.
* **`asOf`**: giao dịch dùng để đọc số dư (`slot`, `blockTime`, `signature`).

`asOf: null` có nghĩa là số dư bằng 0, không phải lỗi. Khi ví không có giao dịch khớp nào tại hoặc trước thời điểm được yêu cầu, endpoint trả về `200` cùng với `balance: "0"` và `asOf: null` — đơn giản là ví chưa từng nắm giữ token đó vào thời điểm ấy.

## Trường hợp sử dụng

### Thay đổi số dư trong một kỳ

So sánh lượng nắm giữ tại hai thời điểm:

```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 };
};
```

### Kiểm tra điều kiện đủ tại thời điểm chụp nhanh

Xác minh một ví nắm giữ token tại slot chụp nhanh:

```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;
};
```

## Phương pháp hay nhất

* **Sử dụng `slot` để nhận kết quả xác định.** `time` và `datetime` được phân giải thông qua thời gian khối do trình xác thực báo cáo, có thể chênh lệch vài giây. Khi cần khả năng tái lập chính xác (ảnh chụp nhanh, kiểm toán), hãy truy vấn bằng `slot`.
* **Phân tích số dư dưới dạng chuỗi.** `balance` và `balanceRaw` là chuỗi để duy trì độ chính xác. Hãy sử dụng `BigInt(balanceRaw)` (hoặc số nguyên có độ chính xác tùy ý của ngôn ngữ bạn dùng) để tính toán — không ép kiểu thành số dấu phẩy động.
* **Coi `asOf: null` là số 0.** `asOf` có giá trị `null` là một phản hồi thành công, nghĩa là ví không có hoạt động nào với token đó tính đến thời điểm được yêu cầu. Không xử lý trường hợp này như một lỗi.
* **Lưu kết quả trong quá khứ vào bộ nhớ đệm.** Số dư tại một thời điểm trong quá khứ không bao giờ thay đổi. Hãy lưu kết quả vĩnh viễn vào bộ nhớ đệm để tránh gọi API nhiều lần.

## Lỗi thường gặp

| Mã lỗi | Mô tả                                                                                                                                  | Giải pháp                                                      |
| ------ | -------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
| 400    | Thiếu `mint`, mint không hợp lệ, không có hoặc có nhiều tham số trong số `time`/`datetime`/`slot`, hoặc không thể phân tích `datetime` | Cung cấp một mint hợp lệ và chính xác một tham số thời điểm    |
| 401    | Thiếu hoặc khóa API không hợp lệ                                                                                                       | Kiểm tra để đảm bảo khóa API đã được thêm vào yêu cầu          |
| 404    | Địa chỉ ví trong đường dẫn không hợp lệ                                                                                                | Xác minh địa chỉ là một địa chỉ Solana base58 hợp lệ           |
| 429    | Vượt quá giới hạn tốc độ                                                                                                               | Giảm tần suất yêu cầu hoặc nâng cấp gói                        |
| 502    | Lỗi RPC thượng nguồn hoặc hết thời gian chờ                                                                                            | Thử lại với chiến lược thời gian chờ tăng dần theo cấp số nhân |

## Hạn chế

* **Ví có nhiều tài khoản token có thể bị tính thiếu.** Số dư được đọc từ một giao dịch khớp gần nhất duy nhất. Trường hợp phổ biến — một tài khoản token liên kết cho mỗi mint — cho kết quả chính xác. Một ví nắm giữ cùng một mint trên nhiều tài khoản token có thể bị tính thiếu nếu giao dịch mới nhất chỉ tác động đến một số tài khoản trong đó.
* **Độ chính xác của SOL gốc đối với số dư rất lớn.** Đối với số dư SOL vượt quá khoảng 9.007.199 SOL (2⁵³ lamport), độ chính xác có thể bị mất ở thượng nguồn. Số lượng token không bị ảnh hưởng.
* **Độ chính xác của `time`/`datetime` phụ thuộc vào thời gian khối do trình xác thực báo cáo**, có thể chênh lệch vài giây. Hãy sử dụng `slot` để nhận kết quả chính xác và xác định.
* **Mỗi yêu cầu chỉ hỗ trợ một token.** Không có dạng xử lý hàng loạt nhiều mint hoặc "tất cả số dư tại thời điểm T".

## Bước tiếp theo

<CardGroup cols={3}>
  <Card title="Wallet Balances" icon="scale-balanced" href="/docs/vi/wallet-api/balances">
    Lấy lượng token và NFT hiện tại mà ví đang nắm giữ cùng giá trị USD.
  </Card>

  <Card title="Wallet API Overview" icon="wallet" href="/docs/vi/wallet-api/overview">
    Tất cả endpoint của Wallet API và các quy ước chung.
  </Card>

  <Card title="API Reference" icon="code" href="/docs/vi/api-reference/wallet-api/balance-at">
    Lược đồ yêu cầu và phản hồi cho số dư trong quá khứ.
  </Card>
</CardGroup>
