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.
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
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:
Transaction Options
SkipPreFlight: Set to
truefor 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:
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