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

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

Phương thức RPC [`getProgramAccounts`](https://www.helius.dev/docs/api-reference/rpc/http/getprogramaccounts) là một công cụ mạnh mẽ để truy vấn blockchain Solana. Phương thức này cho phép bạn truy xuất tất cả tài khoản thuộc sở hữu của một chương trình on-chain cụ thể. Đây là chức năng thiết yếu cho nhiều ứng dụng, từ việc tìm tất cả tài khoản token liên kết với một người dùng đối với một mint token cụ thể cho đến việc khám phá mọi tài khoản dữ liệu dành riêng cho người dùng của một ứng dụng phi tập trung.

Do một chương trình có thể sở hữu số lượng tài khoản rất lớn, `getProgramAccounts` cung cấp khả năng lọc mạnh mẽ để giúp bạn thu hẹp phạm vi tìm kiếm và chỉ truy xuất dữ liệu cần thiết một cách hiệu quả.

Đối với các ứng dụng cần truy vấn tập hợp tài khoản chương trình rất lớn, hãy cân nhắc sử dụng [`getProgramAccountsV2`](/docs/vi/api-reference/rpc/http/getprogramaccountsv2). 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

* **Tìm tất cả tài khoản token của một mint:** Tìm tất cả người nắm giữ một token SPL cụ thể.
* **Truy xuất dữ liệu dành riêng cho người dùng:** Tìm nạp tất cả tài khoản do một chương trình tạo cho một người dùng cụ thể (ví dụ: các vị thế của người dùng trong một giao thức DeFi hoặc trạng thái trò chơi của họ trong trò chơi Play-to-Earn).
* **Liệt kê tất cả phiên bản của một loại tài khoản tùy chỉnh:** Nếu chương trình của bạn xác định một cấu trúc tài khoản cụ thể, `getProgramAccounts` có thể tìm tất cả phiên bản của cấu trúc đó.
* **Giám sát trạng thái chương trình:** Theo dõi tất cả tài khoản liên quan đến một chương trình để nắm bắt trạng thái hoặc hoạt động tổng thể của chương trình.
* **Xây dựng trình khám phá và công cụ phân tích:** Tổng hợp dữ liệu về các chương trình và tài khoản liên kết với chúng.

## Tham số yêu cầu

1. **`programId`** (`string`, bắt buộc):
   * Khóa công khai được mã hóa base-58 của chương trình có các tài khoản mà bạn muốn tìm nạp.
   * Ví dụ: `"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"` (dành cho Chương trình SPL Token).

2. **`options`** (`object`, không bắt buộc): Một đối tượng cấu hình có các trường sau:
   * **`commitment`** (`string`): Chỉ định [mức cam kết](https://www.helius.dev/blog/solana-commitment-levels) (ví dụ: `"finalized"`, `"confirmed"`).
   * **`encoding`** (`string`): Kiểu mã hóa cho trường `data` trong mỗi tài khoản được trả về. Mặc định là `"base64"`.
     * `"base58"`: Phương án thay thế chậm hơn dành cho dữ liệu nhị phân.
     * `"base64"`: Kiểu mã hóa base64 tiêu chuẩn dành cho dữ liệu nhị phân.
     * `"base64+zstd"`: Dữ liệu nhị phân được mã hóa Base64 và nén bằng zstd.
     * `"jsonParsed"`: Nếu nút RPC có trình phân tích cú pháp cho loại tài khoản của chương trình (ví dụ: SPL Token, Stake), trường `data` sẽ là một đối tượng JSON có cấu trúc. Lựa chọn này rất được khuyến nghị để tăng khả năng đọc và dễ sử dụng.
   * **`filters`** (`array`): Một mảng các đối tượng bộ lọc để áp dụng cho tài khoản. Đây là yếu tố rất quan trọng đối với hiệu suất và mức độ liên quan. Bạn có thể sử dụng tối đa 4 bộ lọc. Các bộ lọc phổ biến bao gồm:
     * **`dataSize`** (`object`):
       * `dataSize` (`u64`): Lọc tài khoản theo độ dài dữ liệu tính bằng byte. Ví dụ: `{ "dataSize": 165 }` (dành cho tài khoản SPL Token).
     * **`memcmp`** (`object`): So sánh bộ nhớ. So sánh một phần dữ liệu của tài khoản với các byte được cung cấp.
       * `offset` (`usize`): Độ lệch byte trong dữ liệu tài khoản, nơi bắt đầu phép so sánh.
       * `bytes` (`string`): Chuỗi được mã hóa base-58 của các byte cần khớp. Chuỗi byte phải ngắn hơn 129 byte.
       * Ví dụ: Để tìm tài khoản token cho một mint cụ thể, hãy sử dụng `memcmp` với `offset: 0` (nơi địa chỉ mint được lưu trữ trong tài khoản token) và đặt `bytes` thành khóa công khai của mint.
   * **`dataSlice`** (`object`): Chỉ trả về một phần cụ thể trong dữ liệu của mỗi tài khoản. Hữu ích với các tài khoản lớn khi bạn chỉ cần một phần dữ liệu.
     * `offset` (`usize`): Độ lệch byte để bắt đầu cắt.
     * `length` (`usize`): Số byte cần trả về.
     * *Lưu ý: `dataSlice` chủ yếu dành cho các kiểu mã hóa nhị phân, không dành cho `jsonParsed`.*
   * **`withContext`** (`boolean`): Nếu là `true`, phản hồi sẽ là một đối tượng `RpcResponse` chứa `context` (với `slot`) và `value` (mảng tài khoản). Nếu là `false` hoặc bị bỏ qua, phản hồi thường chỉ trả về mảng tài khoản. Hành vi có thể khác đôi chút tùy theo nhà cung cấp RPC.
   * **`minContextSlot`** (`u64`): Slot tối thiểu mà tại đó yêu cầu có thể được đánh giá.

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

Phản hồi là một mảng các đối tượng, trong đó mỗi đối tượng đại diện cho một tài khoản được tìm thấy và bao gồm:

* **`pubkey`** (`string`): Khóa công khai được mã hóa base-58 của tài khoản.
* **`account`** (`object`):
  * `lamports` (`u64`): Số dư tài khoản tính bằng lamport.
  * `owner` (`string`): Khóa công khai được mã hóa base-58 của chương trình sở hữu tài khoản này (đây sẽ là `programId` mà bạn đã dùng để truy vấn).
  * `data` (`string`, `array` hoặc `object`): Dữ liệu của tài khoản, được định dạng theo tham số `encoding`.
    * Đối với `jsonParsed`: Một đối tượng JSON đại diện cho trạng thái tài khoản đã được giải tuần tự hóa.
    * Đối với `base64`: Một mảng `["encoded_string", "base64"]`.
  * `executable` (`boolean`): Tài khoản có thể thực thi hay không (tức là bản thân tài khoản là một chương trình).
  * `rentEpoch` (`u64`): Epoch tiếp theo mà tài khoản này phải trả phí thuê.
  * `space` (`u64`, không bắt buộc): Độ dài dữ liệu của tài khoản tính bằng byte. Đôi khi được gọi là `data.length` nếu dữ liệu là một bộ đệm hoặc là một phần của cấu trúc đã phân tích cú pháp.

Nếu sử dụng `withContext: true`, mảng này sẽ được lồng trong trường `value` của một đối tượng `RpcResponse`.

## Ví dụ

### 1. Tìm tất cả tài khoản token cho một mint cụ thể (USDC)

Ví dụ này tìm tất cả tài khoản SPL Token nắm giữ USDC. Ví dụ sử dụng `dataSize` để lọc các tài khoản token (165 byte) và `memcmp` để khớp địa chỉ mint USDC tại độ lệch 0.

<CodeGroup>
  ```bash cURL theme={"system"}
  # Replace <api-key> with your Helius API key
  # USDC Mint: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
  # Token Program ID: TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA
  curl https://mainnet.helius-rpc.com/?api-key=<api-key> -X POST -H "Content-Type: application/json" -d \
    '{
      "jsonrpc": "2.0",
      "id": 1,
      "method": "getProgramAccounts",
      "params": [
        "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
        {
          "encoding": "jsonParsed",
          "filters": [
            { "dataSize": 165 },
            {
              "memcmp": {
                "offset": 0, 
                "bytes": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
              }
            }
          ]
        }
      ]
    }'
  ```

  ```javascript JavaScript (using @solana/web3.js) theme={"system"}
  // Replace <api-key> with your Helius API key
  const { Connection, PublicKey } = require('@solana/web3.js');
  const { TOKEN_PROGRAM_ID } = require('@solana/spl-token');

  const USDC_MINT_ADDRESS = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v';

  async function findUsdcTokenAccounts() {
    const connection = new Connection('https://mainnet.helius-rpc.com/?api-key=<api-key>');

    try {
      const accounts = await connection.getProgramAccounts(TOKEN_PROGRAM_ID, {
        encoding: 'jsonParsed',
        filters: [
          {
            dataSize: 165, // Standard token account size
          },
          {
            memcmp: {
              offset: 0, // Offset for the mint address in a token account
              bytes: USDC_MINT_ADDRESS, // Base-58 encoded mint address
            },
          },
        ],
      });

      console.log(`Found ${accounts.length} USDC token accounts.`);
      accounts.forEach((accountInfo, index) => {
        console.log(`--- Account ${index + 1} ---`);
        console.log(`  Pubkey: ${accountInfo.pubkey.toBase58()}`);
        // Accessing parsed data
        const parsedData = accountInfo.account.data.parsed.info;
        console.log(`  Owner: ${parsedData.owner}`);
        console.log(`  Amount: ${parsedData.tokenAmount.uiAmountString}`);
      });
    } catch (error) {
      console.error('Error fetching USDC token accounts:', error);
    }
  }

  findUsdcTokenAccounts();
  ```
</CodeGroup>

### 2. Tìm tất cả tài khoản token thuộc sở hữu của một ví cụ thể

Ví dụ này tìm tất cả tài khoản SPL Token thuộc sở hữu của một địa chỉ ví cụ thể. Ví dụ sử dụng `dataSize` (165 byte) và `memcmp` tại độ lệch 32 (nơi khóa công khai của chủ sở hữu được lưu trữ trong tài khoản token).

<CodeGroup>
  ```bash cURL theme={"system"}
  # Replace <api-key> with your Helius API key
  # Example Wallet Address: Helioo21241PANoNdeG55722hgUnp2VawDgsz2g
  # Token Program ID: TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA
  curl https://mainnet.helius-rpc.com/?api-key=<api-key> -X POST -H "Content-Type: application/json" -d \
    '{
      "jsonrpc": "2.0",
      "id": 1,
      "method": "getProgramAccounts",
      "params": [
        "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
        {
          "encoding": "jsonParsed",
          "filters": [
            { "dataSize": 165 },
            {
              "memcmp": {
                "offset": 32, 
                "bytes": "Helioo21241PANoNdeG55722hgUnp2VawDgsz2g"
              }
            }
          ]
        }
      ]
    }'
  ```

  ```javascript JavaScript (using @solana/web3.js) theme={"system"}
  // Replace <api-key> with your Helius API key
  const { Connection, PublicKey } = require('@solana/web3.js');
  const { TOKEN_PROGRAM_ID } = require('@solana/spl-token');

  const TARGET_WALLET_ADDRESS = 'Helioo21241PANoNdeG55722hgUnp2VawDgsz2g';

  async function findWalletTokenAccounts() {
    const connection = new Connection('https://mainnet.helius-rpc.com/?api-key=<api-key>');

    try {
      const accounts = await connection.getProgramAccounts(TOKEN_PROGRAM_ID, {
        encoding: 'jsonParsed',
        filters: [
          {
            dataSize: 165, // Standard token account size
          },
          {
            memcmp: {
              offset: 32, // Offset for the owner address in a token account
              bytes: TARGET_WALLET_ADDRESS, // Base-58 encoded wallet address
            },
          },
        ],
      });

      console.log(`Found ${accounts.length} token accounts for wallet ${TARGET_WALLET_ADDRESS}.`);
      accounts.forEach((accountInfo, index) => {
        console.log(`--- Account ${index + 1} (${accountInfo.pubkey.toBase58()}) ---`);
        const parsedData = accountInfo.account.data.parsed.info;
        console.log(`  Mint: ${parsedData.mint}`);
        console.log(`  Amount: ${parsedData.tokenAmount.uiAmountString}`);
      });
    } catch (error) {
      console.error('Error fetching token accounts for wallet:', error);
    }
  }

  findWalletTokenAccounts();
  ```
</CodeGroup>

## Lọc nâng cao

Tối ưu hóa truy vấn bằng các bộ lọc để giảm kích thước phản hồi và cải thiện hiệu suất:

```typescript theme={"system"}
// Example filtering by memcmp (memory comparison)
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: "getProgramAccounts",
      params: [
        "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", // Solana Token Program
        {
          encoding: "jsonParsed",
          filters: [
            {
              dataSize: 165, // Size of token account data
            },
            {
              memcmp: {
                offset: 32, // Location of owner address in the token account
                bytes: "83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri",
              },
            },
          ],
        },
      ],
    }),
  }
);
const data = await response.json();
console.log("Filtered program accounts data:", data);
```

<Card title="API Reference" horizontal icon="code" href="/docs/vi/api-reference/rpc/http/getprogramaccounts">
  getProgramAccounts
</Card>

### Các loại bộ lọc

* `memcmp`: Lọc các tài khoản khớp với một mẫu cụ thể tại độ lệch đã cho
* `dataSize`: Lọc tài khoản theo kích thước dữ liệu chính xác
* Nhiều bộ lọc: Tất cả điều kiện đều phải được đáp ứng (phép AND logic)

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

* **Hiệu suất:** `getProgramAccounts` có thể tiêu tốn nhiều tài nguyên trên các nút RPC, đặc biệt khi không có bộ lọc hoặc khi dùng với các chương trình có nhiều tài khoản. Luôn sử dụng bộ lọc (`dataSize`, `memcmp`) và `dataSlice` khi có thể để thu hẹp phạm vi truy vấn và giảm kích thước phản hồi.
* **Tập kết quả lớn:** Đối với các truy vấn trả về nhiều kết quả, phản hồi có thể bị cắt bớt hoặc hết thời gian chờ. Hãy sử dụng bộ lọc để thu hẹp phạm vi hoặc cân nhắc [`getProgramAccountsV2`](/docs/vi/api-reference/rpc/http/getprogramaccountsv2) để được hỗ trợ phân trang.
* **Giới hạn tốc độ:** Hãy lưu ý đến giới hạn tốc độ của nhà cung cấp RPC vì các lệnh gọi `getProgramAccounts` thường xuyên hoặc tốn nhiều tài nguyên có thể chạm đến các giới hạn này.
* **Kiến thức về bố cục dữ liệu:** Để sử dụng `memcmp` hiệu quả, bạn cần hiểu bố cục byte của dữ liệu tài khoản đang truy vấn.
* **Tính khả dụng của `jsonParsed`:** Kiểu mã hóa `jsonParsed` phụ thuộc vào việc nút RPC có trình phân tích cú pháp cho các loại tài khoản của chương trình cụ thể hay không. Kiểu mã hóa này được hỗ trợ rộng rãi cho các chương trình phổ biến như SPL Token.

`getProgramAccounts` là phương thức không thể thiếu đối với các nhà phát triển cần truy vấn và tương tác với tập hợp tài khoản thuộc sở hữu của một chương trình. Nắm vững các tùy chọn lọc là yếu tố then chốt để xây dựng ứng dụng Solana hiệu quả và mạnh mẽ.

## Phân trang cho tập dữ liệu lớn

Đối với các ứng dụng làm việc với chương trình sở hữu số lượng lớn tài khoản (trên 10.000), hãy sử dụng [`getProgramAccountsV2`](/docs/vi/api-reference/rpc/http/getprogramaccountsv2), 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` để điều hướng qua các kết quả
* **Cập nhật tăng dần**: Sử dụng `changedSinceSlot` để chỉ tìm nạp các tài khoản đã được sửa đổi kể từ một slot cụ thể
* **Hiệu suất tốt hơn**: Ngăn tình trạng hết thời gian chờ và giảm mức sử dụng bộ nhớ
* **Hành vi phân trang**: Chỉ xác định đã đến cuối quá trình phân trang khi không có tài khoản nào được trả về. Do hoạt động lọc, số tài khoản trả về có thể ít hơn giới hạn — 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: "getProgramAccountsV2",
    params: [
      "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
      {
        encoding: "base64",
        filters: [{ dataSize: 165 }],
        limit: 5000
      }
    ]
  })
});

const data = await response.json();
console.log(`Found ${data.result.accounts.length} 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 accounts available");
}
```

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

<CardGroup cols={2}>
  <Card title="getProgramAccountsV2" href="/docs/vi/api-reference/rpc/http/getprogramaccountsv2">
    Phiên bản có phân trang với khả năng điều hướng dựa trên con trỏ dành cho tập dữ liệu lớn
  </Card>
</CardGroup>
