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

# API tài khoản lịch sử Solana (Beta)

> Đọc trạng thái tài khoản Solana tại bất kỳ slot hoặc giao dịch nào trong quá khứ bằng getHistoricalAccountInfo và getHistoricalTokenAccountsByOwner (beta) của Helius.

> [!NOTE]
>
> Trạng thái tài khoản lịch sử đang ở giai đoạn Beta. Các tham số, giá, định dạng phản hồi và phạm vi dữ liệu có thể thay đổi.

## Trạng thái tài khoản lịch sử là gì?

Các phương thức RPC Solana tiêu chuẩn như `getAccountInfo` và `getTokenAccountsByOwner` chỉ trả về trạng thái hiện tại của tài khoản. Trình xác thực chỉ lưu phiên bản mới nhất của mỗi tài khoản, vì vậy mỗi lần ghi sẽ thay thế lần ghi trước đó. Lịch sử giao dịch không bổ sung phần còn thiếu này: `getTransaction` và `getBlock` trả về chỉ thị, nhật ký và thay đổi số dư, chứ không phải dữ liệu tài khoản mà chương trình đã ghi.

Các phương thức tài khoản lịch sử của Helius trả về tài khoản ở trạng thái tại một slot trong quá khứ hoặc ngay sau một giao dịch cụ thể trong slot đó. Vì có thể đọc trạng thái giữa hai giao dịch trong cùng một slot, bạn có thể biết chính xác một giao dịch đã thay đổi những gì, ngay cả khi tài khoản được ghi nhiều lần trong một slot. Sử dụng các phương thức này để:

* Kiểm tra số dư token của ví tại một thời điểm cụ thể để kiểm toán, kế toán hoặc báo cáo thuế
* Gỡ lỗi giao dịch bằng cách đọc các tài khoản mà giao dịch đã tác động ngay trước và ngay sau khi thực thi
* Đọc các tài khoản của giao thức, chẳng hạn như pool, vault hoặc oracle, tại một slot trong quá khứ mà không cần tự phát lại sổ cái

Đây là các phương thức RPC độc quyền của Helius. Chúng không thuộc RPC Solana tiêu chuẩn.

## Nên sử dụng phương thức nào?

| Nhu cầu | Sử dụng | Trả về |
| - | - | - |
| Một tài khoản tại một vị trí trong quá khứ | [`getHistoricalAccountInfo`](https://www.helius.dev/docs/vi/api-reference/rpc/http/gethistoricalaccountinfo) | Tài khoản hoặc `null` nếu tài khoản không tồn tại |
| Các tài khoản token của ví tại một vị trí trong quá khứ | [`getHistoricalTokenAccountsByOwner`](https://www.helius.dev/docs/vi/api-reference/rpc/http/gethistoricaltokenaccountsbyowner) | Một trang gồm các tài khoản SPL Token và Token-2022, có thể tùy chọn giới hạn theo một mint |

Cả hai đều nhận cùng ba tham số: một địa chỉ, một đối tượng truy vấn để chọn vị trí trong lịch sử và một đối tượng cấu hình.

## Phạm vi cung cấp và giá

* **Gói**: Gói [Business](https://www.helius.dev/docs/vi/billing/plans.md) trở lên.
* **Chi phí**: 0 tín dụng cho mỗi lệnh gọi trong giai đoạn beta (giá có thể thay đổi).
* **Giới hạn tốc độ**: `getHistoricalAccountInfo` cho phép 100 yêu cầu mỗi giây, với mức tăng đột biến tối đa là 100. `getHistoricalTokenAccountsByOwner` cho phép 10 yêu cầu mỗi giây, với mức tăng đột biến tối đa là 10. Các giới hạn này tách biệt với giới hạn tốc độ RPC của gói và những yêu cầu vượt quá giới hạn sẽ trả về HTTP `429`. Xem [giới hạn tốc độ](https://www.helius.dev/docs/vi/billing/rate-limits.md).
* **Mạng**: Chỉ Mainnet. Gọi các phương thức này trên endpoint mainnet tiêu chuẩn, `https://mainnet.helius-rpc.com`, hoặc endpoint [Gatekeeper](https://www.helius.dev/docs/vi/gatekeeper/overview.md), `https://beta.helius-rpc.com`. Devnet trả về lỗi `-32601`.

## Phạm vi dữ liệu

Lịch sử có sẵn từ slot 444,959,999 (ngày 7 tháng 9 năm 2026) trở đi. Đây là slot cuối cùng của epoch 1029, vì vậy nó chứa trạng thái tại thời điểm bắt đầu epoch 1030. Các yêu cầu đối với slot trước đó trả về lỗi `-32018` ("Không có lịch sử tài khoản").

## Chọn một vị trí trong lịch sử

Tham số thứ hai là một đối tượng truy vấn có hai trường tùy chọn, `slot` và `transactionIndex`:

| Truy vấn | Trả về trạng thái |
| - | - |
| Bỏ qua hoặc `{}` | Tại slot được lập chỉ mục gần nhất, giới hạn theo mức cam kết được yêu cầu |
| `{ "slot": 445000000 }` | Ở cuối slot 445,000,000, sau mọi giao dịch và mọi lần ghi ngoài giao dịch như phần thưởng epoch |
| `{ "slot": 445000000, "transactionIndex": 10 }` | Ngay sau khi giao dịch có chỉ mục 10 trong slot 445,000,000 được thực thi |

`transactionIndex` yêu cầu `slot`.

Các lần ghi ngoài giao dịch, chẳng hạn như phần thưởng epoch, được áp dụng ở cuối slot. Truy vấn có `transactionIndex` không bao giờ bao gồm các lần ghi này, ngay cả đối với giao dịch cuối cùng trong slot. Để bao gồm chúng, hãy truy vấn `slot` mà không có `transactionIndex`.

### Đọc trạng thái trước và sau một giao dịch

Để kiểm tra những gì một giao dịch đã thay đổi, hãy đọc tài khoản hai lần:

* **Sau giao dịch**: truy vấn `slot` và `transactionIndex` của chính giao dịch đó.
* **Trước giao dịch**: truy vấn cùng `slot` với `transactionIndex` trừ 1. Nếu đây là giao dịch đầu tiên trong slot (chỉ mục 0), hãy truy vấn slot trước đó mà không có `transactionIndex`.

[`getTransactionsForAddress`](https://www.helius.dev/docs/vi/rpc/gettransactionsforaddress) trả về `slot` và `transactionIndex` cho mọi giao dịch, vì vậy bạn có thể tìm vị trí của giao dịch tại đó rồi truyền trực tiếp vào các phương thức này.

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

## Tìm hiểu lastUpdatedAt

Mỗi tài khoản được trả về đều bao gồm `lastUpdatedAt`, tức vị trí của lần ghi đã tạo ra trạng thái mà bạn nhận được:

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

* **`slot` và `transactionIndex`** trỏ đến lần ghi cuối cùng tại hoặc trước vị trí được truy vấn. Trong ví dụ trên, truy vấn cho slot 445,000,000 trả về trạng thái được ghi lần cuối bởi giao dịch có chỉ mục 700 trong slot 444,999,837.
* **`transactionIndex: null`** có nghĩa là lần ghi xảy ra bên ngoài giao dịch, chẳng hạn như phần thưởng epoch.
* **`slot: 444959999` với `transactionIndex: null`** có nghĩa là tài khoản chưa thay đổi kể từ khi phạm vi lịch sử bắt đầu. Điều này không có nghĩa là tài khoản đã được ghi trong slot đó.

## Ý nghĩa của giá trị null

`value` là `null` khi tài khoản không tồn tại tại vị trí được truy vấn. Tài khoản có thể chưa được tạo hoặc đã bị đóng.

* **Phản hồi `null` không có `lastUpdatedAt`**, vì vậy phản hồi không cho biết tài khoản được tạo hoặc đóng khi nào. Để tìm các giao dịch đó, hãy sử dụng [`getTransactionsForAddress`](https://www.helius.dev/docs/vi/rpc/gettransactionsforaddress).
* **Một địa chỉ chưa từng tồn tại** cũng trả về `null` chứ không phải lỗi.
* **Một slot trước phạm vi lịch sử** trả về lỗi `-32018` cho mọi địa chỉ, bất kể tài khoản có tồn tại hay không.

## Duyệt ngược lịch sử tài khoản

Sử dụng `lastUpdatedAt` làm con trỏ để liệt kê mọi trạng thái mà tài khoản từng có, từ mới nhất đến cũ nhất. Sau mỗi lần đọc, hãy truy vấn vị trí ngay trước lần ghi vừa nhận được:

| `lastUpdatedAt` đã nhận | Truy vấn tiếp theo |
| - | - |
| `{ "slot": S, "transactionIndex": N }`, trong đó N lớn hơn 0 | `{ "slot": S, "transactionIndex": N - 1 }` |
| `{ "slot": S, "transactionIndex": 0 }` | `{ "slot": S - 1 }` |
| `{ "slot": S, "transactionIndex": null }` | `{ "slot": S - 1 }` |

Dừng khi `value` là `null`, nghĩa là lần ghi vừa duyệt qua đã tạo tài khoản. Đồng thời dừng khi `lastUpdatedAt.slot` đạt 444,959,999, điểm bắt đầu của phạm vi lịch sử.

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

* **Các tài khoản hiện đã đóng** trả về `null` tại slot mới nhất. Hãy truyền một vị trí `start` từ thời điểm tài khoản vẫn còn tồn tại.
* **Các lần ghi ngoài giao dịch** lùi về slot trước đó. Nếu cùng tài khoản cũng được ghi bởi một giao dịch trước đó trong slot đó, quá trình duyệt sẽ bỏ qua trạng thái này.
* **Để duyệt xuôi theo thời gian**, hãy liệt kê các giao dịch của tài khoản từ cũ nhất đến mới nhất bằng [`getTransactionsForAddress`](https://www.helius.dev/docs/vi/rpc/gettransactionsforaddress) và `sortOrder: "asc"`. Sau đó, đọc trạng thái tại `slot` và `transactionIndex` của từng giao dịch.

## Ngữ cảnh phản hồi

`context.slot` trong mỗi phản hồi là slot mới nhất mà Helius đã lập chỉ mục khi xử lý yêu cầu. Đây không phải slot được truy vấn. Hãy sử dụng `slot` trong yêu cầu hoặc `lastUpdatedAt` để phân tích dữ liệu nhận được.

## Phân trang nhất quán

`getHistoricalTokenAccountsByOwner` trả về từng trang kết quả. Mỗi phản hồi bao gồm `value.accounts` và `value.paginationKey`.

* **Truyền lại `paginationKey`** trong đối tượng cấu hình để tải trang tiếp theo.
* **Chỉ dừng khi `paginationKey` là `null`.** Một trang có thể có ít kết quả hoặc trống nhưng vẫn còn kết quả ở phía sau.
* **Cố định `slot` trên mọi trang.** Nếu bỏ qua `slot`, mỗi trang sẽ đọc slot được lập chỉ mục mới nhất và các trang có thể đến từ những thời điểm khác nhau.
* **Đặt `limit`** trong khoảng từ 1 đến 5.000. Giá trị mặc định là 5.000.

## Kiểu mã hóa và dữ liệu đã phân tích

* **Các kiểu mã hóa được hỗ trợ** là `base64` (mặc định), `base64+zstd` và `jsonParsed`. `base58` và `binary` không được hỗ trợ.
* **Các tài khoản token `jsonParsed`** sử dụng số chữ số thập phân của mint tại vị trí được truy vấn, vì vậy số lượng vẫn chính xác ngay cả khi mint đã thay đổi sau đó. Nếu không thể đọc mint tại vị trí đó, tài khoản sẽ dùng base64 thay thế.
* **`dataSlice`** hoạt động giống như trong `getAccountInfo`.

## Mức cam kết

`commitment` chấp nhận `finalized` (mặc định) và `confirmed`. `processed` không được hỗ trợ. Một `slot` mới hơn mức mà cam kết được yêu cầu cho phép sẽ trả về lỗi `-32004`.

## Lỗi

| Mã | Ý nghĩa | Cách xử lý |
| - | - | - |
| `-32018` | Slot nằm trước điểm bắt đầu của phạm vi lịch sử | Truy vấn slot 444,959,999 hoặc mới hơn |
| `-32004` | Slot mới hơn mức mà cam kết được yêu cầu cho phép | Sử dụng `confirmed` hoặc truy vấn một slot cũ hơn |
| `-32602` | Tham số không hợp lệ | Kiểm tra thông báo lỗi để xác định trường cụ thể |
| `-32601` | Không tìm thấy phương thức | Gọi endpoint mainnet Helius hoặc Gatekeeper. Các phương thức tài khoản lịch sử không có trên devnet |

## Câu hỏi thường gặp

### Làm cách nào để lấy trạng thái của tài khoản Solana tại một slot cụ thể?

Gọi `getHistoricalAccountInfo` với địa chỉ và `{ "slot": S }`. Phương thức trả về tài khoản ở trạng thái tại cuối slot S, với cùng định dạng như `getAccountInfo`.

### Có thể đọc tài khoản giữa hai giao dịch trong cùng một slot không?

Có. Thêm `transactionIndex` vào truy vấn để lấy trạng thái tài khoản ngay sau khi giao dịch đó được thực thi. Xem [Đọc trạng thái trước và sau một giao dịch](#đọc-trạng-thái-trước-và-sau-một-giao-dịch).

### Có thể truy vấn ngược về quá khứ bao xa?

Lịch sử bắt đầu từ slot 444,959,999, vào ngày 7 tháng 9 năm 2026. Các slot trước đó trả về lỗi `-32018`.

### Những gói nào bao gồm các phương thức tài khoản lịch sử?

Gói Business trở lên. Các lệnh gọi có chi phí 0 tín dụng trong giai đoạn beta; giá có thể thay đổi. Các phương thức này chỉ có trên mainnet.

### Giới hạn tốc độ là bao nhiêu?

`getHistoricalAccountInfo` cho phép 100 yêu cầu mỗi giây và `getHistoricalTokenAccountsByOwner` cho phép 10 yêu cầu mỗi giây. Cả hai giới hạn đều tách biệt với giới hạn tốc độ RPC của gói. Xem [Phạm vi cung cấp và giá](#phạm-vi-cung-cấp-và-giá).

### Làm cách nào để lấy mọi trạng thái trước đây của tài khoản?

Lùi từ một lần ghi về lần ghi trước đó bằng `lastUpdatedAt`. Xem [Duyệt ngược lịch sử tài khoản](#duyệt-ngược-lịch-sử-tài-khoản).

### Tôi có cần SDK mới không?

Không. Đây là các phương thức JSON-RPC, vì vậy mọi máy khách HTTP đều có thể sử dụng. Các thư viện máy khách Solana tiêu chuẩn không bao gồm những phương thức này, vì vậy hãy gửi yêu cầu trực tiếp như trong các ví dụ trên.

## Bước tiếp theo

> [card] [getHistoricalAccountInfo](https://www.helius.dev/docs/vi/api-reference/rpc/http/gethistoricalaccountinfo.md)
>
> Đọc một tài khoản tại một slot hoặc giao dịch trong quá khứ

> [card] [getHistoricalTokenAccountsByOwner](https://www.helius.dev/docs/vi/api-reference/rpc/http/gethistoricaltokenaccountsbyowner.md)
>
> Liệt kê các tài khoản token của ví tại một slot trong quá khứ

> [card] [getTransactionsForAddress](https://www.helius.dev/docs/vi/rpc/gettransactionsforaddress.md)
>
> Tìm slot và transactionIndex của các giao dịch cần kiểm tra

> [card] [Historical data overview](https://www.helius.dev/docs/vi/rpc/historical-data.md)
>
> Lịch sử giao dịch, chuyển khoản và các phương thức lưu trữ khác


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