# Submit Batched Transactions

Submit 2-4 transactions as an atomic bundle to NextBlock using Rust. Batched transactions are processed as Jito bundles - either all transactions succeed, or none do.

This example shows bundle construction and the request shape you send to NextBlock. Replace the placeholder generated client types with the client generated from `nextblock-proto`.

## Example

<pre class="language-rust"><code class="lang-rust"><strong>use solana_sdk::{
</strong>    instruction::Instruction,
    message::Message,
    pubkey::Pubkey,
    signature::{Keypair, Signature, Signer},
    system_instruction,
    transaction::Transaction,
    hash::Hash,
};
use solana_client::rpc_client::RpcClient;
use rand::seq::SliceRandom;
use std::str::FromStr;

<strong>// NextBlock tip wallets
</strong>const NEXTBLOCK_TIP_WALLETS: [&#x26;str; 8] = [
    "NEXTbLoCkB51HpLBLojQfpyVAMorm3zzKg7w9NFdqid",
    "nextBLoCkPMgmG8ZgJtABeScP35qLa2AMCNKntAP7Xc",
    "NextbLoCkVtMGcV47JzewQdvBpLqT9TxQFozQkN98pE", 
    "NexTbLoCkWykbLuB1NkjXgFWkX9oAtcoagQegygXXA2",
    "NeXTBLoCKs9F1y5PJS9CKrFNNLU1keHW71rfh7KgA1X",
    "NexTBLockJYZ7QD7p2byrUa6df8ndV2WSd8GkbWqfbb",
    "neXtBLock1LeC67jYd1QdAa32kbVeubsfPNTJC1V5At",
    "nEXTBLockYgngeRmRrjDV31mGSekVPqZoMGhQEZtPVG",
];

<strong>// Transaction builder for bundle
</strong>struct TransactionBundle {
    transactions: Vec&#x3C;Transaction>,
}

impl TransactionBundle {
    fn new() -> Self {
        Self {
            transactions: Vec::new(),
        }
    }
    
<strong>    // Add transaction to bundle
</strong>    fn add_transaction(&#x26;mut self, transaction: Transaction) -> Self {
        self.transactions.push(transaction);
        self
    }
    
<strong>    // Get transactions as base64 strings
</strong>    fn to_base64_transactions(&#x26;self) -> Result&#x3C;Vec&#x3C;String>, Box&#x3C;dyn std::error::Error>> {
        self.transactions
            .iter()
            .map(|tx| {
                let serialized = bincode::serialize(tx)?;
                Ok(base64::encode(serialized))
            })
            .collect()
    }
}

<strong>// Build single transaction with tip
</strong>fn build_transaction_with_tip(
    payer: &#x26;Keypair,
    recent_blockhash: Hash,
    tip_amount: u64,
    instructions: Vec&#x3C;Instruction>,
) -> Result&#x3C;Transaction, Box&#x3C;dyn std::error::Error>> {
    let mut rng = rand::thread_rng();
    let tip_wallet_str = NEXTBLOCK_TIP_WALLETS.choose(&#x26;mut rng)
        .ok_or("No tip wallets available")?;
    let tip_wallet = Pubkey::from_str(tip_wallet_str)?;
    
<strong>    // Create tip instruction (should be first)
</strong>    let tip_instruction = system_instruction::transfer(
        &#x26;payer.pubkey(),
        &#x26;tip_wallet,
        tip_amount,
    );
    
<strong>    // Combine instructions
</strong>    let mut all_instructions = vec![tip_instruction];
    all_instructions.extend(instructions);
    
<strong>    // Build and sign transaction
</strong>    let message = Message::new(&#x26;all_instructions, Some(&#x26;payer.pubkey()));
    let mut transaction = Transaction::new_unsigned(message);
    transaction.sign(&#x26;[payer], recent_blockhash);
    
    Ok(transaction)
}

<strong>// Submit batched transactions
</strong>async fn submit_batched_transactions(
    // nextblock_client: &#x26;mut YourGeneratedApiClient,
    rpc_client: &#x26;RpcClient,
    signer: &#x26;Keypair,
) -> Result&#x3C;String, Box&#x3C;dyn std::error::Error>> {
<strong>    // Get recent blockhash (same for all transactions in bundle)
</strong>    let recent_blockhash = rpc_client.get_latest_blockhash()?;
    
<strong>    // Build transaction bundle
</strong>    let mut bundle = TransactionBundle::new();
    
<strong>    // Transaction 1: Setup transaction
</strong>    let setup_instructions = vec![
        system_instruction::transfer(
            &#x26;signer.pubkey(),
            &#x26;Pubkey::from_str("&#x3C;recipient1-public-key>")?,
            100_000, // 0.0001 SOL
        ),
    ];
    
    let setup_tx = build_transaction_with_tip(
        signer,
        recent_blockhash,
        500_000, // 0.0005 SOL tip
        setup_instructions,
    )?;
    bundle = bundle.add_transaction(setup_tx);
    
<strong>    // Transaction 2: Main operation
</strong>    let main_instructions = vec![
        system_instruction::transfer(
            &#x26;signer.pubkey(),
            &#x26;Pubkey::from_str("&#x3C;recipient2-public-key>")?,
            200_000, // 0.0002 SOL
        ),
    ];
    
    let main_tx = build_transaction_with_tip(
        signer,
        recent_blockhash,
        1_000_000, // 0.001 SOL tip (higher for main transaction)
        main_instructions,
    )?;
    bundle = bundle.add_transaction(main_tx);
    
<strong>    // Transaction 3: Cleanup transaction
</strong>    let cleanup_instructions = vec![
        system_instruction::transfer(
            &#x26;signer.pubkey(),
            &#x26;Pubkey::from_str("&#x3C;recipient3-public-key>")?,
            50_000, // 0.00005 SOL
        ),
    ];
    
    let cleanup_tx = build_transaction_with_tip(
        signer,
        recent_blockhash,
        500_000, // 0.0005 SOL tip
        cleanup_instructions,
    )?;
    bundle = bundle.add_transaction(cleanup_tx);
    
<strong>    // Convert to base64 for submission
</strong>    let base64_transactions = bundle.to_base64_transactions()?;
    
<strong>    // Print transaction signatures for tracking
</strong>    for (i, tx) in bundle.transactions.iter().enumerate() {
        println!("Transaction {} signature: {}", i + 1, tx.signatures[0]);
    }
    
<strong>    // Submit bundle to NextBlock
</strong>    /* Uncomment when you have the generated API client
    let entries: Vec&#x3C;PostSubmitRequestEntry> = base64_transactions
        .into_iter()
        .map(|tx_base64| PostSubmitRequestEntry {
            transaction: Some(TransactionMessage {
                content: tx_base64,
            }),
        })
        .collect();
    
    let request = tonic::Request::new(PostSubmitBatchRequest {
        entries,
    });
    
    let response = nextblock_client.post_submit_batch_v2(request).await?;
    let submit_response = response.into_inner();
    
    println!("Batch submitted successfully!");
    println!("Bundle signature: {}", submit_response.signature);
    
    Ok(submit_response.signature)
    */
    
    println!("Local bundle prepared with {} transactions.", bundle.transactions.len());
    Ok("".to_string())
}
</code></pre>

## Arbitrage Bundle Example

<pre class="language-rust"><code class="lang-rust"><strong>// Example: Build arbitrage bundle
</strong>async fn build_arbitrage_bundle(
    // nextblock_client: &#x26;mut YourGeneratedApiClient,
    rpc_client: &#x26;RpcClient,
    signer: &#x26;Keypair,
    dex_a_address: Pubkey,
    dex_b_address: Pubkey,
    trade_amount: u64,
) -> Result&#x3C;String, Box&#x3C;dyn std::error::Error>> {
    let recent_blockhash = rpc_client.get_latest_blockhash()?;
    let mut bundle = TransactionBundle::new();
    
<strong>    // Transaction 1: Buy on DEX A
</strong>    let buy_instructions = vec![
<strong>        // Add your DEX-specific buy instructions here
</strong>        system_instruction::transfer(
            &#x26;signer.pubkey(),
            &#x26;dex_a_address,
            trade_amount,
        ),
    ];
    
    let buy_tx = build_transaction_with_tip(
        signer,
        recent_blockhash,
        2_000_000, // Higher tip for arbitrage
        buy_instructions,
    )?;
    bundle = bundle.add_transaction(buy_tx);
    
<strong>    // Transaction 2: Sell on DEX B
</strong>    let sell_instructions = vec![
<strong>        // Add your DEX-specific sell instructions here
</strong>        system_instruction::transfer(
            &#x26;signer.pubkey(),
            &#x26;dex_b_address,
            trade_amount,
        ),
    ];
    
    let sell_tx = build_transaction_with_tip(
        signer,
        recent_blockhash,
        2_000_000, // Higher tip for arbitrage
        sell_instructions,
    )?;
    bundle = bundle.add_transaction(sell_tx);
    
    println!("Arbitrage bundle prepared with {} transactions", bundle.transactions.len());
    
<strong>    // Submit the bundle
</strong>    // Implementation similar to submit_batched_transactions
    Ok("".to_string())
}
</code></pre>

## Complex DeFi Bundle Example

<pre class="language-rust"><code class="lang-rust"><strong>// Example: Complex DeFi operation bundle
</strong>async fn build_defi_operation_bundle(
    // nextblock_client: &#x26;mut YourGeneratedApiClient,
    rpc_client: &#x26;RpcClient,
    signer: &#x26;Keypair,
) -> Result&#x3C;String, Box&#x3C;dyn std::error::Error>> {
    let recent_blockhash = rpc_client.get_latest_blockhash()?;
    let mut bundle = TransactionBundle::new();
    
<strong>    // Transaction 1: Create token accounts
</strong>    let account_creation_instructions = vec![
<strong>        // Add token account creation instructions
</strong>        system_instruction::create_account(
            &#x26;signer.pubkey(),
            &#x26;Keypair::new().pubkey(), // New account
            1_000_000, // Rent exemption
            165,       // Token account space
            &#x26;spl_token::id(),
        ),
    ];
    
    let account_tx = build_transaction_with_tip(
        signer,
        recent_blockhash,
        500_000,
        account_creation_instructions,
    )?;
    bundle = bundle.add_transaction(account_tx);
    
<strong>    // Transaction 2: Execute swap
</strong>    let swap_instructions = vec![
<strong>        // Add swap instructions (Jupiter, Raydium, etc.)
</strong>        system_instruction::transfer(
            &#x26;signer.pubkey(),
            &#x26;Pubkey::from_str("&#x3C;swap-program-address>")?,
            0, // No SOL transfer for swap
        ),
    ];
    
    let swap_tx = build_transaction_with_tip(
        signer,
        recent_blockhash,
        1_500_000, // Higher tip for main operation
        swap_instructions,
    )?;
    bundle = bundle.add_transaction(swap_tx);
    
<strong>    // Transaction 3: Stake or provide liquidity
</strong>    let stake_instructions = vec![
<strong>        // Add staking/liquidity instructions
</strong>        system_instruction::transfer(
            &#x26;signer.pubkey(),
            &#x26;Pubkey::from_str("&#x3C;stake-pool-address>")?,
            0,
        ),
    ];
    
    let stake_tx = build_transaction_with_tip(
        signer,
        recent_blockhash,
        750_000,
        stake_instructions,
    )?;
    bundle = bundle.add_transaction(stake_tx);
    
    println!("DeFi operation bundle prepared with {} transactions", bundle.transactions.len());
    Ok("".to_string())
}
</code></pre>

## Usage Example

<pre class="language-rust"><code class="lang-rust"><strong>#[tokio::main]
</strong>async fn main() -> Result&#x3C;(), Box&#x3C;dyn std::error::Error>> {
<strong>    // Initialize configuration
</strong>    let signer = Keypair::new(); // Use your actual keypair
    let rpc_client = RpcClient::new("https://api.mainnet-beta.solana.com".to_string());
    
<strong>    // Connect to NextBlock (see connection.md)
</strong>    // let config = NextBlockConfig::default();
    // let mut nextblock_client = create_nextblock_client(config).await?;
    
<strong>    // Submit basic batch
</strong>    let bundle_signature = submit_batched_transactions(
        // &#x26;mut nextblock_client,
        &#x26;rpc_client,
        &#x26;signer,
    ).await?;
    
    println!("Batch submitted with signature: {}", bundle_signature);
    
<strong>    // Submit arbitrage bundle
</strong>    let arb_signature = build_arbitrage_bundle(
        // &#x26;mut nextblock_client,
        &#x26;rpc_client,
        &#x26;signer,
        Pubkey::from_str("&#x3C;dex-a-address>")?,
        Pubkey::from_str("&#x3C;dex-b-address>")?,
        1_000_000, // 0.001 SOL
    ).await?;
    
    println!("Arbitrage bundle: {}", arb_signature);
    
<strong>    // Submit complex DeFi bundle
</strong>    let defi_signature = build_defi_operation_bundle(
        // &#x26;mut nextblock_client,
        &#x26;rpc_client,
        &#x26;signer,
    ).await?;
    
    println!("DeFi bundle: {}", defi_signature);
    
    Ok(())
}
</code></pre>

## Bundle Best Practices

### Size and Composition

* **Bundle size**: 2-4 transactions (optimal: 2-3)
* **Transaction order**: Setup → Main operations → Cleanup
* **Compute limits**: Stay within bundle compute unit limits

### Tip Strategy

* **Progressive tipping**: Higher tips for more critical transactions
* **Bundle coherence**: All transactions should have meaningful tips
* **Market conditions**: Adjust tips based on network congestion

### Error Handling

<pre class="language-rust"><code class="lang-rust"><strong>// Validate bundle before submission
</strong>fn validate_bundle(bundle: &#x26;TransactionBundle) -> Result&#x3C;(), &#x26;'static str> {
    if bundle.transactions.is_empty() {
        return Err("Bundle cannot be empty");
    }
    
    if bundle.transactions.len() > 4 {
        return Err("Bundle cannot contain more than 4 transactions");
    }
    
    if bundle.transactions.len() &#x3C; 2 {
        return Err("Bundle must contain at least 2 transactions");
    }
    
<strong>    // Check for signature duplicates
</strong>    let mut signatures = std::collections::HashSet::new();
    for tx in &#x26;bundle.transactions {
        if !signatures.insert(tx.signatures[0]) {
            return Err("Duplicate transaction signatures in bundle");
        }
    }
    
    Ok(())
}
</code></pre>

## Common Use Cases

1. **Arbitrage**: Multi-DEX price differences
2. **DeFi operations**: Account setup → Trade → Stake
3. **NFT operations**: Create → Mint → Transfer → List
4. **Token operations**: Create mint → Create accounts → Transfer
5. **Liquidations**: Detect → Execute → Collect rewards

## Bundle Failure Prevention

* **Account validation**: Ensure all accounts exist and have sufficient balance
* **Instruction compatibility**: Avoid conflicting program interactions
* **Blockhash freshness**: Use recent blockhashes (valid \~60 seconds)
* **Compute budget**: Monitor total compute units across all transactions


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.nextblock.io/api/examples/rust/submit-batched-transactions.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
