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

# 如何使用 getProgramAccounts

> 了解 getProgramAccounts 的使用案例、代码示例、请求参数、响应结构和提示。

[`getProgramAccounts`](https://www.helius.dev/docs/api-reference/rpc/http/getprogramaccounts) RPC 方法是查询 Solana 区块链的强大工具。它允许您检索由特定链上程序拥有的所有账户。这对于多种应用程序至关重要，从查找与特定代币铸造相关的所有用户代币账户，到发现去中心化应用程序的所有用户特定数据账户。

由于程序可能拥有的大量账户，`getProgramAccounts` 提供了强大的过滤功能，帮助您缩小搜索范围，仅高效检索所需数据。

对于需要查询非常大程序账户集的应用程序，请考虑使用 [`getProgramAccountsV2`](/docs/zh/api-reference/rpc/http/getprogramaccountsv2)，该方法提供基于游标的分页支持，每次请求最多可配置 10,000 个账户。

## 常见用例

* **查找铸币的所有代币账户：** 发现特定 SPL 代币的所有持有者。
* **检索用户特定数据：** 获取程序为特定用户创建的所有账户（例如，用户在 DeFi 协议中的头寸，或他们在游戏中的状态）。
* **列出自定义账户类型的所有实例：** 如果您的程序定义了特定账户结构，`getProgramAccounts` 可以找到该结构的所有实例。
* **监控程序状态：** 观察与程序相关的所有账户，以跟踪其整体状态或活动。
* **构建探查器和分析工具：** 聚合有关程序及其关联账户的数据。

## 请求参数

1. **`programId`** (`string`, 必需):
   * 您要获取其账户的程序的 base-58 编码公钥。
   * 示例：`"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"`（用于 SPL 代币程序）。

2. **`options`** (`object`, 可选项): 包含以下字段的配置对象：
   * **`commitment`** (`string`): 指定[承诺级别](https://www.helius.dev/blog/solana-commitment-levels)（例如，`"finalized"`, `"confirmed"`）。
   * **`encoding`** (`string`): 每个返回账户中`data`字段的编码。默认为`"base64"`。
     * `"base58"`: 较慢的二进制数据替代方案。
     * `"base64"`: 标准的base64编码用于二进制数据。
     * `"base64+zstd"`: base64编码的zstd压缩二进制数据。
     * `"jsonParsed"`: 如果RPC节点有程序账户类型的解析器（例如，SPL Token，Stake），则`data`字段将是一个结构化的JSON对象。这对于可读性和易用性非常推荐。
   * **`filters`** (`array`): 要应用于账户的过滤器对象数组。这对于性能和相关性至关重要。你最多可以使用4个过滤器。常见的过滤器包括:
     * **`dataSize`** (`object`):
       * `dataSize` (`u64`): 按字节过滤账户的数据长度。示例: `{ "dataSize": 165 }` (用于SPL Token账户)。
     * **`memcmp`** (`object`): 内存比较。将账户数据的切片与提供的字节进行比较。
       * `offset` (`usize`): 开始比较的账户数据的字节偏移量。
       * `bytes` (`string`): 要匹配的字节的base-58编码字符串。字节字符串必须小于129字节。
       * 示例: 要查找特定铸币的代币账户，可以使用`memcmp`与`offset: 0` (其中铸币地址存储在代币账户中) 和 `bytes`设置为铸币的公钥。
   * **`dataSlice`** (`object`): 仅返回每个账户数据的特定切片。当您只需要部分数据时，这对大账户非常有用。
     * `offset` (`usize`): 开始切片的字节偏移量。
     * `length` (`usize`): 要返回的字节数。
     * *注意: `dataSlice`主要用于二进制编码，而非`jsonParsed`。*
   * **`withContext`** (`boolean`): 如果`true`, 则响应将是一个`RpcResponse`对象，包含一个`context` (带有`slot`) 和 `value` (账户数组)。如果`false`或省略，通常只返回账户数组。行为可能因RPC提供商而略有不同。
   * **`minContextSlot`** (`u64`): 可以评估请求的最小槽位。

## 响应结构

响应是一个对象数组，其中每个对象代表一个找到的账户，并包括：

* **`pubkey`** (`string`): 账户的 base-58 编码公钥。
* **`account`** (`object`):
  * `lamports` (`u64`): 账户以 lamports 为单位的余额。
  * `owner` (`string`): 拥有此账户的程序的 base-58 编码公钥（这将是您查询的 `programId`）。
  * `data` (`string`, `array`, 或 `object`): 根据 `encoding` 参数格式化的账户数据。
    * 对于 `jsonParsed`: 表示反序列化账户状态的 JSON 对象。
    * 对于 `base64`: 一个数组 `["encoded_string", "base64"]`。
  * `executable` (`boolean`): 账户是否可执行（即自身为程序）。
  * `rentEpoch` (`u64`): 此账户下次欠租的纪元。
  * `space` (`u64`, 可选): 账户数据的字节长度。如果数据是缓冲区，或作为解析结构的一部分，有时称为 `data.length`。

如果使用 `withContext: true`，这个数组将嵌套在 `value` 字段中的 `RpcResponse` 对象中。

## 示例

### 1. 查找特定铸币（USDC）的所有代币账户

此示例查找持有 USDC 的所有 SPL 代币账户。它使用 `dataSize` 过滤代币账户（165 字节）并使用 `memcmp` 匹配在偏移量 0 的 USDC 铸币地址。

<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. 查找由特定钱包拥有的所有代币账户

此示例查找由特定钱包地址拥有的所有 SPL 代币账户。它使用 `dataSize`（165 字节）和 `memcmp` 在偏移量 32（所有者公钥存储在令牌账户中的位置）。

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

## 高级过滤

使用过滤器优化查询以减少响应大小并提高性能：

```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/zh/api-reference/rpc/http/getprogramaccounts">
  getProgramAccounts
</Card>

### 过滤器类型

* `memcmp`: 过滤在特定偏移处匹配特定模式的账户
* `dataSize`: 按账户的精确数据大小进行过滤
* 多重过滤器：所有条件必须满足（逻辑与）

## 开发者提示

* **性能：** `getProgramAccounts` 在没有过滤器或程序拥有许多账户时，可能对RPC节点资源消耗较大。务必使用过滤器（`dataSize`, `memcmp`）和 `dataSlice` 来减少查询范围和响应大小。
* **大结果集：** 对于返回许多结果的查询，响应可能会被截断或超时。使用过滤减少范围，或考虑使用[`getProgramAccountsV2`](/docs/zh/api-reference/rpc/http/getprogramaccountsv2) 进行分页支持。
* **速率限制：** 注意RPC提供商的速率限制，因为频繁或大量的 `getProgramAccounts` 调用可能会达到这些限制。
* **数据布局知识：** 有效使用 `memcmp` 需要了解您正在查询的账户数据的字节布局。
* **`jsonParsed` 可用性：** `jsonParsed` 编码依赖于RPC节点是否有特定程序账户类型的解析器。对于常见程序，如SPL Token，广泛支持。

`getProgramAccounts` 是开发人员需要查询和交互程序所拥有账户集的重要方法。掌握其过滤选项是构建高效稳健的Solana应用程序的关键。

## 大数据集的分页

对于处理拥有大量账户（10,000+）的程序的应用，使用[`getProgramAccountsV2`](/docs/zh/api-reference/rpc/http/getprogramaccountsv2)，该方法提供：

* **基于游标的分页**: 设置 `limit`（1-10,000）并使用 `paginationKey` 导航结果
* **增量更新**: 使用 `changedSinceSlot` 仅获取自特定槽以来修改的账户
* **更好的性能**: 防止超时和减少内存使用
* **分页行为**: 只有在不返回账户时才表示分页结束。由于过滤，返回的账户可能少于限制 - 继续分页直到 `paginationKey` 为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");
}
```

## 相关方法

<CardGroup cols={2}>
  <Card title="getProgramAccountsV2" href="/docs/zh/api-reference/rpc/http/getprogramaccountsv2">
    使用基于游标的导航用于大型数据集的分页版本
  </Card>
</CardGroup>
