# Submit batched transactions

Submit 2-4 transactions as an atomic bundle to NextBlock. Batched transactions are processed as Jito bundles, meaning either all transactions succeed and land, or none of them 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

```go
package main

import (
    "context"
    "fmt"
    "log"
    "math/rand"
    "time"
    
    "github.com/gagliardetto/solana-go"
    "github.com/gagliardetto/solana-go/programs/system"
    "github.com/gagliardetto/solana-go/rpc"
    // Import your generated proto client
    // "path/to/your/generated/api"
)

// NextBlock tip wallets - use these for tipping
var NextblockTipWallets = []solana.PublicKey{
    solana.MustPublicKeyFromBase58("NEXTbLoCkB51HpLBLojQfpyVAMorm3zzKg7w9NFdqid"),
    solana.MustPublicKeyFromBase58("nextBLoCkPMgmG8ZgJtABeScP35qLa2AMCNKntAP7Xc"),
    solana.MustPublicKeyFromBase58("NextbLoCkVtMGcV47JzewQdvBpLqT9TxQFozQkN98pE"),
    solana.MustPublicKeyFromBase58("NexTbLoCkWykbLuB1NkjXgFWkX9oAtcoagQegygXXA2"),
    solana.MustPublicKeyFromBase58("NeXTBLoCKs9F1y5PJS9CKrFNNLU1keHW71rfh7KgA1X"),
    solana.MustPublicKeyFromBase58("NexTBLockJYZ7QD7p2byrUa6df8ndV2WSd8GkbWqfbb"),
    solana.MustPublicKeyFromBase58("neXtBLock1LeC67jYd1QdAa32kbVeubsfPNTJC1V5At"),
    solana.MustPublicKeyFromBase58("nEXTBLockYgngeRmRrjDV31mGSekVPqZoMGhQEZtPVG"),
}

// GetRandomNextblockTipWallet returns a random tip wallet for load balancing
func GetRandomNextblockTipWallet() solana.PublicKey {
    randVal := rand.IntN(len(NextblockTipWallets))
    return NextblockTipWallets[randVal]
}

// BuildTransaction creates a single transaction with tip and instructions
func BuildTransaction(
    rpcClient *rpc.Client,
    signerPrivateKey solana.PrivateKey,
    blockhash solana.Hash,
    tipAmount uint64,
    instructions []solana.Instruction,
) (*solana.Transaction, error) {
    txBuilder := solana.NewTransactionBuilder()
    txBuilder.SetFeePayer(signerPrivateKey.PublicKey())
    txBuilder.SetRecentBlockHash(blockhash)

    // Add tip instruction first (recommended practice)
    tipInstruction := system.NewTransferInstruction(
        tipAmount,
        signerPrivateKey.PublicKey(),
        GetRandomNextblockTipWallet(),
    ).Build()
    txBuilder.AddInstruction(tipInstruction)

    // Add all provided instructions
    for _, instruction := range instructions {
        txBuilder.AddInstruction(instruction)
    }

    // Build and sign transaction
    tx, err := txBuilder.Build()
    if err != nil {
        return nil, fmt.Errorf("failed to build transaction: %w", err)
    }

    _, err = tx.Sign(func(key solana.PublicKey) *solana.PrivateKey {
        if key.Equals(signerPrivateKey.PublicKey()) {
            return &signerPrivateKey
        }
        return nil
    })
    if err != nil {
        return nil, fmt.Errorf("failed to sign transaction: %w", err)
    }

    return tx, nil
}

// BuildAndSubmitBatchTransactions creates and submits multiple transactions as a bundle
func BuildAndSubmitBatchTransactions(
    nextblockApiClient interface{}, // Replace with your generated API client type
    rpcClient *rpc.Client,
    signerPrivateKey solana.PrivateKey,
) error {
    // Get latest blockhash (same for all transactions in bundle)
    bh, err := rpcClient.GetLatestBlockhash(context.TODO(), rpc.CommitmentFinalized)
    if err != nil {
        return fmt.Errorf("failed to get latest blockhash: %w", err)
    }

    var transactions []*solana.Transaction
    
    // Transaction 1: Setup transaction
    setupInstructions := []solana.Instruction{
        system.NewTransferInstruction(
            100000, // 0.0001 SOL
            signerPrivateKey.PublicKey(),
            solana.MustPublicKeyFromBase58("recipient1-public-key-here"),
        ).Build(),
    }
    
    tx1, err := BuildTransaction(
        rpcClient,
        signerPrivateKey,
        bh.Value.Blockhash,
        500000, // 0.0005 SOL tip
        setupInstructions,
    )
    if err != nil {
        return fmt.Errorf("failed to build transaction 1: %w", err)
    }
    transactions = append(transactions, tx1)

    // Transaction 2: Main operation
    mainInstructions := []solana.Instruction{
        system.NewTransferInstruction(
            200000, // 0.0002 SOL
            signerPrivateKey.PublicKey(),
            solana.MustPublicKeyFromBase58("recipient2-public-key-here"),
        ).Build(),
    }
    
    tx2, err := BuildTransaction(
        rpcClient,
        signerPrivateKey,
        bh.Value.Blockhash,
        1000000, // 0.001 SOL tip (higher for main transaction)
        mainInstructions,
    )
    if err != nil {
        return fmt.Errorf("failed to build transaction 2: %w", err)
    }
    transactions = append(transactions, tx2)

    // Transaction 3: Cleanup transaction (optional)
    cleanupInstructions := []solana.Instruction{
        system.NewTransferInstruction(
            50000, // 0.00005 SOL
            signerPrivateKey.PublicKey(),
            solana.MustPublicKeyFromBase58("recipient3-public-key-here"),
        ).Build(),
    }
    
    tx3, err := BuildTransaction(
        rpcClient,
        signerPrivateKey,
        bh.Value.Blockhash,
        500000, // 0.0005 SOL tip
        cleanupInstructions,
    )
    if err != nil {
        return fmt.Errorf("failed to build transaction 3: %w", err)
    }
    transactions = append(transactions, tx3)

    // Convert transactions to base64 for submission
    var entries []*interface{} // Replace with your generated API types
    
    for i, tx := range transactions {
        base64EncodedTransaction := tx.ToBase64()
        fmt.Printf("Transaction %d signature: %s\n", i+1, tx.Signatures[0].String())
        
        // Create entry for batch submission
        /* Uncomment when you have the generated API client
        entry := &api.PostSubmitRequestEntry{
            Transaction: &api.TransactionMessage{
                Content: base64EncodedTransaction,
            },
        }
        entries = append(entries, entry)
        */
        _ = base64EncodedTransaction // Placeholder to avoid unused variable error
    }

    // Submit batch to NextBlock
    /* Uncomment when you have the generated API client
    submitResponse, err := nextblockApiClient.PostSubmitBatchV2(context.TODO(), &api.PostSubmitBatchRequest{
        Entries: entries,
    })
    if err != nil {
        return fmt.Errorf("failed to submit batch: %w", err)
    }

    fmt.Printf("Batch submitted successfully!\n")
    fmt.Printf("Bundle signature: %s\n", submitResponse.Signature)
    */

    fmt.Printf("Local bundle prepared with %d transactions.\n", len(transactions))
    return nil
}

// Example: Arbitrage Bundle
func BuildArbitrageBundleExample(
    nextblockApiClient interface{},
    rpcClient *rpc.Client,
    signerPrivateKey solana.PrivateKey,
) error {
    // Get blockhash
    bh, err := rpcClient.GetLatestBlockhash(context.TODO(), rpc.CommitmentFinalized)
    if err != nil {
        return fmt.Errorf("failed to get latest blockhash: %w", err)
    }

    var transactions []*solana.Transaction

    // Transaction 1: Buy on DEX A
    buyInstructions := []solana.Instruction{
        // Add your DEX buy instructions here
        system.NewTransferInstruction(
            1000000, // Placeholder instruction
            signerPrivateKey.PublicKey(),
            solana.MustPublicKeyFromBase58("dex-a-address"),
        ).Build(),
    }
    
    buyTx, err := BuildTransaction(
        rpcClient,
        signerPrivateKey,
        bh.Value.Blockhash,
        2000000, // Higher tip for arbitrage
        buyInstructions,
    )
    if err != nil {
        return err
    }
    transactions = append(transactions, buyTx)

    // Transaction 2: Sell on DEX B
    sellInstructions := []solana.Instruction{
        // Add your DEX sell instructions here
        system.NewTransferInstruction(
            1000000, // Placeholder instruction
            signerPrivateKey.PublicKey(),
            solana.MustPublicKeyFromBase58("dex-b-address"),
        ).Build(),
    }
    
    sellTx, err := BuildTransaction(
        rpcClient,
        signerPrivateKey,
        bh.Value.Blockhash,
        2000000, // Higher tip for arbitrage
        sellInstructions,
    )
    if err != nil {
        return err
    }
    transactions = append(transactions, sellTx)

    fmt.Printf("Arbitrage bundle with %d transactions prepared\n", len(transactions))
    // Submit logic similar to above...
    return nil
}

func main() {
    // Initialize random seed
    rand.Seed(time.Now().UnixNano())

    // Configuration
    signerPrivateKey := solana.MustPrivateKeyFromBase58("your-private-key-here")
    rpcEndpoint := "https://api.mainnet-beta.solana.com"
    
    // Initialize RPC client
    rpcClient := rpc.New(rpcEndpoint)

    // Connect to NextBlock (see connection.md for full setup)
    // conn, err := ConnectToNextblock("frankfurt.nextblock.io:443", "your-api-key", true)
    // if err != nil {
    //     log.Fatal(err)
    // }
    // defer conn.Close()
    // nextblockApiClient := api.NewApiClient(conn)

    // Submit batch transactions
    err := BuildAndSubmitBatchTransactions(
        nil, // nextblockApiClient,
        rpcClient,
        signerPrivateKey,
    )
    if err != nil {
        log.Fatal(err)
    }

    // Example: Submit arbitrage bundle
    err = BuildArbitrageBundleExample(
        nil, // nextblockApiClient,
        rpcClient,
        signerPrivateKey,
    )
    if err != nil {
        log.Fatal(err)
    }
}
```

## Batch Transaction Best Practices

### Bundle Size Limits

* **Minimum**: 2 transactions
* **Maximum**: 4 transactions
* **Optimal**: 2-3 transactions for better success rates

### Transaction Ordering

1. **Setup transactions**: Account creation, token account setup
2. **Main transactions**: Core business logic
3. **Cleanup transactions**: Close accounts, collect fees

### Tip Strategy for Bundles

* **Higher tips**: Use higher tips for atomic operations like arbitrage
* **Distributed tips**: Each transaction should have its own tip
* **Progressive tipping**: Increase tips for more important transactions

### Error Handling

```go
// Check if bundle was included atomically
func CheckBundleStatus(signature string) {
    // Implement bundle status checking
    // All transactions should have the same block slot if successful
}
```

### Common Use Cases

1. **Arbitrage**: Buy low on one DEX, sell high on another
2. **Complex DeFi operations**: Setup → Trade → Cleanup
3. **NFT operations**: Create → Mint → Transfer
4. **Token operations**: Create mint → Create accounts → Transfer

### Bundle Failure Scenarios

* **Insufficient balance**: Ensure all accounts have enough SOL
* **Account conflicts**: Avoid write conflicts between transactions
* **Instruction limits**: Keep total compute units under limits
* **Blockhash expiry**: Use fresh blockhashes (valid for \~60 seconds)


---

# 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/golang/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.
