# RPC getProgramAccounts (Carbon 2.0)

> Load every account a program owns, once, with a single getProgramAccounts call on any Solana RPC.

`GpaDatasource` calls `getProgramAccounts` once for a program, emits every returned account as an account update at the response's slot, and finishes. Use it to load the current state of a program's accounts at startup, usually with filters that select one account type, and pair it with a streaming datasource to keep that state up to date.

|          |                                                                                                                                  |
| -------- | -------------------------------------------------------------------------------------------------------------------------------- |
| Crate    | `carbon-rpc-gpa-datasource` 2.0.0                                                                                                |
| Install  | `cargo add carbon-rpc-gpa-datasource@2.0.0`                                                                                      |
| Emits    | `AccountUpdate`                                                                                                                  |
| Mode     | One-shot snapshot of current state. Finishes after the last account.                                                             |
| Provider | Any Solana JSON-RPC HTTP endpoint. The public endpoint works for filtered queries on programs with few accounts.                 |
| API docs | [docs.rs](https://docs.rs/carbon-rpc-gpa-datasource/2.0.0/carbon_rpc_gpa_datasource/)                                            |
| Source   | [`datasources/rpc-gpa-datasource`](https://github.com/sevenlabs-hq/carbon/blob/v2.0.0/datasources/rpc-gpa-datasource/src/lib.rs) |

## Quick example

This program loads every Orca Whirlpool `FeeTier` account from the public RPC. A `DataSize(44)` filter keeps the request small.

```sh
cargo new gpa && cd gpa
cargo add carbon-core@2.0.0 carbon-log-metrics@2.0.0 carbon-orca-whirlpool-decoder@2.0.0 carbon-rpc-gpa-datasource@2.0.0
cargo add solana-client@=4.2.2
cargo add tokio@1 --features full
cargo add env_logger@0.11 log@0.4
```

`src/main.rs`:

```rust
use {
    carbon_core::{
        account::AccountProcessorInputType, error::CarbonResult, pipeline::Pipeline,
        processor::Processor,
    },
    carbon_log_metrics::LogMetrics,
    carbon_orca_whirlpool_decoder::{
        accounts::OrcaWhirlpoolAccount, OrcaWhirlpoolDecoder, PROGRAM_ID,
    },
    carbon_rpc_gpa_datasource::GpaDatasource,
    solana_client::{
        rpc_config::{RpcAccountInfoConfig, RpcProgramAccountsConfig},
        rpc_filter::RpcFilterType,
    },
    std::sync::Arc,
};

#[tokio::main]
async fn main() -> CarbonResult<()> {
    env_logger::init();

    // Only Orca `FeeTier` accounts: 8-byte discriminator + 32 + 2 + 2 = 44 bytes.
    let config = RpcProgramAccountsConfig {
        filters: Some(vec![RpcFilterType::DataSize(44)]),
        account_config: RpcAccountInfoConfig::default(),
        ..Default::default()
    };

    let datasource = GpaDatasource::new_with_config(
        "https://api.mainnet-beta.solana.com".to_string(),
        PROGRAM_ID,
        config,
    );

    Pipeline::builder()
        .datasource(datasource)
        .metrics(Arc::new(LogMetrics::new()))
        .account(OrcaWhirlpoolDecoder, FeeTierLogger)
        .build()?
        .run()
        .await
}

struct FeeTierLogger;

impl Processor<AccountProcessorInputType<'_, OrcaWhirlpoolAccount>> for FeeTierLogger {
    async fn process(
        &mut self,
        input: &AccountProcessorInputType<'_, OrcaWhirlpoolAccount>,
    ) -> CarbonResult<()> {
        if let OrcaWhirlpoolAccount::FeeTier(fee_tier) = &input.decoded_account.data {
            println!(
                "{} tick_spacing={} default_fee_rate={}",
                input.metadata.pubkey, fee_tier.tick_spacing, fee_tier.default_fee_rate
            );
        }
        Ok(())
    }
}
```

```sh
RUST_LOG=info cargo run
```

The program prints one line per fee tier (825 when this page was written) and exits about a second later:

Output:

```text
1XuAqKHeX1in2pJubEA5yUkyqJj2mDCt9GQdUqVg9iN tick_spacing=8 default_fee_rate=10
12UZ2XU7Lv1aWHgu6DMsymhXEdBHQEuLaB9fPCatGo4Y tick_spacing=32896 default_fee_rate=3000
143Eu77adnubqv8j68fTDDYaRweJtrLU3zvB9pyudTeR tick_spacing=32896 default_fee_rate=3000
14HcGyeg7VqSundRvpi4Xj6GeZxWVqxeTjdi3pibFtdg tick_spacing=8 default_fee_rate=50
...
[INFO  carbon_rpc_gpa_datasource] Fetched 825 accounts for program whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc (slot: 467112536)
[INFO  carbon_core::pipeline] update_receiver closed, shutting down.
[INFO  carbon_core::pipeline] pipeline shutdown complete.
```

## Configuration

### Constructors

```rust
pub fn new(rpc_url: String, program_id: Pubkey) -> Self
pub const fn new_with_config(rpc_url: String, program_id: Pubkey, config: RpcProgramAccountsConfig) -> Self
```

`new` uses `RpcProgramAccountsConfig::default()`: no filters, every account of the program. The struct fields `rpc_url`, `program_id` and `config` are public.

### `RpcProgramAccountsConfig`

The type comes from `solana_client::rpc_config` (`solana-client` 4.2.2). Filters come from `solana_client::rpc_filter`.

| Field                             | Type                         | Default              | Meaning                                                                                                                                                                                                             |
| --------------------------------- | ---------------------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `filters`                         | `Option<Vec<RpcFilterType>>` | `None`               | Server-side filters, all of which must match: `RpcFilterType::DataSize(u64)`, `RpcFilterType::Memcmp(Memcmp::new_base58_encoded(offset, &bytes))` (or `Memcmp::new_raw_bytes`), `RpcFilterType::TokenAccountState`. |
| `account_config.encoding`         | `Option<UiAccountEncoding>`  | —                    | **Ignored**: the datasource always requests `Base64`.                                                                                                                                                               |
| `account_config.commitment`       | `Option<CommitmentConfig>`   | `None` (`confirmed`) | Commitment of the snapshot.                                                                                                                                                                                         |
| `account_config.data_slice`       | `Option<UiDataSliceConfig>`  | `None`               | Return only part of each account's data. Decoders usually fail on sliced data.                                                                                                                                      |
| `account_config.min_context_slot` | `Option<Slot>`               | `None`               | Reject the request if the node is behind this slot.                                                                                                                                                                 |
| `with_context`                    | `Option<bool>`               | —                    | **Ignored**: always set to `true`, so every update carries the response slot.                                                                                                                                       |
| `sort_results`                    | `Option<bool>`               | `None`               | Passed through to the RPC.                                                                                                                                                                                          |

## Behaviour

- **One request.** The datasource sends a single `getProgramAccounts` and holds the whole response in memory before emitting anything. Large programs need filters, a generous RPC, or [Helius gPA v2](https://carbon.sevenlabs.org/datasources/helius-gpa-v2.md), which pages.
- **Slot.** Every update gets the response's context slot and `transaction_signature: None`.
- **Backpressure.** Updates are sent with `send().await`, so nothing is dropped when the pipeline is busy.
- **Finishing.** `consume` returns after the last account. When this is the pipeline's only datasource, the pipeline logs `update_receiver closed, shutting down.` and `run()` returns. When it runs next to a streaming datasource, the pipeline keeps running. See [shutdown](https://carbon.sevenlabs.org/concepts/shutdown.md).
- **Errors.** There are no retries. Any RPC or transport error returns `FailedToConsumeDatasource("Failed to fetch program accounts")`; the original error is not included.
- **Cancellation.** Checked between accounts.
- **Metrics.** `rpc_gpa_accounts_processed_total` (counter), `rpc_gpa_fetch_duration_milliseconds` (histogram).

## Troubleshooting

### `Failed to fetch program accounts`

The datasource hides the RPC's error message. Send the same request with `curl` to see it:

```sh
curl -s https://api.mainnet-beta.solana.com -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"getProgramAccounts","params":["whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc",{"encoding":"base64","filters":[{"dataSize":44}]}]}'
```

Common causes: the program has too many accounts for the node (add `DataSize` or `Memcmp` filters), the public endpoint's data allowance is used up (`"You have used your data allowance"`, HTTP 413: wait or use your own RPC), or the request timed out.

### The processor receives no accounts

The decoder returned `None` for every account. Check that your filters select an account type the decoder knows, and that you did not set `data_slice`.

### The pipeline exits right after loading

Expected when this is the only datasource. Add a streaming datasource such as [`RpcProgramSubscribe`](https://carbon.sevenlabs.org/datasources/rpc-program-subscribe.md) to keep running; see [Load account state](https://carbon.sevenlabs.org/guides/load-account-state.md).

## See also

- [Load account state](https://carbon.sevenlabs.org/guides/load-account-state.md)
- [Helius gPA v2](https://carbon.sevenlabs.org/datasources/helius-gpa-v2.md): paginated, with `changedSinceSlot`.
- [Validator snapshot](https://carbon.sevenlabs.org/datasources/validator-snapshot.md): every account of a program from a snapshot file.
- [Datasources concept](https://carbon.sevenlabs.org/concepts/datasources.md) and [update types](https://carbon.sevenlabs.org/concepts/updates.md)

---

Carbon 2.0.0 documentation · Web: https://carbon.sevenlabs.org/datasources/rpc-gpa · Last updated 2026-11-09 · Index of all pages: https://carbon.sevenlabs.org/llms.txt
