The getProgramAccounts method is one of the most commonly used RPC calls in Solana to retrieve all accounts associated with a specific program. However, it does not natively provide a parameter to directly “limit” the number of results, which can result in a large response if the program manages many accounts.
So how can you optimize its use and filter or paginate data? This is where filters and additional manual pagination techniques come into play.
Some issues are related:
Solana provides filters to reduce the size of the returned dataset from getProgramAccounts. These filters allow you to apply various conditions to the data in each accountA data structure on Solana that holds tokens and state; acco... More:
Filter | Parameters | Description |
---|---|---|
memcmp | { offset, bytes } | Filters accounts whose data matches (at a certain offset) the specified byte sequence. |
dataSize | size | Returns only accounts whose data size is exactly size. |
dataSlice | { offset, length } | Returns only the slice of account data between offset and offset + length. Useful for “trimming” the returned data. |
Tip: Combining multiple filters can dramatically improve the efficiency of your queries.
Below is an example with @solana/web3.js that shows how to use the memcmp, dataSize, and dataSlice filters to reduce the response size.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
import { Connection, PublicKey } from '@solana/web3.js'; async function filterProgramAccounts() { // Connect to the Solana cluster (e.g., mainnet-beta) const connection = new Connection('https://api.mainnet-beta.solana.com'); // The Program ID whose accounts we want to filter const programId = new PublicKey('ExampleProgramId...'); // Define filters const filters = [ { // Filter accounts by matching bytes at a specific offset memcmp: { offset: 0, // Byte offset bytes: '7v1G...' // Base58-encoded sequence (example) } }, { // Filter accounts whose total data size equals 200 bytes dataSize: 200 } ]; // dataSlice to get only pubkeys (without account data) const config = { filters: filters, dataSlice: { offset: 0, length: 0 } // Returns 0 bytes of data }; try { const accounts = await connection.getProgramAccounts(programId, config); console.log(`Found ${accounts.length} matching accounts.`); // Display only the pubkeys of each account accounts.forEach((acc, index) => { console.log(`Account #${index + 1}: ${acc.pubkey.toBase58()}`); }); } catch (error) { console.error('Error fetching filtered accounts:', error); } } filterProgramAccounts(); |
Note: In this example, dataSlice is configured to retrieve 0 bytes of data, which makes it easier to get only the list of pubkeys. You can then make a second, more selective call using getAccountInfo or getMultipleAccountsInfo if you truly need the contents of certain accounts.
Because getProgramAccounts does not offer built-in pagination, a common solution is to implement manual pagination:
This method allows you to retrieve detailed information (data, lamports, etc.) for multiple accounts in one call.
Each batch’s data and proceed to the next, iterating through the entire list.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
import { Connection, PublicKey } from '@solana/web3.js'; async function paginateAccounts() { const connection = new Connection('https://api.mainnet-beta.solana.com'); const programId = new PublicKey('ExampleProgramId...'); // 1) Fetch just the Pubkeys with dataSlice const allAccounts = await connection.getProgramAccounts(programId, { dataSlice: { offset: 0, length: 0 } }); // 2) Split the list into batches const batchSize = 100; for (let i = 0; i < allAccounts.length; i += batchSize) { const batch = allAccounts.slice(i, i + batchSize); // Extract only the Pubkeys const pubkeys = batch.map(acc => acc.pubkey); // 3) Retrieve account info for each batch const accountsInfo = await connection.getMultipleAccountsInfo(pubkeys); // 4) Process each account in the batch accountsInfo.forEach((info, idx) => { if (info) { console.log(`Account: ${pubkeys[idx].toBase58()}`); // Process info.data, info.lamports, etc. } }); console.log(`Batch processed: ${i + batchSize} / ${allAccounts.length}`); } } paginateAccounts(); |
With this method, you avoid handling thousands of accounts in a single call that could overwhelm the RPC nodeAny server or computer running the Solana client software, p... More and your bandwidth.
1. Use Filters Wisely: leverage memcmp or dataSize to limit your search to only those accounts you really need.
2. Take Advantage of dataSlice for Pubkeys Only: if you don’t need the entire account data, skip the overhead and reduce network load.
3. Plan for Performance: if you handle very large volumes of accounts, consider a premium-level RPC service to get higher request limits and faster response times.
4. Retries and Timeouts: when processing large data sets, implement retry logic in case of errors and handle timeouts appropriately.
5. Monitor Connection Usage: as your project scales, evaluate the need for an RPC provider with scalable plans or gRPCgRPC es un framework de código abierto de alto rendimiento ... More support for higher performance.
Even though getProgramAccounts doesn’t offer a direct limiting or pagination parameter, you can control the data returned in several ways:
1. Filters (memcmp, dataSize, dataSlice): reduce the account set and data returned.
2. Manual pagination with getMultipleAccountsInfo: optimize processing and avoid overloading the node.
3. Choose a Reliable RPC Provider: critical to avoid strict limits or frequent failures.
Engineer. CEO of GS Node. Marketing Manager at Smithii.