# Submit single transactions

This example shows how to submit a single transaction to NextBlock using the gRPC API. We'll create a transaction with a tip and a useful instruction.

If you want to send raw signed transaction bytes over QUIC instead of gRPC, see [QUIC Transaction Submission](/api/examples/golang/quic.md).

This example shows transaction 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]
}

// Calculate optimal tip based on priority and current tip floor
func calculateOptimalTip(priority string) uint64 {
    // This should get tip from your tip floor stream
    // Higher tips = higher priority
    // Adapt based on current network conditions
    
    // Get current tip floor data from your streaming connection
    // tipFloor := getCurrentTipFloor()
    
    // Use tip floor percentiles to determine appropriate tip
    // Example: use 25th percentile for low priority, 50th for normal, etc.
    // Return the calculated tip based on current network conditions
    
    // For now, return 0 - you must implement tip floor integration
    return 0 // Replace with actual tip floor calculation
}

// BuildAndSubmitTransaction creates and submits a transaction with proper tip
func BuildAndSubmitTransaction(
    nextblockApiClient interface{}, // Replace with your generated API client type
    rpcClient *rpc.Client,
    signerPrivateKey solana.PrivateKey,
    recipientPubkey solana.PublicKey,
    transferAmount uint64,
    tipAmount uint64,
) error {
    // Get latest blockhash for transaction
    bh, err := rpcClient.GetLatestBlockhash(context.TODO(), rpc.CommitmentFinalized)
    if err != nil {
        return fmt.Errorf("failed to get latest blockhash: %w", err)
    }

    // Build transaction
    txBuilder := solana.NewTransactionBuilder()
    txBuilder.SetFeePayer(signerPrivateKey.PublicKey())
    txBuilder.SetRecentBlockHash(bh.Value.Blockhash)

    // Add tip instruction (IMPORTANT: This helps prioritize your transaction)
    tipInstruction := system.NewTransferInstruction(
        tipAmount,
        signerPrivateKey.PublicKey(),
        GetRandomNextblockTipWallet(),
    ).Build()
    txBuilder.AddInstruction(tipInstruction)

    // Add your main instruction (replace with your actual business logic)
    transferInstruction := system.NewTransferInstruction(
        transferAmount,
        signerPrivateKey.PublicKey(),
        recipientPubkey,
    ).Build()
    txBuilder.AddInstruction(transferInstruction)

    // Build and sign transaction
    tx, err := txBuilder.Build()
    if err != nil {
        return 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 fmt.Errorf("failed to sign transaction: %w", err)
    }

    // Convert to base64 for submission
    base64EncodedTransaction := tx.ToBase64()

    // Configure submission options
    frontRunningProtection := false // Set to true to protect from MEV
    revertOnFail := false          // Set to true to revert if transaction fails
    disableRetries := false        // Set to true to disable automatic retries
    snipeTransaction := false      // Set to true for high-priority submission

    // Submit transaction to NextBlock
    /* Uncomment when you have the generated API client
    submitResponse, err := nextblockApiClient.PostSubmitV2(context.TODO(), &api.PostSubmitRequest{
        Transaction:            &api.TransactionMessage{Content: base64EncodedTransaction},
        SkipPreFlight:          true, // Skip preflight checks for faster submission
        SnipeTransaction:       &snipeTransaction,
        FrontRunningProtection: &frontRunningProtection,
        DisableRetries:         &disableRetries,
        RevertOnFail:          &revertOnFail,
    })
    if err != nil {
        return fmt.Errorf("failed to submit transaction: %w", err)
    }

    fmt.Printf("Transaction submitted successfully!\n")
    fmt.Printf("Signature: %s\n", submitResponse.Signature)
    fmt.Printf("UUID: %s\n", submitResponse.Uuid)
    */

    fmt.Printf("Local transaction built successfully: %s\n", tx.Signatures[0].String())
    return nil
}

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

    // Set up your configuration
    signerPrivateKey := solana.MustPrivateKeyFromBase58("your-private-key-here")
    recipientPubkey := solana.MustPublicKeyFromBase58("recipient-public-key-here")
    rpcEndpoint := "https://api.mainnet-beta.solana.com" // Use your preferred RPC
    
    // 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 transaction with dynamic tip based on priority
    err := BuildAndSubmitTransaction(
        nil, // nextblockApiClient,
        rpcClient,
        signerPrivateKey,
        recipientPubkey,
        10000,    // Transfer amount
        calculateOptimalTip("normal"),  // Dynamic tip based on tip floor
    )
    if err != nil {
        log.Fatal(err)
    }
}
```

## Key Features Explained

### Tip Amount Calculation

Use the tip floor API to determine appropriate tip amounts:

```go
// Get current tip floor data (see tip-floor-stream.md for streaming version)
func GetOptimalTipAmount(nextblockApiClient interface{}) uint64 {
    // This would call the tip floor API to get current percentiles
    // For now, use a reasonable default
    return 1000000 // 0.001 SOL
}
```

### Transaction Options

* **SkipPreFlight**: Set to `true` for faster submission (recommended)
* **FrontRunningProtection**: Protects against MEV attacks
* **DisableRetries**: Disable automatic retries if you handle them yourself
* **RevertOnFail**: Revert transaction state if it fails
* **SnipeTransaction**: High-priority submission mode

### Error Handling

Always implement proper error handling:

```go
if err != nil {
    // Log the error with context
    log.Printf("Transaction submission failed: %v", err)
    
    // Implement retry logic if needed
    // Check if error is retryable
    // Wait and retry with exponential backoff
}
```

### Best Practices

1. **Always include tips**: NextBlock prioritizes transactions based on tip amounts - higher tips = higher priority
2. **Use random tip wallets**: Distribute load across multiple tip addresses
3. **Adapt tips to network conditions**: Use the tip floor streaming API to dynamically adjust your tips based on current network conditions
4. **Choose appropriate priority levels**: Use different tip levels based on your transaction urgency needs
5. **Handle errors gracefully**: Implement retry logic for network issues
6. **Use appropriate RPC endpoints**: Choose reliable RPC providers for blockhash retrieval
7. **Keep connections alive**: Reuse gRPC connections for better performance


---

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