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

> Tìm hiểu các trường hợp sử dụng getMultipleAccounts, 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 [`getMultipleAccounts`](https://www.helius.dev/docs/api-reference/rpc/http/getmultipleaccounts) là một cách rất hiệu quả để truy xuất đồng thời thông tin của một danh sách tài khoản Solana. Thay vì tạo từng yêu cầu `getAccountInfo` riêng lẻ cho mỗi tài khoản, `getMultipleAccounts` cho phép bạn gộp các yêu cầu này thành một lô, nhờ đó giảm chi phí mạng và cải thiện tốc độ phản hồi của ứng dụng.

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

* **Tải dữ liệu tài khoản theo lô:** Khi ứng dụng cần hiển thị hoặc xử lý dữ liệu từ nhiều tài khoản đã biết (ví dụ: các tài khoản token của người dùng, danh sách cấu hình chương trình trên chuỗi).
* **Công cụ theo dõi danh mục đầu tư:** Truy xuất số dư và trạng thái của nhiều tài khoản token thuộc sở hữu của một người dùng.
* **Giao diện người dùng của thị trường:** Hiển thị thông tin chi tiết của nhiều NFT hoặc mục đang được niêm yết bằng cách truy xuất dữ liệu tài khoản của chúng trong một lần.
* **Cải thiện hiệu năng dApp:** Giảm đáng kể số lượng lệnh gọi RPC, giúp rút ngắn thời gian tải và mang lại trải nghiệm người dùng tốt hơn, đặc biệt khi làm việc với nhiều tài khoản.

## Tham số yêu cầu

1. **`pubkeys`** (`array` gồm các `string`, bắt buộc):
   * Một mảng gồm các chuỗi khóa công khai được mã hóa base-58 của những tài khoản bạn muốn truy vấn.
   * Tối đa 100 khóa công khai cho mỗi yêu cầu.
   * Ví dụ: `["So11111111111111111111111111111111111111112", "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"]`

2. **`options`** (`object`, tùy chọn): Một đối tượng cấu hình chứa một hoặc nhiều trường sau:
   * **`commitment`** (`string`): Chỉ định [mức cam kết](https://www.helius.dev/blog/solana-commitment-levels) cho truy vấn (ví dụ: `"finalized"`, `"confirmed"`, `"processed"`).
   * **`encoding`** (`string`): Kiểu mã hóa cho dữ liệu tài khoản. Các tùy chọn gồm:
     * `"base64"` (mặc định): Kiểu mã hóa base64 tiêu chuẩn.
     * `"base58"`: Chậm hơn nhưng có thể hữu ích trong một số trường hợp.
     * `"base64+zstd"`: Dữ liệu nén zstd được mã hóa base64.
     * `"jsonParsed"`: Nếu tài khoản thuộc sở hữu của một chương trình mà nút RPC có trình phân tích cú pháp tương ứng (ví dụ: SPL Token Program, Stake Program), trường `data` sẽ là một đối tượng JSON. Tùy chọn này rất hữu ích cho dữ liệu có cấu trúc.
   * **`dataSlice`** (`object`): Cho phép bạn chỉ truy xuất một phần cụ thể của dữ liệu tài khoản. Tùy chọn này hữu ích với các tài khoản lớn khi bạn chỉ cần một phần thông tin nhỏ.
     * `offset` (`usize`): Độ lệch tính bằng byte từ đầu dữ liệu tài khoản.
     * `length` (`usize`): Số byte cần trả về kể từ vị trí độ lệch.
     * *Lưu ý: `dataSlice` chỉ dùng được với các kiểu mã hóa `base58`, `base64` hoặc `base64+zstd`.*
   * **`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

Đối tượng phản hồi JSON-RPC sẽ có trường `result` chứa:

* **`context`** (`object`):
  * `slot` (`u64`): Slot mà tại đó thông tin được truy xuất.
  * `apiVersion` (`string`, tùy chọn): Phiên bản API của nút.
* **`value`** (`array`):
  * Một mảng trong đó mỗi phần tử tương ứng với khóa công khai tại cùng chỉ mục trong mảng `pubkeys` của yêu cầu.
  * Mỗi phần tử sẽ là một trong hai dạng:
    * `null`: Nếu tài khoản tại khóa công khai được chỉ định không tồn tại hoặc đã xảy ra lỗi với tài khoản cụ thể đó.
    * Một **Đối tượng tài khoản** có các trường sau:
      * `lamports` (`u64`): Số lamport thuộc sở hữu của tài khoản.
      * `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.
      * `data` (`array` hoặc `object`): Dữ liệu tài khoản. Nếu `encoding` là `jsonParsed` và có trình phân tích cú pháp, giá trị này sẽ là một đối tượng JSON. Nếu không, đây thường là một mảng `["encoded_string", "encoding_format"]` (ví dụ: `["SGVsbG8=", "base64"]`).
      * `executable` (`boolean`): Tài khoản có chứa một chương trình hay không (có thể thực thi hay không).
      * `rentEpoch` (`u64`): Epoch tiếp theo mà tài khoản này phải trả phí thuê.
      * `space` (`u64`): Độ dài dữ liệu của tài khoản tính bằng byte.

## Ví dụ

### 1. Truy xuất thông tin cơ bản của hai tài khoản

Ví dụ này truy xuất dữ liệu của hai tài khoản: SOL Llama (một NFT) và Serum Dex Program v3.

<CodeGroup>
  ```bash cURL theme={"system"}
  # Replace <api-key> with your Helius API key
  # SOL Llama Mint: Abug4qgG1x23AEdjS2h9CEJ1m6ha2Z22LdK2kL2pys3F
  # Serum Dex Program v3: 9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin
  curl https://mainnet.helius-rpc.com/?api-key=<api-key> -X POST -H "Content-Type: application/json" -d \
    '{
      "jsonrpc": "2.0",
      "id": 1,
      "method": "getMultipleAccounts",
      "params": [
        [
          "Abug4qgG1x23AEdjS2h9CEJ1m6ha2Z22LdK2kL2pys3F",
          "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin"
        ]
      ]
    }'
  ```

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

  async function fetchMultipleAccountInfo() {
    const connection = new Connection('https://mainnet.helius-rpc.com/?api-key=<api-key>');
    const accountPubkeys = [
      new PublicKey('Abug4qgG1x23AEdjS2h9CEJ1m6ha2Z22LdK2kL2pys3F'), // SOL Llama
      new PublicKey('9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin')  // Serum Dex Program v3
    ];

    try {
      const accountsInfo = await connection.getMultipleAccountsInfo(accountPubkeys);
      
      accountsInfo.forEach((account, index) => {
        console.log(`--- Account ${index + 1} (${accountPubkeys[index].toBase58()}) ---`);
        if (account) {
          console.log(`  Owner: ${account.owner.toBase58()}`);
          console.log(`  Lamports: ${account.lamports}`);
          console.log(`  Executable: ${account.executable}`);
          console.log(`  Data length: ${account.data.length}`);
          // For brevity, not logging full data buffer
        } else {
          console.log("  Account not found or error fetching.");
        }
      });
    } catch (error) {
      console.error('Error fetching multiple accounts:', error);
    }
  }

  fetchMultipleAccountInfo();
  ```
</CodeGroup>

### 2. Truy xuất dữ liệu tài khoản token đã được phân tích cú pháp

Ví dụ này truy xuất dữ liệu của hai tài khoản SPL Token và yêu cầu kiểu mã hóa `jsonParsed` để nhận dữ liệu có cấu trúc.

<CodeGroup>
  ```bash cURL theme={"system"}
  # Replace <api-key> with your Helius API key
  # Example USDC Token Account 1: GqoZ2MCrdTtygoX1F2b8X7F2tDXxNxyvMvykR9RzQW8p
  # Example USDT Token Account 2: HYnLMbkaPMh9W2aPNy2yP4LzLSWWw9zSCYEZdX2g2E7m
  curl https://mainnet.helius-rpc.com/?api-key=<api-key> -X POST -H "Content-Type: application/json" -d \
    '{
      "jsonrpc": "2.0",
      "id": 1,
      "method": "getMultipleAccounts",
      "params": [
        [
          "GqoZ2MCrdTtygoX1F2b8X7F2tDXxNxyvMvykR9RzQW8p",
          "HYnLMbkaPMh9W2aPNy2yP4LzLSWWw9zSCYEZdX2g2E7m"
        ],
        {
          "encoding": "jsonParsed"
        }
      ]
    }'
  ```

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

  async function fetchParsedTokenAccounts() {
    const connection = new Connection('https://mainnet.helius-rpc.com/?api-key=<api-key>');
    const tokenAccountPubkeys = [
      new PublicKey('GqoZ2MCrdTtygoX1F2b8X7F2tDXxNxyvMvykR9RzQW8p'), // Example USDC account
      new PublicKey('HYnLMbkaPMh9W2aPNy2yP4LzLSWWw9zSCYEZdX2g2E7m')  // Example USDT account
    ];

    try {
      const accountsInfo = await connection.getMultipleAccountsInfo(tokenAccountPubkeys, 'confirmed'); // Can also pass commitment here
      // Note: @solana/web3.js's getMultipleAccountsInfo automatically requests jsonParsed if the node supports it for token accounts.
      // For explicit control with raw RPC, you use the options object as in the cURL example.

      accountsInfo.forEach((account, index) => {
        console.log(`--- Token Account ${index + 1} (${tokenAccountPubkeys[index].toBase58()}) ---`);
        if (account && account.data && typeof account.data !== 'string') { // Check if data is parsed
          // The actual structure of account.data depends on the program (e.g., SPL Token)
          // For SPL Token accounts, you'd typically find parsed data in account.data.parsed.info
          const parsedInfo = (account.data as any).parsed?.info;
          if (parsedInfo) {
              console.log(`  Mint: ${parsedInfo.mint}`);
              console.log(`  Owner: ${parsedInfo.owner}`);
              console.log(`  Amount: ${parsedInfo.tokenAmount.uiAmountString} (decimals: ${parsedInfo.tokenAmount.decimals})`);
          } else {
              console.log("  Account data is not in the expected parsed format or is not a token account.");
              // console.log("Raw data:", account.data.toString('base64')); // if buffer
          }
        } else if (account) {
          console.log("  Account found, but data is not parsed or is a string (binary data).");
          // console.log("  Raw data:", account.data.toString()); // if string
        } else {
          console.log("  Account not found or error fetching.");
        }
      });
    } catch (error) {
      console.error('Error fetching parsed token accounts:', error);
    }
  }

  fetchParsedTokenAccounts();
  ```
</CodeGroup>

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

* **Tối đa 100 tài khoản:** Bạn có thể yêu cầu tối đa 100 tài khoản cho mỗi lệnh gọi.
* **Tính nguyên tử:** Yêu cầu không có tính nguyên tử theo nghĩa là nếu việc tra cứu một tài khoản thất bại, các tài khoản khác vẫn có thể thành công. Hãy kiểm tra từng phần tử trong mảng `value` để tìm `null`.
* **Sự tiện lợi của `jsonParsed`:** Bạn nên sử dụng kiểu mã hóa `jsonParsed` khi làm việc với các loại tài khoản phổ biến như tài khoản SPL Token vì không cần tự giải tuần tự hóa dữ liệu.
* **`dataSlice` cho tài khoản lớn:** Với các tài khoản rất lớn (ví dụ: một số tài khoản trạng thái chương trình), hãy sử dụng `dataSlice` để chỉ truy xuất các byte cần thiết, tránh truyền quá nhiều dữ liệu.
* **Xử lý lỗi:** Hãy chuẩn bị xử lý các mục `null` trong mảng `value` của phản hồi. Các mục này cho biết không tìm thấy hoặc không thể truy xuất tài khoản.

Bằng cách tận dụng `getMultipleAccounts`, bạn có thể xây dựng các ứng dụng Solana có hiệu năng và khả năng mở rộng tốt hơn.

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

<CardGroup cols={2}>
  <Card title="getAccountInfo" href="/docs/vi/api-reference/rpc/http/getaccountinfo">
    Truy xuất thông tin chi tiết của một tài khoản
  </Card>

  <Card title="getProgramAccounts" href="/docs/vi/api-reference/rpc/http/getprogramaccounts">
    Lấy tất cả tài khoản thuộc sở hữu của một chương trình cụ thể
  </Card>
</CardGroup>
