Skip to main content

Overview

The SubscribeDeshred method subscribes to deshred transaction updates, which are transactions received BEFORE execution. These transactions are emitted when entries are formed from network shreds, before any execution or status metadata is available.
rpc SubscribeDeshred(stream SubscribeDeshredRequest) returns (stream SubscribeUpdateDeshred) {}

What are Deshred Transactions?

Deshred transactions represent the earliest possible point to observe transactions in the Solana validator:
  • Pre-execution: Transactions are captured when shreds are assembled into entries, before any execution occurs
  • No execution metadata: TransactionStatusMeta is not available (no logs, compute units, or error information)
  • Address lookup tables resolved: Both static account keys and dynamically loaded addresses from ALTs are available for filtering
  • Earliest signal: Get the fastest possible notification that a transaction has been received by the validator

Use Cases

MEV Detection

Monitor transaction flow before execution to detect MEV opportunities or sandwich attacks.

Latency-Critical Apps

React to transactions with minimal latency, before execution completes.

Transaction Analysis

Analyze transaction patterns and account interactions before results are known.

Front-Running Detection

Detect potential front-running attempts by observing transaction order before execution.

SubscribeDeshredRequest

The request message defines filters for deshred transactions.
deshred_transactions
map<string, SubscribeRequestFilterDeshredTransactions>
Map of deshred transaction filters. Key is a user-defined filter name, value contains filter criteria.
ping
SubscribeRequestPing
Send a ping request. Server will respond with a pong message containing the same ID.

Deshred Transaction Filters

vote
bool
Filter vote transactions. Set to false to exclude vote transactions (recommended for most use cases).
account_include
string[]
List of account public keys. Receive transactions that mention any of these accounts in either static keys or ALT-loaded addresses.
account_exclude
string[]
List of account public keys. Exclude transactions that mention any of these accounts.
account_required
string[]
List of account public keys. Receive transactions that mention ALL of these accounts (static or ALT-loaded).

SubscribeUpdateDeshred

The server responds with a stream of deshred update messages.
filters
string[]
Names of the filters that matched this update.
update_oneof
oneof
One of the following update types:
  • deshred_transaction: Pre-execution transaction data
  • ping: Server-initiated ping for keepalive
  • pong: Response to client ping
created_at
google.protobuf.Timestamp
Timestamp when the update was created on the server.

Deshred Transaction Update

transaction.signature
bytes
Transaction signature (64 bytes).
transaction.is_vote
bool
Whether this is a vote transaction.
transaction.transaction
solana.storage.ConfirmedBlock.Transaction
Full transaction data including message, signatures, and account keys. Note: meta field is not populated for deshred transactions.
transaction.loaded_writable_addresses
bytes[]
Account addresses loaded from address lookup tables with writable permissions. These addresses are resolved and available for filtering.
transaction.loaded_readonly_addresses
bytes[]
Account addresses loaded from address lookup tables with read-only permissions. These addresses are resolved and available for filtering.
slot
uint64
Slot number where this transaction was deshredded.

Examples

use yellowstone_grpc_client::GeyserGrpcClient;
use yellowstone_grpc_proto::prelude::{
    SubscribeDeshredRequest,
    SubscribeRequestFilterDeshredTransactions,
};
use futures::stream::StreamExt;
use std::collections::HashMap;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Connect to the gRPC endpoint
    let mut client = GeyserGrpcClient::build_from_shared("http://127.0.0.1:10000")?
        .x_token(Some("your-token-here"))?
        .connect()
        .await?;

    // Create deshred subscription request
    let mut deshred_transactions = HashMap::new();
    deshred_transactions.insert(
        "raydium_swaps".to_string(),
        SubscribeRequestFilterDeshredTransactions {
            vote: Some(false),
            account_include: vec![
                "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8".to_string(), // Raydium AMM program
            ],
            account_exclude: vec![],
            account_required: vec![],
        },
    );

    let request = SubscribeDeshredRequest {
        deshred_transactions,
        ping: None,
    };

    // Subscribe and receive deshred updates
    let (mut subscribe_tx, mut stream) = client
        .subscribe_deshred_with_request(Some(request))
        .await?;

    println!("Deshred subscription started, receiving pre-execution transactions...");
    
    while let Some(message) = stream.next().await {
        match message {
            Ok(msg) => {
                if let Some(update) = msg.update_oneof {
                    match update {
                        SubscribeUpdateDeshred::DeshredTransaction(tx_update) => {
                            let tx = tx_update.transaction.unwrap();
                            println!("Deshred transaction at slot {}:", tx_update.slot);
                            println!("  Signature: {:?}", tx.signature);
                            println!("  Is vote: {}", tx.is_vote);
                            println!("  Loaded writable addresses: {}", 
                                tx.loaded_writable_addresses.len());
                            println!("  Loaded readonly addresses: {}", 
                                tx.loaded_readonly_addresses.len());
                        }
                        SubscribeUpdateDeshred::Ping(_) => {
                            // Respond to ping to keep connection alive
                            subscribe_tx.send(SubscribeDeshredRequest {
                                ping: Some(SubscribeRequestPing { id: 1 }),
                                ..Default::default()
                            }).await?;
                        }
                        _ => {}
                    }
                }
            }
            Err(error) => {
                eprintln!("Error: {:?}", error);
                break;
            }
        }
    }

    Ok(())
}

Key Differences from Subscribe

SubscribeDeshred provides transaction data at the earliest possible point - when shreds are assembled into entries, before execution. Regular Subscribe transactions include execution results.
Deshred transactions do NOT include TransactionStatusMeta, so there are no logs, compute units consumed, error information, or account state changes. You only get the raw transaction data.
ALT addresses are fully resolved and available for filtering in deshred transactions. This makes filtering by program interactions reliable even with versioned transactions.
Deshred is optimized for latency-sensitive applications that need to react before execution completes, such as MEV detection or transaction ordering analysis.

Filtering by ALT-Loaded Addresses

One powerful feature of deshred transactions is that address lookup table (ALT) addresses are resolved and available for filtering:
// Filter for transactions that interact with a specific program,
// even if that program is referenced through an ALT
let deshred_filter = SubscribeRequestFilterDeshredTransactions {
    vote: Some(false),
    account_include: vec![
        "JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4".to_string(), // Jupiter program
    ],
    ..Default::default()
};
This will match transactions where Jupiter is referenced in:
  • Static account keys (regular transaction accounts)
  • Loaded writable addresses (from ALTs)
  • Loaded readonly addresses (from ALTs)

Latency Considerations

Deshred transactions provide the lowest latency signal:
  1. Network shreds received → Shreds arrive at validator from the network
  2. Entry assembled → Shreds are assembled into an entry
  3. 🔔 Deshred transaction emitted ← You are here (SubscribeDeshred)
  4. Transaction executed → Validator executes the transaction
  5. Regular transaction emitted ← Regular Subscribe sees it here
The time difference between steps 3 and 5 can be significant, especially for complex transactions or during high network load.

Best Practices

  • Filter out votes: Always set vote: false unless you specifically need vote transactions
  • Use specific account filters: Deshred can produce high volumes; filter to reduce bandwidth
  • Don’t expect execution results: Remember that deshred transactions haven’t executed yet
  • Handle duplicates: You may see the same transaction in both SubscribeDeshred and Subscribe
  • Combine with regular Subscribe: Use deshred for early signals, then correlate with executed transactions
  • Monitor memory: Buffer deshred transactions carefully if storing them for later correlation

Build docs developers (and LLMs) love