Updates
The Update enum that datasources send into a pipeline, its four variants, UpdateType, zero-lamport account deletions, Transaction V1 and block details.
On this page
Every piece of data a datasource produces enters the pipeline as a carbon_. You meet this type when you write a custom datasource, and its fields show up again in the metadata your processors receive. Full field-by-field details are in the updates reference.
The Update enum#
pub enum Update {
Account(AccountUpdate),
Transaction(Box<TransactionUpdate>),
AccountDeletion(AccountDeletion),
BlockDetails(BlockDetails),
}| Variant | Means | Delivered to |
|---|---|---|
Account | An account's state at a slot: a write, or a read from a snapshot or getProgramAccounts. | account pipes, through their decoder |
Transaction | A transaction with its execution metadata. | instruction pipes (one call per decoded instruction) and transaction pipes |
AccountDeletion | An account that was closed: its lamports reached zero. | account_ pipes |
BlockDetails | Metadata about a block. | block_ pipes |
The pipeline routes each variant only to pipes of the matching kind; Architecture gives the exact order.
Account updates#
pub struct AccountUpdate {
pub pubkey: Pubkey,
pub account: Account,
pub slot: u64,
pub transaction_signature: Option<Signature>,
}account is a solana_ with lamports, data, owner, executable and rent_epoch. transaction_ is the transaction that wrote the account, when the source knows it: Geyser streams usually set it; getProgramAccounts and snapshots don't.
Account deletions#
Solana has no explicit "account closed" event. A program closes an account by moving all its lamports out, and the runtime then removes it. Carbon 2.0 reports every account write with zero lamports as an AccountDeletion:
pub struct AccountDeletion {
pub pubkey: Pubkey,
pub account: Account, // the final state the source reported
pub slot: u64,
pub transaction_signature: Option<Signature>,
}Datasources do this conversion with AccountUpdate::, which returns Update:: when account. and Update:: otherwise. The Yellowstone gRPC, Helius LaserStream, Helius Atlas websocket, RPC program subscribe and stream message datasources all use it. Call it in your own datasources too, so that deletions are classified in one place:
let update = AccountUpdate { pubkey, account, slot, transaction_signature }.into_update();Because deletions are a separate variant, an account pipe never sees a zero-lamport account from these datasources. Register an account_ processor to handle closures. The final account keeps the owner and data the source reported, so you can tell which program the account belonged to.
Transactions#
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>,
}transactionis the signed transaction;metais its execution result: status, fee, balances, log messages, inner instructions, loaded addresses and so on.indexis the transaction's position in its block. Yellowstone, LaserStream, RPC block subscribe, the block crawler and Helius GTFA set it; the transaction crawler and Helius Atlas websockets leave itNone.block_timeis a Unix timestamp in seconds, when the source provides it.
The pipeline turns each TransactionUpdate into a shared TransactionMetadata and the list of its instructions before any processor runs.
Transaction V1#
The Agave 4.2 Solana crates add the V1 transaction format alongside legacy and v0 transactions. transaction. is a VersionedMessage, and Carbon 2.0 handles all three variants (Legacy, V0 and V1) when it extracts instructions and their accounts.
Datasources must request V1 transactions and a binary encoding, because Carbon can't rebuild a transaction from a Json or JsonParsed payload. The RPC transaction crawler and the Helius GTFA datasource set this internally. When you pass your own RpcBlockSubscribeConfig or RpcBlockConfig, set it yourself:
RpcBlockSubscribeConfig {
encoding: Some(UiTransactionEncoding::Base64),
transaction_details: Some(TransactionDetails::Full),
max_supported_transaction_version: Some(1),
..RpcBlockSubscribeConfig::default()
}Block details#
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>,
}Among the Carbon 2.0 datasources, only RPC block subscribe sends BlockDetails: one per block, before that block's transactions. A custom datasource can send them too.
UpdateType#
UpdateType names the variants without their data:
pub enum UpdateType {
AccountUpdate,
Transaction,
AccountDeletion,
BlockDetails,
}Update:: returns the UpdateType of an update, and Datasource:: returns the types a datasource declares it sends. In 2.0.0 the pipeline doesn't read update_ or check updates against it, so treat it as documentation. It isn't always complete: RPC block subscribe declares only Transaction but also sends BlockDetails.
Example#
This program builds the same token account twice, once funded and once with zero lamports, and shows how into_update classifies each.
cargo add carbon-core@2.0.0 solana-account@4.3.2 solana-pubkey@4.2.1use {
carbon_core::datasource::{AccountUpdate, Update},
solana_account::Account,
solana_pubkey::Pubkey,
};
fn main() {
let token_program = Pubkey::from_str_const("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
for lamports in [2_039_280, 0] {
let update = AccountUpdate {
pubkey: Pubkey::new_unique(),
account: Account {
lamports,
data: vec![0; 165],
owner: token_program,
executable: false,
rent_epoch: u64::MAX,
},
slot: 371_000_000,
transaction_signature: None,
};
match update.into_update() {
Update::Account(update) => {
println!("account update: {} lamports", update.account.lamports)
}
Update::AccountDeletion(deletion) => println!(
"account deletion: owner {}, {} bytes of final data",
deletion.account.owner,
deletion.account.data.len()
),
other => println!("{:?}", other.update_type()),
}
}
}cargo runaccount update: 2039280 lamports
account deletion: owner TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA, 165 bytes of final data