Solana gRPC in Go — Examples
TL;DR
- - Generate Go gRPC stubs from Yellowstone protobuf definitions
- - Use bidirectional streaming to subscribe to accounts, transactions, and slots
- - Go's goroutines handle concurrent streams efficiently
Go is a natural fit for Solana gRPC streaming: its goroutine-based concurrency model handles multiple streams efficiently, and the compiled binary deploys without runtime dependencies.
Setup
Install the gRPC and protobuf tools:
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
Generate Go stubs from the Yellowstone protobuf:
git clone https://github.com/rpcpool/yellowstone-grpc
protoc --go_out=. --go-grpc_out=. proto/geyser.proto
Add dependencies:
go get google.golang.org/grpc
go get google.golang.org/grpc/credentials
Connecting to the gRPC endpoint
package main
import (
"context"
"crypto/tls"
"log"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/metadata"
pb "your-module/geyser"
)
func connect(endpoint, token string) (pb.GeyserClient, error) {
tlsCreds := credentials.NewTLS(&tls.Config{})
conn, err := grpc.Dial(endpoint, grpc.WithTransportCredentials(tlsCreds))
if err != nil {
return nil, err
}
return pb.NewGeyserClient(conn), nil
}
func authContext(ctx context.Context, token string) context.Context {
return metadata.AppendToOutgoingContext(ctx, "x-token", token)
}
Subscribe to account updates
func subscribeAccounts(client pb.GeyserClient, token, owner string) error {
ctx := authContext(context.Background(), token)
stream, err := client.Subscribe(ctx)
if err != nil {
return err
}
req := &pb.SubscribeRequest{
Accounts: map[string]*pb.SubscribeRequestFilterAccounts{
"my_accounts": {
Owner: []string{owner},
},
},
Commitment: pb.CommitmentLevel_CONFIRMED.Enum(),
}
if err := stream.Send(req); err != nil {
return err
}
for {
resp, err := stream.Recv()
if err != nil {
return err
}
if acct := resp.GetAccount(); acct != nil {
log.Printf("Account update: slot=%d pubkey=%x",
acct.Slot, acct.Account.Pubkey)
}
}
}
Subscribe to transactions
func subscribeTransactions(client pb.GeyserClient, token, programID string) error {
ctx := authContext(context.Background(), token)
stream, err := client.Subscribe(ctx)
if err != nil {
return err
}
req := &pb.SubscribeRequest{
Transactions: map[string]*pb.SubscribeRequestFilterTransactions{
"program_txs": {
AccountInclude: []string{programID},
},
},
Commitment: pb.CommitmentLevel_CONFIRMED.Enum(),
}
if err := stream.Send(req); err != nil {
return err
}
for {
resp, err := stream.Recv()
if err != nil {
return err
}
if tx := resp.GetTransaction(); tx != nil {
log.Printf("Transaction: slot=%d sig=%x",
tx.Slot, tx.Transaction.Signature)
}
}
}
Concurrent subscriptions
Go’s goroutines make it easy to run multiple subscriptions in parallel:
func main() {
client, err := connect("your-endpoint:443", "your-token")
if err != nil {
log.Fatal(err)
}
token := "your-token"
go func() {
for {
err := subscribeAccounts(client, token, "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA")
log.Printf("Account stream disconnected: %v. Reconnecting...", err)
time.Sleep(5 * time.Second)
}
}()
go func() {
for {
err := subscribeTransactions(client, token, "JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4")
log.Printf("Transaction stream disconnected: %v. Reconnecting...", err)
time.Sleep(5 * time.Second)
}
}()
select {} // block forever
}
FAQ
Is there an official Go client library? No official high-level Go SDK exists. Use the generated gRPC stubs from the Yellowstone protobuf definitions.
How do I handle backpressure? If your processing is slower than the stream, use a buffered channel between the gRPC receiver goroutine and the processing goroutine. Drop or batch updates when the buffer is full.
What Go version is required?
Go 1.21+ is recommended for the latest gRPC library features. The generated protobuf code requires the google.golang.org/protobuf module (not the deprecated github.com/golang/protobuf).
Updated: