Solana Yellowstone gRPC in Go.
Six steps from an empty go.mod to a production Solana streaming bot running on grpc-go, with reconnect handling and a worker pool. Works against any Yellowstone-compatible endpoint — Subglow, Triton, QuickNode, Helius Laserstream-compat.
1. Minimum viable client
Generate Go stubs from yellowstone.proto once, then wire them up against grpc.subglow.io:443. Here's the shortest end-to-end Pump.fun subscription:
package main
import (
"context"
"crypto/tls"
"log"
pb "github.com/your-org/yellowstone-go/pb"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/metadata"
)
func main() {
// 1. TLS + connect
creds := credentials.NewTLS(&tls.Config{})
conn, err := grpc.NewClient(
"grpc.subglow.io:443",
grpc.WithTransportCredentials(creds),
)
if err != nil {
log.Fatalf("dial: %v", err)
}
defer conn.Close()
// 2. API key via metadata
ctx := metadata.AppendToOutgoingContext(
context.Background(),
"x-token", "YOUR_SUBGLOW_API_KEY",
)
client := pb.NewGeyserClient(conn)
stream, err := client.Subscribe(ctx)
if err != nil {
log.Fatalf("subscribe: %v", err)
}
// 3. Send a filter — Pump.fun program transactions only
req := &pb.SubscribeRequest{
Transactions: map[string]*pb.SubscribeRequestFilterTransactions{
"pumpfun": {
AccountInclude: []string{"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"},
Vote: boolPtr(false),
Failed: boolPtr(false),
},
},
}
if err := stream.Send(req); err != nil {
log.Fatalf("send filter: %v", err)
}
// 4. Consume
for {
msg, err := stream.Recv()
if err != nil {
log.Fatalf("recv: %v", err)
}
if tx := msg.GetTransaction(); tx != nil {
log.Printf("slot=%d sig=%x", tx.Slot, tx.Transaction.Signature)
}
}
}
func boolPtr(b bool) *bool { return &b }That's it — every Pump.fun transaction streams into your process. Latency on Subglow: 30–80ms from slot confirmation to stream.Recv().
2. Production reconnect loop
Solana slots are fast and validators fail over. A naive client crashes on the first disconnect. Wrap everything in a backoff loop and pass a context for graceful shutdown:
func runWithReconnect(ctx context.Context) {
backoff := 500 * time.Millisecond
for ctx.Err() == nil {
if err := runOnce(ctx); err != nil {
log.Printf("stream err, reconnecting in %v: %v", backoff, err)
time.Sleep(backoff)
backoff = min(backoff*2, 30*time.Second)
continue
}
backoff = 500 * time.Millisecond
}
}3. Worker pool for high throughput
Never run decode and trading logic synchronously in the gRPC receive loop — that's how Go bots drop messages under load. Push raw messages to a buffered channel, let a pool of goroutines process them, drop (and count) if the buffer fills:
type txMsg struct {
Slot uint64
Sig []byte
Data []byte
}
func startWorkers(n int, in <-chan *txMsg) {
for i := 0; i < n; i++ {
go func(id int) {
for m := range in {
processTx(m) // decode + business logic here
}
}(i)
}
}
// In the gRPC receive loop:
// select {
// case workCh <- msg:
// default:
// droppedCounter.Inc() // never block the stream
// }Six-step cheatsheet
- 1Generate the Go stubs
Clone the yellowstone-grpc repo's proto directory, run protoc with protoc-gen-go and protoc-gen-go-grpc against yellowstone.proto. Commit the generated *.pb.go files to your repo.
- 2Install grpc-go
go get google.golang.org/grpc@latest google.golang.org/grpc/credentials. Go 1.22+ is the minimum we recommend (for context support in the latest grpc-go releases).
- 3Dial the endpoint with TLS
Use grpc.NewClient with credentials.NewTLS — every production Yellowstone endpoint (including Subglow) requires TLS. Localhost dev endpoints may use grpc.WithInsecure().
- 4Send your filter
Build a SubscribeRequest with transactions, accounts, or slot filters and call stream.Send(req). You can update filters at any time by sending a new request on the same stream.
- 5Consume in a worker pool
Never block the gRPC receive loop with business logic. Push raw messages to a buffered channel and let a pool of goroutines handle decoding and trading decisions.
- 6Wrap everything in a reconnect loop
Wrap the subscribe + consume logic in a for-loop with exponential backoff. Track the last processed slot and include from_slot on reconnect to avoid gaps.
FAQ
Is there an official Go client for Yellowstone gRPC?
There is no official Triton-One-maintained Go client library, but generating a Go client from the public yellowstone-grpc .proto files is a ~60-second step using protoc with the grpc-go plugin. The generated stubs give you the full type-safe API. Most Solana bot teams running Go (Jito-style arb bots, liquidators) go this route.
Which gRPC Go library should I use — google.golang.org/grpc or connectrpc?
Use google.golang.org/grpc (grpc-go). Yellowstone is a bidirectional streaming service and grpc-go has the most mature support for streaming on Go. Connect-RPC is excellent for request/response APIs but streaming support is less battle-tested. Every Solana gRPC provider we know of tests against grpc-go.
How do I handle disconnects and backpressure in a Go subscription?
Wrap the Subscribe call in a for-loop with exponential backoff (500ms, 1s, 2s, 5s, max 30s). Track the last processed slot and resume from there on reconnect by including a from_slot in your SubscribeRequest. For backpressure, use a buffered channel between the gRPC receive loop and your business logic — if the buffer fills, log and drop (never block the stream).
Is Subglow's endpoint compatible with the standard yellowstone-grpc Go client?
Yes. Subglow speaks the standard Yellowstone (Dragon's Mouth) gRPC protocol. Change the endpoint to grpc.subglow.io:443 and pass your API key as the x-token header in your context metadata. Your generated stubs and subscription code work unchanged. You additionally get pre-parsed JSON in the custom metadata fields so you can skip most of the Borsh decoding your bot would otherwise do.
What's the latency difference between Go and TypeScript clients?
Negligible on the wire — gRPC serialization is protobuf-level in both. The real delta shows up in hot-path parsing: Go with protoreflect decodes a typical Solana transaction in ~150µs vs ~800µs–2ms for Node.js with the ts-proto generated code. For latency-sensitive bots, Go removes ~1ms of jitter per message. Subglow's pre-parsed mode eliminates this difference for both languages.
Can I run a Yellowstone Go client at 10k+ transactions/sec?
Yes — Go's goroutine model handles this well. The patterns that matter: (1) process messages in a worker pool, not synchronously in the gRPC receive loop; (2) use sync.Pool to reuse decoded transaction structs; (3) pin the receive goroutine to a specific CPU core if you run on a colocated machine. A single grpc-go connection can sustain 50k+ msg/sec on commodity hardware.