1. API reference

Updates

The Update enum and every type a datasource emits in Carbon 2.0.0, with all fields.

On this page

Datasources send (Update, DatasourceId) pairs into the pipeline. This page lists every type in carbon_core::datasource that makes up an update, field by field. You need it when you write a custom datasource or a processor for account deletions or block details. For how updates move through the pipeline, see Updates.

Source: crates/core/src/datasource.rs.

Update#

The payload a datasource sends into the pipeline.

Rust
#[derive(Debug, Clone)]
pub enum Update {
    Account(AccountUpdate),
    Transaction(Box<TransactionUpdate>),
    AccountDeletion(AccountDeletion),
    BlockDetails(BlockDetails),
}

Details.

VariantRouted toEmitted in 2.0.0 by
Accountaccount pipes (account)RPC GPA, Helius GPA V2, validator snapshot, and every streaming source for funded accounts
Transactioninstruction pipes, then transaction pipesRPC transaction crawler, RPC block crawler, RPC block subscribe, Yellowstone gRPC, Helius LaserStream, Helius Atlas WebSocket, Helius GTFA, stream message
AccountDeletiondeletion pipes (account_deletions)Yellowstone gRPC, Helius LaserStream, Helius Atlas WebSocket, RPC program subscribe, stream message
BlockDetailsblock-details pipes (block_details)RPC block subscribe only

Transaction is boxed because TransactionUpdate is large. update_type() returns the matching UpdateType.

Example.

Rust
match &update {
    Update::Account(account) => println!("account {}", account.pubkey),
    Update::Transaction(tx) => println!("tx {}", tx.signature),
    Update::AccountDeletion(deletion) => println!("closed {}", deletion.pubkey),
    Update::BlockDetails(block) => println!("block {}", block.slot),
}

See also. docs.rs, Datasources.

Update::update_type#

Returns the variant as an UpdateType.

Rust
pub fn update_type(&self) -> UpdateType

Details. Account → UpdateType::AccountUpdate, Transaction → UpdateType::Transaction, AccountDeletion → UpdateType::AccountDeletion, BlockDetails → UpdateType::BlockDetails.

Example.

Rust
assert_eq!(update.update_type(), UpdateType::AccountUpdate);

See also. UpdateType.

UpdateType#

The kinds of update a datasource declares it can emit.

Rust
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum UpdateType {
    AccountUpdate,
    Transaction,
    AccountDeletion,
    BlockDetails,
}

Details. A datasource returns these from Datasource::update_types(). In 2.0.0 the value is informational: the pipeline never calls update_types() and never checks it against the pipes you registered or the updates the datasource sends. Several built-in datasources under-report (for example, RPC block subscribe declares only Transaction but also emits BlockDetails, and Yellowstone gRPC doesn't list AccountDeletion). Don't rely on it to decide what a datasource emits; use the table under Update.

Example.

Rust
fn update_types(&self) -> Vec<UpdateType> {
    vec![UpdateType::AccountUpdate, UpdateType::AccountDeletion]
}

See also. Datasource, docs.rs.

AccountUpdate#

The state of one account at a slot.

Rust
#[derive(Debug, Clone)]
pub struct AccountUpdate {
    pub pubkey: Pubkey,
    pub account: Account,
    pub slot: u64,
    pub transaction_signature: Option<Signature>,
}

Details.

FieldTypeMeaning
pubkeysolana_pubkey::PubkeyAddress of the account.
accountsolana_account::AccountFull account: lamports, data, owner, executable, rent_epoch.
slotu64Slot the state was observed at. For GPA and snapshot sources, the slot of the RPC context or the snapshot.
transaction_signatureOption<solana_signature::Signature>Transaction that wrote this state. Yellowstone and LaserStream fill it from the update's txn_signature when present; every other built-in datasource sends None. DeduplicationFilter only deduplicates accounts that have it.

The pipeline turns AccountUpdate into an AccountMetadata plus the raw Account before calling your account decoder.

Example.

Rust
let update = AccountUpdate {
    pubkey,
    account: Account { lamports: 1_000_000, data, owner, executable: false, rent_epoch: 0 },
    slot,
    transaction_signature: None,
}
.into_update();

See also. into_update, docs.rs.

AccountUpdate::into_update#

Wraps the account update in an Update, turning zero-lamport accounts into deletions.

Rust
pub fn into_update(self) -> Update

Details. Returns Update::AccountDeletion with the same four fields when account.lamports == 0, and Update::Account otherwise. Custom datasources should always build updates this way so closures reach deletion processors, as the built-in streaming datasources do. The GPA and snapshot datasources build Update::Account directly, which is safe because they only return live accounts.

Example.

Rust
let update = AccountUpdate { pubkey, account, slot, transaction_signature: None }.into_update();
sender
    .send((update, id.clone()))
    .await
    .map_err(|e| Error::FailedToConsumeDatasource(e.to_string()))?;

See also. AccountDeletion, Custom datasource.

TransactionUpdate#

A confirmed transaction with its status metadata.

Rust
#[derive(Debug, Clone)]
pub struct TransactionUpdate {
    pub signature: Signature,
    pub transaction: VersionedTransaction,
    pub meta: TransactionStatusMeta,
    pub is_vote: bool,
    pub slot: u64,
    pub index: Option<u64>,
    pub block_time: Option<i64>,
    pub block_hash: Option<Hash>,
}

Details.

FieldTypeMeaning
signaturesolana_signature::SignatureFirst signature of the transaction.
transactionsolana_transaction::versioned::VersionedTransactionSignatures and message (legacy, V0 or V1). The pipeline reads the fee payer and instructions from transaction.message.
metasolana_transaction_status::TransactionStatusMetaExecution result: status, fee, balances, inner_instructions, log_messages, token balances, rewards, loaded_addresses, return_data, compute_units_consumed, cost_units. Inner instructions and loaded addresses must be present for CPIs and V0 lookup-table accounts to resolve.
is_voteboolVote transaction flag. Only Yellowstone and LaserStream set it from the source; the RPC and Helius GTFA datasources always set false.
slotu64Slot the transaction landed in.
indexOption<u64>Position of the transaction in its block. Some for RPC block crawler, RPC block subscribe, Yellowstone and LaserStream; GTFA passes the provider's value; None for the RPC transaction crawler and Helius Atlas. SlotRangeFilter uses it for transaction-index bounds.
block_timeOption<i64>Unix timestamp in seconds, when the source provides it.
block_hashOption<solana_hash::Hash>Hash of the block. Only the RPC block crawler and RPC block subscribe set it; every other built-in datasource sends None.

The pipeline converts the update into a TransactionMetadata. If the message has no account keys, the conversion fails with MissingFeePayer.

Example.

Rust
let update = Update::Transaction(Box::new(TransactionUpdate {
    signature,
    transaction,
    meta,
    is_vote: false,
    slot,
    index: Some(tx_index),
    block_time: Some(block_time),
    block_hash: None,
}));

See also. Transactions, docs.rs.

AccountDeletion#

An account that was closed, with its final state.

Rust
#[derive(Debug, Clone)]
pub struct AccountDeletion {
    pub pubkey: Pubkey,
    pub account: Account,
    pub slot: u64,
    pub transaction_signature: Option<Signature>,
}

Details.

FieldTypeMeaning
pubkeysolana_pubkey::PubkeyAddress of the closed account.
accountsolana_account::AccountFinal state as the source reported it, unchanged: lamports is 0. Programs often clear the data and reassign the account to the System Program when closing it, so owner and data may no longer identify the program. New in 2.0.0.
slotu64Slot of the closure.
transaction_signatureOption<solana_signature::Signature>Closing transaction, when the source reports it.

Deletion processors receive &AccountDeletion directly, with no decoder. To know which program the account belonged to, check account.owner or keep your own index of addresses.

Example.

Rust
impl Processor<AccountDeletion> for DeletionLogger {
    async fn process(&mut self, deletion: &AccountDeletion) -> CarbonResult<()> {
        println!("closed {} at slot {}", deletion.pubkey, deletion.slot);
        Ok(())
    }
}

See also. account_deletions, docs.rs.

BlockDetails#

Block-level metadata.

Rust
#[derive(Debug, Clone)]
pub struct BlockDetails {
    pub slot: u64,
    pub block_hash: Option<Hash>,
    pub previous_block_hash: Option<Hash>,
    pub rewards: Option<Rewards>,
    pub num_reward_partitions: Option<u64>,
    pub block_time: Option<i64>,
    pub block_height: Option<u64>,
}

Details.

FieldTypeMeaning
slotu64Slot of the block.
block_hashOption<solana_hash::Hash>Hash of this block.
previous_block_hashOption<solana_hash::Hash>Hash of the parent block.
rewardsOption<solana_transaction_status::Rewards>Block rewards (Vec<Reward>), when the subscription requests them.
num_reward_partitionsOption<u64>Number of partitions for partitioned epoch rewards, when reported.
block_timeOption<i64>Unix timestamp in seconds.
block_heightOption<u64>Block height.

In 2.0.0 only RpcBlockSubscribe emits block details, one per block, before that block's transactions. It copies every field from the RPC UiConfirmedBlock.

Example.

Rust
impl Processor<BlockDetails> for BlockLogger {
    async fn process(&mut self, block: &BlockDetails) -> CarbonResult<()> {
        println!("block {} height={:?}", block.slot, block.block_height);
        Ok(())
    }
}

See also. block_details, docs.rs.

DatasourceId#

Identifies the datasource an update came from.

Rust
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct DatasourceId(String);

impl DatasourceId {
    pub fn new_unique() -> Self;
    pub fn new_named(name: &str) -> Self;
}

Details.

ConstructorValue
new_unique()A random UUID v4 string. PipelineBuilder::datasource uses this.
new_named(name)The string you pass. Two IDs with the same name are equal.

The inner String is private and there is no accessor or Display impl; compare IDs with == or print them with {:?} (which shows DatasourceId("name")). Filters receive the ID in FilterContext, and Datasource::consume receives it so the datasource can tag what it sends.

Example.

Rust
let geyser = DatasourceId::new_named("geyser");
assert_eq!(geyser, DatasourceId::new_named("geyser"));
println!("{geyser:?}");

See also. datasource_with_id, DatasourceFilter, docs.rs.

DatasourceDisconnection#

Describes a gap in a streaming datasource after it reconnects.

Rust
#[derive(Debug, Clone)]
pub struct DatasourceDisconnection {
    pub source: String,
    pub disconnect_time: DateTime<Utc>,
    pub last_slot_before_disconnect: Slot,
    pub first_slot_after_reconnect: Slot,
    pub missed_slots: u64,
}

Details. This is not an Update variant and never goes through the pipeline. Two datasources in 2.0.0 can report gaps: RpcBlockSubscribe (through RpcBlockSubscribe::with_disconnect_notifier) and YellowstoneGrpcGeyserClient (through the disconnect_notifier argument of new). Each takes a tokio::sync::mpsc::Sender<DatasourceDisconnection> and sends one event with the first message after a reconnect. You read the receiver yourself, for example to backfill the missed range with a crawler. Yellowstone sends with try_send, so the event is dropped if your channel is full.

FieldTypeMeaning
sourceString"rpc-websocket" for RPC block subscribe, "yellowstone-grpc" for Yellowstone.
disconnect_timechrono::DateTime<Utc>When the stream was detected as closed or timed out.
last_slot_before_disconnectu64 (solana_clock::Slot)Last slot processed before the disconnect.
first_slot_after_reconnectu64Slot of the first message received after reconnecting.
missed_slotsu64first_slot_after_reconnect - last_slot_before_disconnect (saturating). The slots strictly between the two are the ones you may have missed, so the true gap is one less than this value.

Example.

Rust
let (tx, mut rx) = tokio::sync::mpsc::channel::<DatasourceDisconnection>(16);
tokio::spawn(async move {
    while let Some(gap) = rx.recv().await {
        eprintln!(
            "{} missed {} slots ({}..{})",
            gap.source, gap.missed_slots, gap.last_slot_before_disconnect, gap.first_slot_after_reconnect
        );
    }
});
let datasource = RpcBlockSubscribe::with_disconnect_notifier(ws_url, filters, tx);

See also. Production, docs.rs.

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.