> ## 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 sử dụng getTokenAccountsByOwner

> Tìm hiểu các trường hợp sử dụng, ví dụ mã, tham số yêu cầu, cấu trúc phản hồi và mẹo cho getTokenAccountsByOwner.

Phương thức RPC [`getTokenAccountsByOwner`](https://www.helius.dev/docs/api-reference/rpc/http/gettokenaccountsbyowner) được dùng để truy xuất tất cả [tài khoản token](https://www.helius.dev/blog/how-to-get-token-holders-on-solana) SPL thuộc sở hữu của một khóa công khai cụ thể. Đây là phương thức cơ bản dành cho các ví và ứng dụng cần hiển thị lượng token mà người dùng nắm giữ hoặc tương tác với các tài khoản token khác nhau của họ.

Bạn phải lọc truy vấn theo một `mint` token cụ thể hoặc một `programId` (ví dụ: SPL Token Program hoặc Token-2022 Program).

Đối với các ví có danh mục token lớn, hãy cân nhắc sử dụng [`getTokenAccountsByOwnerV2`](/docs/vi/api-reference/rpc/http/gettokenaccountsbyownerv2), phương thức này hỗ trợ phân trang dựa trên con trỏ với kích thước trang có thể cấu hình lên đến 10.000 tài khoản cho mỗi yêu cầu.

## Các trường hợp sử dụng phổ biến

* **Hiển thị danh mục của người dùng:** Truy xuất tất cả tài khoản token (và do đó cả số dư) của một địa chỉ ví người dùng nhất định để hiển thị toàn bộ danh mục token của họ.
* **Logic ứng dụng:** Xác định tài khoản token cụ thể của người dùng cho một mint nhất định trước khi bắt đầu chuyển hoặc thực hiện tương tác khác.
* **Xác minh:** Kiểm tra chủ sở hữu có những tài khoản token nào đối với một loại token nhất định.
* **Lập chỉ mục người nắm giữ token:** Mặc dù kém hiệu quả hơn các phương thức khác khi lập chỉ mục toàn cục, phương thức này có thể được dùng để tìm tài khoản cho một tập hợp chủ sở hữu đã biết.

## Tham số yêu cầu

1. **`ownerPubkey`** (chuỗi, bắt buộc): Khóa công khai được mã hóa base-58 của chủ sở hữu tài khoản có các tài khoản token mà bạn muốn truy xuất.

2. **`filter`** (đối tượng, bắt buộc): Một đối tượng JSON **phải** chỉ định `mint` hoặc `programId`:
   * **`mint`** (chuỗi): Khóa công khai được mã hóa base-58 của một mint token cụ thể. Nếu được cung cấp, hệ thống chỉ trả về các tài khoản token của mint này thuộc sở hữu của `ownerPubkey`.
   * **`programId`** (chuỗi): Khóa công khai được mã hóa base-58 của Token Program quản lý các tài khoản. Các giá trị phổ biến là:
     * SPL Token Program: `TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA`
     * Token-2022 Program: `TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb`

3. **`options`** (đối tượng, không bắt buộc): Một đối tượng cấu hình tùy chọn có thể bao gồm:
   * **`commitment`** (chuỗi, không bắt buộc): Chỉ định [mức cam kết](https://www.helius.dev/blog/solana-commitment-levels).
   * **`encoding`** (chuỗi, không bắt buộc): Kiểu mã hóa cho dữ liệu tài khoản. Bạn rất nên dùng `"jsonParsed"`. Các tùy chọn khác: `"base64"`, `"base64+zstd"`. Giá trị mặc định là `"base64"`.
   * **`dataSlice`** (đối tượng, không bắt buộc): Dùng để truy xuất một phần cụ thể của dữ liệu tài khoản (`offset`: usize, `length`: usize). Chỉ dành cho các kiểu mã hóa `base58`, `base64` hoặc `base64+zstd`.
   * **`minContextSlot`** (u64, không bắt buộc): Slot tối thiểu cho truy vấn.

## Cấu trúc phản hồi

Trường `result.value` trong phản hồi JSON-RPC là một mảng đối tượng. Mỗi đối tượng tương ứng với một tài khoản SPL Token thuộc sở hữu của `ownerPubkey` và khớp với `filter`.

Mỗi đối tượng trong mảng `value` chứa:

* **`pubkey`** (chuỗi): Khóa công khai được mã hóa base-58 của chính tài khoản token.
* **`account`** (đối tượng): Thông tin chi tiết về tài khoản token:
  * **`lamports`** (u64): Số dư lamport để được miễn tiền thuê.
  * **`owner`** (chuỗi): Chương trình sở hữu (ví dụ: khóa công khai của Token Program).
  * **`data`**: Dữ liệu tài khoản. Nếu sử dụng kiểu mã hóa `"jsonParsed"`, trường này chứa:
    * **`program`** (chuỗi): ví dụ: `"spl-token"`.
    * **`parsed`**: Một đối tượng chứa thông tin có cấu trúc:
      * **`info`**: Các chi tiết như:
        * **`mint`** (chuỗi): Địa chỉ mint của token.
        * **`owner`** (chuỗi): Chủ sở hữu tài khoản token (giá trị này phải khớp với `ownerPubkey` trong yêu cầu).
        * **`tokenAmount`** (đối tượng): Số dư token (`amount`, `decimals`, `uiAmount`, `uiAmountString`).
        * **`state`** (chuỗi): Trạng thái của tài khoản token (ví dụ: `"initialized"`).
        * **`isNative`** (boolean): Tài khoản có giữ SOL được bọc hay không.
        * **`delegate`** (chuỗi, không bắt buộc): Địa chỉ đại diện nếu đã thiết lập một đại diện.
        * **`delegatedAmount`** (đối tượng, không bắt buộc): Số lượng được ủy quyền nếu đã thiết lập một đại diện.
      * **`type`** (chuỗi): ví dụ: `"account"`.
  * **`executable`** (boolean): Tài khoản có thể thực thi hay không.
  * **`rentEpoch`** (u64): Kỷ nguyên tiếp theo đến hạn trả tiền thuê.
  * **`space`** (u64, nếu không phải `jsonParsed`): Độ dài của dữ liệu tài khoản thô tính theo byte.

**Phản hồi mẫu (với kiểu mã hóa `jsonParsed`, được lọc theo `programId`):**

```json theme={"system"}
{
  "jsonrpc": "2.0",
  "result": {
    "context": {
      "slot": 183459000
    },
    "value": [
      {
        "pubkey": "AssociatedTokenAccountPubkey1...",
        "account": {
          "data": {
            "program": "spl-token",
            "parsed": {
              "info": {
                "isNative": false,
                "mint": "SomeTokenMintPubkey...",
                "owner": "OwnerPubkeyProvidedInRequest...",
                "state": "initialized",
                "tokenAmount": {
                  "amount": "1000000000", // 1 token if decimals is 9
                  "decimals": 9,
                  "uiAmount": 1.0,
                  "uiAmountString": "1.0"
                }
              },
              "type": "account"
            },
            "space": 165
          },
          "executable": false,
          "lamports": 2039280,
          "owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
          "rentEpoch": 380
        }
      },
      {
        "pubkey": "AnotherAssociatedTokenAccountPubkey...",
        "account": {
          // ... similar structure for another token owned by the same owner
        }
      }
    ]
  },
  "id": 1
}
```

## Ví dụ mã

<CodeGroup>
  ```bash cURL theme={"system"}
  # Replace <OWNER_PUBKEY> and <TOKEN_MINT_PUBKEY> or <TOKEN_PROGRAM_ID>

  # Example filtering by programId (SPL Token Program)
  curl -X POST -H "Content-Type: application/json" -d \
    '{
      "jsonrpc": "2.0",
      "id": 1,
      "method": "getTokenAccountsByOwner",
      "params": [
        "<OWNER_PUBKEY>",
        { "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" },
        { "encoding": "jsonParsed" }
      ]
    }' \
    <YOUR_RPC_URL>

  # Example filtering by a specific mint
  curl -X POST -H "Content-Type: application/json" -d \
    '{
      "jsonrpc": "2.0",
      "id": 1,
      "method": "getTokenAccountsByOwner",
      "params": [
        "<OWNER_PUBKEY>",
        { "mint": "<SPECIFIC_TOKEN_MINT_PUBKEY>" },
        { "encoding": "jsonParsed", "commitment": "confirmed" }
      ]
    }' \
    <YOUR_RPC_URL>
  ```

  ```javascript JavaScript (using @solana/web3.js) theme={"system"}
  const { Connection, PublicKey } = require('@solana/web3.js');

  async function findOwnerTokenAccounts(ownerAddress, filter, encoding = 'jsonParsed') {
    // Replace with your RPC endpoint
    const connection = new Connection('https://mainnet.helius-rpc.com/?api-key=<api-key>');
    const ownerPubKey = new PublicKey(ownerAddress);

    try {
      let actualFilter;
      if (filter.mint) {
        actualFilter = { mint: new PublicKey(filter.mint) };
      } else if (filter.programId) {
        actualFilter = { programId: new PublicKey(filter.programId) };
      } else {
        console.error("Filter must contain either 'mint' or 'programId'");
        return;
      }

      const accounts = await connection.getTokenAccountsByOwner(
        ownerPubKey,
        actualFilter,
        { encoding }
      );

      console.log(`Found ${accounts.value.length} token accounts for owner ${ownerAddress}:`);
      accounts.value.forEach(accInfo => {
        console.log(`  Token Account: ${accInfo.pubkey.toBase58()}`);
        if (encoding === 'jsonParsed' && accInfo.account.data.parsed) {
          console.log(`    Mint: ${accInfo.account.data.parsed.info.mint}`);
          console.log(`    Balance: ${accInfo.account.data.parsed.info.tokenAmount.uiAmountString}`);
        }
        // console.log(JSON.stringify(accInfo, null, 2)); // For full details
      });

    } catch (error) {
      console.error(`Error fetching token accounts for owner ${ownerAddress}:`, error);
    }
  }

  // Replace with an actual owner's public key
  const exampleOwner = 'HXtBm8XZbxaTt41uqaKhwUAa6Z1aPyvJdsZVENiWsetg'; // Example wallet address

  // Example 1: Find all SPL Token Program accounts owned by `exampleOwner`
  findOwnerTokenAccounts(exampleOwner, { programId: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA' });

  // Example 2: Find USDC token accounts owned by `exampleOwner`
  // const usdcMint = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v';
  // findOwnerTokenAccounts(exampleOwner, { mint: usdcMint });

  // Example 3: Find Token-2022 Program accounts owned by `exampleOwner`
  // findOwnerTokenAccounts(exampleOwner, { programId: 'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb' });
  ```
</CodeGroup>

## Mẹo dành cho nhà phát triển

* **Yêu cầu về bộ lọc:** Bạn *phải* cung cấp `mint` hoặc `programId` trong bộ lọc. Không thể truy vấn tất cả tài khoản token của một chủ sở hữu trên mọi loại token nếu thiếu một trong các bộ lọc chính này.
* **Tài khoản token liên kết:** Phương thức này trả về tất cả tài khoản token thuộc sở hữu của khóa công khai, bao gồm các Associated Token Account (ATA) tiêu chuẩn và mọi tài khoản SPL token khác mà họ có thể sở hữu (ví dụ: từ các cách triển khai ví cũ hoặc cấu hình tùy chỉnh).
* **Mã hóa:** Bạn rất nên dùng `"jsonParsed"` cho tùy chọn `encoding`. Tùy chọn này giải mã dữ liệu tài khoản nhị phân thành một cấu trúc JSON dễ sử dụng hơn.
* **Hiệu suất:** Nếu một chủ sở hữu có rất nhiều tài khoản token (đặc biệt khi chỉ lọc theo `programId`), phản hồi có thể rất lớn. Trong những trường hợp như vậy, hãy sử dụng [`getTokenAccountsByOwnerV2`](/docs/vi/api-reference/rpc/http/gettokenaccountsbyownerv2), phương thức này tích hợp sẵn tính năng phân trang.
* **Token-2022 (Phần mở rộng token):** Nếu đang làm việc với các token được tạo bằng chương trình Token-2022 (hỗ trợ các phần mở rộng như phí chuyển, lãi suất, v.v.), hãy đảm bảo sử dụng đúng `programId`: `TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb`.

Hướng dẫn này cung cấp kiến thức toàn diện về phương thức RPC `getTokenAccountsByOwner`, giúp bạn truy xuất hiệu quả thông tin tài khoản token cho bất kỳ địa chỉ Solana nào.

## Phân trang cho danh mục token lớn

Đối với các ví nắm giữ lượng token lớn, hãy sử dụng [`getTokenAccountsByOwnerV2`](/docs/vi/api-reference/rpc/http/gettokenaccountsbyownerv2), phương thức này cung cấp:

* **Phân trang dựa trên con trỏ**: Đặt `limit` (1-10.000) và sử dụng `paginationKey` để duyệt qua các kết quả
* **Cập nhật gia tăng**: Sử dụng `changedSinceSlot` để chỉ truy xuất các tài khoản token đã được sửa đổi kể từ một slot cụ thể
* **Hiệu suất tốt hơn**: Ngăn hết thời gian chờ và cho phép theo dõi danh mục theo thời gian thực
* **Cơ chế phân trang**: Chỉ xác định đã kết thúc phân trang khi không có tài khoản token nào được trả về. Số tài khoản trả về có thể ít hơn giới hạn do quá trình lọc — hãy tiếp tục phân trang cho đến khi `paginationKey` là null

```typescript theme={"system"}
// Example: Paginated query for all token accounts
const response = await fetch("https://mainnet.helius-rpc.com/?api-key=YOUR_API_KEY", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    jsonrpc: "2.0",
    id: "1",
    method: "getTokenAccountsByOwnerV2",
    params: [
      "9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM",
      { "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" },
      {
        encoding: "jsonParsed",
        limit: 1000
      }
    ]
  })
});

const data = await response.json();
console.log(`Found ${data.result.value.length} token accounts`);
if (data.result.paginationKey) {
  console.log("More results available, use paginationKey for next page");
  // Continue pagination even if fewer than limit accounts were returned
} else {
  console.log("End of pagination - no more token accounts available");
}
```

## Các phương thức liên quan

<CardGroup cols={2}>
  <Card title="getTokenAccountsByOwnerV2" href="/docs/vi/api-reference/rpc/http/gettokenaccountsbyownerv2">
    Phiên bản phân trang với khả năng điều hướng dựa trên con trỏ dành cho danh mục lớn
  </Card>

  <Card title="getTokenAccountBalance" href="/docs/vi/api-reference/rpc/http/gettokenaccountbalance">
    Lấy số dư của một tài khoản token cụ thể
  </Card>
</CardGroup>
