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.
Example
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("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("fra.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:
// 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:
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
Always include tips: NextBlock prioritizes transactions based on tip amounts - higher tips = higher priority
Use random tip wallets: Distribute load across multiple tip addresses
Adapt tips to network conditions: Use the tip floor streaming API to dynamically adjust your tips based on current network conditions
Choose appropriate priority levels: Use different tip levels based on your transaction urgency needs
Handle errors gracefully: Implement retry logic for network issues
Use appropriate RPC endpoints: Choose reliable RPC providers for blockhash retrieval
Keep connections alive: Reuse gRPC connections for better performance
Last updated