# Updates (Carbon 2.0)

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

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](https://carbon.sevenlabs.org/guides/custom-datasource.md) or a processor for account deletions or block details. For how updates move through the pipeline, see [Updates](https://carbon.sevenlabs.org/concepts/updates.md).

Source: [`crates/core/src/datasource.rs`](https://github.com/sevenlabs-hq/carbon/blob/v2.0.0/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.**

| Variant           | Routed to                                                                                                            | Emitted in 2.0.0 by                                                                                                                                        |
| ----------------- | -------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Account`         | account pipes ([`account`](https://carbon.sevenlabs.org/reference/pipeline-builder.md#account))                      | RPC GPA, Helius GPA V2, validator snapshot, and every streaming source for funded accounts                                                                 |
| `Transaction`     | instruction pipes, then transaction pipes                                                                            | RPC transaction crawler, RPC block crawler, RPC block subscribe, Yellowstone gRPC, Helius LaserStream, Helius Atlas WebSocket, Helius GTFA, stream message |
| `AccountDeletion` | deletion pipes ([`account_deletions`](https://carbon.sevenlabs.org/reference/pipeline-builder.md#account_deletions)) | Yellowstone gRPC, Helius LaserStream, Helius Atlas WebSocket, RPC program subscribe, stream message                                                        |
| `BlockDetails`    | block-details pipes ([`block_details`](https://carbon.sevenlabs.org/reference/pipeline-builder.md#block_details))    | RPC block subscribe only                                                                                                                                   |

`Transaction` is boxed because `TransactionUpdate` is large. `update_type()` returns the matching [`UpdateType`](#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](https://docs.rs/carbon-core/2.0.0/carbon_core/datasource/enum.Update.html), [Datasources](https://carbon.sevenlabs.org/concepts/datasources.md).

## `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).

## `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`](#update).

**Example.**

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

**See also.** [`Datasource`](https://carbon.sevenlabs.org/reference/traits.md#datasource), [docs.rs](https://docs.rs/carbon-core/2.0.0/carbon_core/datasource/enum.UpdateType.html).

## `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.**

| Field                   | Type                                  | Meaning                                                                                                                                                                                                                             |
| ----------------------- | ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `pubkey`                | `solana_pubkey::Pubkey`               | Address of the account.                                                                                                                                                                                                             |
| `account`               | `solana_account::Account`             | Full account: `lamports`, `data`, `owner`, `executable`, `rent_epoch`.                                                                                                                                                              |
| `slot`                  | `u64`                                 | Slot the state was observed at. For GPA and snapshot sources, the slot of the RPC context or the snapshot.                                                                                                                          |
| `transaction_signature` | `Option<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`](https://carbon.sevenlabs.org/reference/processor-inputs.md#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`](#accountupdateinto_update), [docs.rs](https://docs.rs/carbon-core/2.0.0/carbon_core/datasource/struct.AccountUpdate.html).

## `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`](#accountdeletion), [Custom datasource](https://carbon.sevenlabs.org/guides/custom-datasource.md).

## `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.**

| Field         | Type                                                  | Meaning                                                                                                                                                                                                                                                                                            |
| ------------- | ----------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `signature`   | `solana_signature::Signature`                         | First signature of the transaction.                                                                                                                                                                                                                                                                |
| `transaction` | `solana_transaction::versioned::VersionedTransaction` | Signatures and message (legacy, V0 or V1). The pipeline reads the fee payer and instructions from `transaction.message`.                                                                                                                                                                           |
| `meta`        | `solana_transaction_status::TransactionStatusMeta`    | Execution 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_vote`     | `bool`                                                | Vote transaction flag. Only Yellowstone and LaserStream set it from the source; the RPC and Helius GTFA datasources always set `false`.                                                                                                                                                            |
| `slot`        | `u64`                                                 | Slot the transaction landed in.                                                                                                                                                                                                                                                                    |
| `index`       | `Option<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_time`  | `Option<i64>`                                         | Unix timestamp in seconds, when the source provides it.                                                                                                                                                                                                                                            |
| `block_hash`  | `Option<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`](https://carbon.sevenlabs.org/reference/processor-inputs.md#transactionmetadata). If the message has no account keys, the conversion fails with [`MissingFeePayer`](https://carbon.sevenlabs.org/reference/errors.md#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](https://carbon.sevenlabs.org/concepts/transactions.md), [docs.rs](https://docs.rs/carbon-core/2.0.0/carbon_core/datasource/struct.TransactionUpdate.html).

## `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.**

| Field                   | Type                                  | Meaning                                                                                                                                                                                                                                    |
| ----------------------- | ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `pubkey`                | `solana_pubkey::Pubkey`               | Address of the closed account.                                                                                                                                                                                                             |
| `account`               | `solana_account::Account`             | Final 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. |
| `slot`                  | `u64`                                 | Slot of the closure.                                                                                                                                                                                                                       |
| `transaction_signature` | `Option<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`](https://carbon.sevenlabs.org/reference/pipeline-builder.md#account_deletions), [docs.rs](https://docs.rs/carbon-core/2.0.0/carbon_core/datasource/struct.AccountDeletion.html).

## `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.**

| Field                   | Type                                         | Meaning                                                             |
| ----------------------- | -------------------------------------------- | ------------------------------------------------------------------- |
| `slot`                  | `u64`                                        | Slot of the block.                                                  |
| `block_hash`            | `Option<solana_hash::Hash>`                  | Hash of this block.                                                 |
| `previous_block_hash`   | `Option<solana_hash::Hash>`                  | Hash of the parent block.                                           |
| `rewards`               | `Option<solana_transaction_status::Rewards>` | Block rewards (`Vec<Reward>`), when the subscription requests them. |
| `num_reward_partitions` | `Option<u64>`                                | Number of partitions for partitioned epoch rewards, when reported.  |
| `block_time`            | `Option<i64>`                                | Unix timestamp in seconds.                                          |
| `block_height`          | `Option<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`](https://carbon.sevenlabs.org/reference/pipeline-builder.md#block_details), [docs.rs](https://docs.rs/carbon-core/2.0.0/carbon_core/datasource/struct.BlockDetails.html).

## `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.**

| Constructor       | Value                                                             |
| ----------------- | ----------------------------------------------------------------- |
| `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`](https://carbon.sevenlabs.org/reference/filters.md#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`](https://carbon.sevenlabs.org/reference/pipeline-builder.md#datasource_with_id), [`DatasourceFilter`](https://carbon.sevenlabs.org/reference/filters.md#datasourcefilter), [docs.rs](https://docs.rs/carbon-core/2.0.0/carbon_core/datasource/struct.DatasourceId.html).

## `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.

| Field                         | Type                         | Meaning                                                                                                                                                                                    |
| ----------------------------- | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `source`                      | `String`                     | `"rpc-websocket"` for RPC block subscribe, `"yellowstone-grpc"` for Yellowstone.                                                                                                           |
| `disconnect_time`             | `chrono::DateTime<Utc>`      | When the stream was detected as closed or timed out.                                                                                                                                       |
| `last_slot_before_disconnect` | `u64` (`solana_clock::Slot`) | Last slot processed before the disconnect.                                                                                                                                                 |
| `first_slot_after_reconnect`  | `u64`                        | Slot of the first message received after reconnecting.                                                                                                                                     |
| `missed_slots`                | `u64`                        | `first_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](https://carbon.sevenlabs.org/guides/production.md), [docs.rs](https://docs.rs/carbon-core/2.0.0/carbon_core/datasource/struct.DatasourceDisconnection.html).

---

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