1. Datasources

Helius getTransactionsForAddress

Backfill full transactions for an address in pages of 100 with Helius getTransactionsForAddress, filtered by slot, time, signature or status.

On this page

HeliusGtfaDatasource pages through Helius' getTransactionsForAddress, which returns full transactions with metadata in one call, and emits them as transaction updates. Compared with the RPC transaction crawler, it needs one request per 100 transactions instead of one per transaction, can go oldest-first, and filters by slot, block time, signature range or status on the server. Use it to backfill an address's history on Helius.

Cratecarbon-helius-gtfa-datasource 2.0.0
Installcargo add carbon-helius-gtfa-datasource@2.0.0
EmitsTransaction
ModeHistorical. Finishes after the last page.
ProviderA Helius RPC URL with API key, on a plan that includes getTransactionsForAddress.
API docsdocs.rs
Sourcedatasources/helius-gtfa-datasource

Quick example#

This program backfills 1,000 slots of Orca Whirlpool transactions, oldest first, prints each swapV2, and exits. It compiles as shown; to run it, set HELIUS_API_KEY and START_SLOT.

Terminal
cargo new helius-gtfa && cd helius-gtfa
cargo add carbon-core@2.0.0 carbon-log-metrics@2.0.0 carbon-orca-whirlpool-decoder@2.0.0 carbon-helius-gtfa-datasource@2.0.0
cargo add solana-commitment-config@3.1.1
cargo add tokio@1 --features full
cargo add env_logger@0.11 log@0.4
src/main.rs
use {
    carbon_core::{
        error::CarbonResult, instruction::InstructionProcessorInputType, pipeline::Pipeline,
        processor::Processor,
    },
    carbon_helius_gtfa_datasource::{
        HeliusGtfaConfig, HeliusGtfaDatasource, HeliusGtfaFilters, SlotFilter, SortOrder,
    },
    carbon_log_metrics::LogMetrics,
    carbon_orca_whirlpool_decoder::{
        instructions::OrcaWhirlpoolInstruction, OrcaWhirlpoolDecoder, PROGRAM_ID,
    },
    solana_commitment_config::CommitmentConfig,
    std::{env, sync::Arc},
};

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

    let start_slot: u64 = env::var("START_SLOT").expect("START_SLOT must be set").parse().unwrap();

    let config = HeliusGtfaConfig::new(
        Some(SortOrder::Asc), // oldest first
        Some(100),            // transactions per page (max 100)
        Some(CommitmentConfig::finalized()),
        Some(HeliusGtfaFilters {
            slot: Some(SlotFilter {
                gte: Some(start_slot),
                lt: Some(start_slot + 1_000),
                gt: None,
                lte: None,
            }),
            block_time: None,
            signature: None,
            status: None, // successful transactions only
        }),
        None, // min_context_slot
    );

    let datasource = HeliusGtfaDatasource::new_with_config(
        format!(
            "https://mainnet.helius-rpc.com/?api-key={}",
            env::var("HELIUS_API_KEY").expect("HELIUS_API_KEY must be set")
        ),
        PROGRAM_ID,
        config,
    );

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

struct SwapLogger;

impl Processor<InstructionProcessorInputType<'_, OrcaWhirlpoolInstruction>> for SwapLogger {
    async fn process(
        &mut self,
        input: &InstructionProcessorInputType<'_, OrcaWhirlpoolInstruction>,
    ) -> CarbonResult<()> {
        let tx = &input.metadata.transaction_metadata;
        if let OrcaWhirlpoolInstruction::SwapV2 { data, accounts, .. } = input.decoded_instruction {
            println!("slot {} swapV2 pool={} amount={}", tx.slot, accounts.whirlpool, data.amount);
        }
        Ok(())
    }
}
Terminal
HELIUS_API_KEY=your-helius-api-key START_SLOT=467000000 RUST_LOG=info cargo run

Swaps print in slot order, followed by Reached end of pagination and pipeline shutdown. With an invalid key:

Output
[ERROR carbon_helius_gtfa_datasource] RPC request failed with status 401 Unauthorized: Unauthorized
[ERROR carbon_core::pipeline] error consuming datasource: FailedToConsumeDatasource("HTTP error 401 Unauthorized: Unauthorized")

Configuration#

Constructors#

Rust
pub fn new(helius_rpc_url: String, address: Pubkey) -> Self
pub fn new_with_config(helius_rpc_url: String, address: Pubkey, config: HeliusGtfaConfig) -> Self

helius_rpc_url is the full URL including ?api-key=. address is the account whose transactions you want: a program, wallet, pool or mint.

HeliusGtfaConfig#

Rust
pub fn new(
    sort_order: Option<SortOrder>,
    limit: Option<u32>,
    commitment: Option<CommitmentConfig>,
    filters: Option<HeliusGtfaFilters>,
    min_context_slot: Option<u64>,
) -> Self
FieldTypeDefaultMeaning
sort_orderOption<SortOrder>Some(Asc)SortOrder::Asc (oldest first) or Desc. None (possible with new) leaves it to Helius.
limitOption<u32>Some(100)Transactions per page. Capped at 100; None in new also means 100.
commitmentOption<CommitmentConfig>confirmedNone (possible with new) leaves it to Helius.
filtersOption<HeliusGtfaFilters>NoneServer-side filters. A filter with every field empty is not sent.
min_context_slotOption<u64>NoneReject the request if Helius is behind this slot.

The datasource always requests transactionDetails: "full", Base64 encoding and maxSupportedTransactionVersion: 1.

HeliusGtfaFilters#

FieldTypeMeaning
slotOption<SlotFilter>RangeFilter<u64> with gte, gt, lte, lt.
block_timeOption<BlockTimeFilter>Unix seconds: gte, gt, lte, lt, eq.
signatureOption<SignatureFilter>RangeFilter<String> of base58 signatures.
statusOption<TransactionStatusFilter>Succeeded, Failed or Any. Applied by Helius and again by the datasource.

The datasource's own status check: None and Succeeded drop failed transactions, Failed keeps only failed ones, Any keeps both.

Behaviour#

  • Paging. Pages are fetched one at a time, following paginationToken until Helius returns none. Order within and across pages follows sort_order.
  • Updates. slot, block_time and index (the transaction's position in its block, when Helius returns it) are set; block_hash is None and is_vote is false.
  • Backpressure. Updates are sent with send().await; nothing is dropped.
  • Finishing. consume returns after the last page. A pipeline with only this datasource then shuts down.
  • Errors. There are no retries. A non-2xx status, a body that isn't JSON, an RPC error, or a transaction whose metadata can't be converted stops the datasource with FailedToConsumeDatasource. Transactions that fail to decode are skipped with Failed to decode transaction.
  • Cancellation. Checked before each page and between transactions.
  • Metrics. helius_gtfa_pages_fetched_total, helius_gtfa_transactions_processed_total (counters), helius_gtfa_fetch_duration_milliseconds (histogram).

Troubleshooting#

HTTP error 401 Unauthorized#

The API key is missing or invalid. The URL must end in ?api-key=<your key>.

RPC error: … (code: …)#

Helius rejected the request: an unsupported method on your plan, a malformed filter, or a bad address. The message is Helius' own.

The backfill stops halfway with an error#

There are no retries. Restart with a slot filter (gte = the last slot you stored, with Asc) to resume, and make your writes idempotent so the overlap is harmless.

Failed transactions are missing#

By default the datasource drops them. Set status: Some(TransactionStatusFilter::Failed) or Any.

See also#

Your endpoints

Code samples use these instead of the placeholders, so you can copy and run them as they are. They’re stored only in this browser and never sent anywhere.