go
Send a bundle using Go.
Types
go
package main
type BundleParams struct {
Encoding string `json:"encoding"`
SkipPreflight bool `json:"skipPreflight"`
}
type JsonRpcRequest struct {
Jsonrpc string `json:"jsonrpc"`
ID int `json:"id"`
Method string `json:"method"`
Params []interface{} `json:"params"`
}
type BundleResult struct {
BundleID string `json:"bundle_id"`
Signatures []string `json:"signatures"`
}
type JsonRpcResponse struct {
Jsonrpc string `json:"jsonrpc"`
ID int `json:"id"`
Result *BundleResult `json:"result,omitempty"`
Error *RpcError `json:"error,omitempty"`
}
type RpcError struct {
Code int `json:"code"`
Message string `json:"message"`
}Send Bundle Function
go
package main
import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/http"
)
const ENDPOINT = "https://fra.blocksprint.io/bundle/v1/YOUR_API_KEY"
func sendBundle(transactions [][]byte) (*BundleResult, error) {
// Encode transactions to base64
encodedTxs := make([]string, len(transactions))
for i, tx := range transactions {
encodedTxs[i] = base64.StdEncoding.EncodeToString(tx)
}
request := JsonRpcRequest{
Jsonrpc: "2.0",
ID: 1,
Method: "sendBundle",
Params: []interface{}{
encodedTxs,
BundleParams{
Encoding: "base64",
SkipPreflight: true,
},
},
}
body, err := json.Marshal(request)
if err != nil {
return nil, err
}
resp, err := http.Post(
ENDPOINT,
"application/json",
bytes.NewBuffer(body),
)
if err != nil {
return nil, err
}
defer resp.Body.Close()
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
var response JsonRpcResponse
if err := json.Unmarshal(respBody, &response); err != nil {
return nil, err
}
if response.Error != nil {
return nil, fmt.Errorf("RPC error %d: %s", response.Error.Code, response.Error.Message)
}
return response.Result, nil
}Using solana-go
go
package main
import (
"context"
"fmt"
"github.com/gagliardetto/solana-go"
"github.com/gagliardetto/solana-go/programs/system"
"github.com/gagliardetto/solana-go/rpc"
)
// Your Solana RPC (for the blockhash)
const RPC_URL = "https://api.mainnet-beta.solana.com"
// BlockSprint tip address, tip at least 0.001 SOL
var TIP_ADDRESS = solana.MustPublicKeyFromBase58("Sp4JHSh9cksfzXbgK7Pq2ovtn8LirLQydaJKTsiNT77")
func main() {
client := rpc.New(RPC_URL)
ctx := context.Background()
wallet := solana.NewWallet()
recipient := solana.MustPublicKeyFromBase58("RECIPIENT")
// Get blockhash
recent, err := client.GetLatestBlockhash(ctx, rpc.CommitmentFinalized)
if err != nil {
panic(err)
}
// Transaction 1
tx1, err := solana.NewTransaction(
[]solana.Instruction{
system.NewTransferInstruction(
1_000_000,
wallet.PublicKey(),
recipient,
).Build(),
},
recent.Value.Blockhash,
solana.TransactionPayer(wallet.PublicKey()),
)
if err != nil {
panic(err)
}
tx1.Sign(func(key solana.PublicKey) *solana.PrivateKey {
if wallet.PublicKey().Equals(key) {
return &wallet.PrivateKey
}
return nil
})
// Transaction 2 (with the 0.001 SOL tip)
tx2, err := solana.NewTransaction(
[]solana.Instruction{
system.NewTransferInstruction(
2_000_000,
wallet.PublicKey(),
recipient,
).Build(),
system.NewTransferInstruction(
1_000_000,
wallet.PublicKey(),
TIP_ADDRESS,
).Build(),
},
recent.Value.Blockhash,
solana.TransactionPayer(wallet.PublicKey()),
)
if err != nil {
panic(err)
}
tx2.Sign(func(key solana.PublicKey) *solana.PrivateKey {
if wallet.PublicKey().Equals(key) {
return &wallet.PrivateKey
}
return nil
})
// Serialize
tx1Bytes, _ := tx1.MarshalBinary()
tx2Bytes, _ := tx2.MarshalBinary()
// Send bundle
result, err := sendBundle([][]byte{tx1Bytes, tx2Bytes})
if err != nil {
panic(err)
}
fmt.Printf("Bundle ID: %s\n", result.BundleID)
for i, sig := range result.Signatures {
fmt.Printf("Tx %d: %s\n", i+1, sig)
}
}With Retry Logic
go
package main
import (
"strings"
"time"
)
func sendBundleWithRetry(transactions [][]byte, maxRetries int) (*BundleResult, error) {
var lastErr error
for i := 0; i < maxRetries; i++ {
result, err := sendBundle(transactions)
if err == nil {
return result, nil
}
lastErr = err
// Check if rate limited
if strings.Contains(err.Error(), "Rate limit") {
time.Sleep(time.Duration(1<<i) * 100 * time.Millisecond)
continue
}
break
}
return nil, lastErr
}