Ir al contenido principal

Supervisar los fondos de liquidez de Solana con Solana gRPC (Go)

Actualizado el
Jun 24, 2026

9 minutos de lectura

Resumen

El gRPC de Solana de Quicknode (Geyser gRPC compatible con Yellowstone) es una potente interfaz gRPC basada en el sistema de complementos Geyser de Solana que transmite datos de la cadena de bloques en tiempo real. Al conectarte a este servicio, podrás realizar un seguimiento simultáneo de la actividad en múltiples pools de DEX, analizar patrones de transacciones y desarrollar aplicaciones con gran capacidad de respuesta que requieran datos actualizados al milisegundo.

En esta guía, aprenderás a:

  1. Configurar un entorno Go para Solana gRPC
  2. Crear un cliente para suscribirse a las transacciones del pool SOL/USDC
  3. Supervisar las transacciones en un fondo de liquidez activo de Raydium
  4. Analizar el rendimiento de las transacciones con estadísticas básicas

Requisitos previos


  • Una cuenta de Quicknode con acceso a Solana gRPC (incluida en los planes Scale y Business, o disponible a través del complemento Solana gRPC en los planes Build y Accelerate)
  • Conocimientos básicos de programación en Go
  • Go (versión 1.18 o superior) instalado en tu ordenador

Introducción a gRPC y Geyser de Solana

¿Qué es Geyser?

Geyser es el sistema de complementos de Solana que permite a los validadores transmitir datos de la cadena de bloques en tiempo real a sistemas externos sin generar una carga elevada en el RPC. En lugar de consultar repetidamente la cadena de bloques mediante llamadas RPC, Geyser envía los datos a medida que están disponibles, lo que reduce significativamente la latencia y el consumo de recursos.

¿Qué es Solana gRPC?

Solana gRPC es una interfaz de transmisión de alto rendimiento y segura en cuanto a tipos, basada en el complemento Geyser de Solana. Permite la transmisión de:


  • Actualizaciones de la cuenta
  • Transacciones
  • Entradas
  • Bloquear notificaciones
  • Notificaciones de tragaperras

En el caso de las aplicaciones DeFi y los sistemas de negociación, este acceso a datos en tiempo real puede suponer una ventaja competitiva fundamental.

Configuración de tu entorno

En primer lugar, vamos a crear un nuevo proyecto en Go e instalar las dependencias necesarias.


  1. Crea un directorio para el proyecto:
mkdir solana-dex-monitor && cd solana-dex-monitor

  1. Inicializar el módulo de Go:
go mod init solana-dex-monitor

  1. Instala las dependencias necesarias:
Ve a google.golang.org/grpc
Ve a github.com/joho/godotenv
Ve a github.com/mr-tron/base58
Ve a github.com/rpcpool/yellowstone-grpc/examples/golang/proto

  1. Crear un .env archivo para guardar tus credenciales de Quicknode:
qn_grpc_url=tu-punto-de-acceso-de-QuickNode.grpc.solana.quiknode.pro:443
qn_grpc_token=tu-token-de-QuickNode

Puedes encontrar información sobre cómo configurar tu punto final en nuestra documentación, aquí.

Creación del monitor del fondo de liquidez

Ahora, vamos a crear nuestra aplicación principal para supervisar las transacciones del fondo de liquidez. Para este ejemplo, utilizaremos un fondo SOL/USDC muy activo en Raydium, 3ucNos4NbumPLZNWztqGHNFFgkHeRMBQAVemeeomsUxv, pero puedes añadir fácilmente otros fondos o adaptar esto a otros fondos o DEX.

Crea un archivo llamado main.go y añade el siguiente código:

package main

import (
"context"
"crypto/tls"
"fmt"
"log"
"os"
"strings"
"sync"
"time"

pb "github.com/rpcpool/yellowstone-grpc/examples/golang/proto"

"github.com/joho/godotenv"
"github.com/mr-tron/base58"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/encoding/gzip"
"google.golang.org/grpc/keepalive"
)

var (
endpoint string
token string
)

var SolUsdcPoolAddresses = []string{
"3ucNos4NbumPLZNWztqGHNFFgkHeRMBQAVemeeomsUxv", // Example Raydium SOL/USDC pool
// Add more pool addresses as needed
}

// Load environment variables
func init() {
err := godotenv.Load()
if err != nil {
log.Fatalf("Error loading .env file: %v", err)
}

endpoint = getEnv("qn_grpc_url", "example.com:10000") // Default value as fallback
token = getEnv("qn_grpc_token", "token")
}

// Helper function to get environment variable with a default value
func getEnv(key, defaultValue string) string {
value := os.Getenv(key)
if value == "" {
return defaultValue
}
return value
}

// PoolTxStats tracks statistics for transactions
type PoolTxStats struct {
txCount int
firstTxTime time.Time
lastTxTime time.Time
mutex sync.Mutex
}

// Global variables for tracking statistics
var (
poolStats = make(map[string]*PoolTxStats) // Key is slot as string
statsMutex sync.RWMutex
)

type tokenAuth struct {
token string
}

func (t tokenAuth) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
return map[string]string{
"authorization": t.token,
}, nil
}

func (t tokenAuth) RequireTransportSecurity() bool {
return true
}

// Function to safely extract signature from transaction
func extractSignature(tx *pb.SubscribeUpdateTransaction) string {
if tx == nil {
return "No transaction"
}

if sig := tx.GetTransaction().GetSignature(); len(sig) > 0 {
// Convert the binary signature to base58
return base58.Encode(sig)
}

return "No signature found"
}

func boolPtr(b bool) *bool {
return &b
}

func main() {
// Setup connection parameters
kacp := keepalive.ClientParameters{
Time: 10 * time.Second,
Timeout: 5 * time.Second,
PermitWithoutStream: true,
}

opts := []grpc.DialOption{
grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{})),
grpc.WithKeepaliveParams(kacp),
grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(1024*1024*1024), grpc.UseCompressor(gzip.Name)),
grpc.WithPerRPCCredentials(tokenAuth{token: token}),
}

// Establish connection
conn, err := grpc.Dial(endpoint, opts...)
if err != nil {
log.Fatalf("Failed to connect: %v", err)
}
defer conn.Close()

client := pb.NewGeyserClient(conn)

// Create subscription request for SOL/USDC liquidity pool transactions
transactions := make(map[string]*pb.SubscribeRequestFilterTransactions)
transactions["sol_usdc_pool_txs"] = &pb.SubscribeRequestFilterTransactions{
Vote: boolPtr(false),
Failed: boolPtr(false),
AccountInclude: SolUsdcPoolAddresses,
AccountExclude: []string{},
AccountRequired: []string{},
}

commitment := pb.CommitmentLevel_CONFIRMED
subReq := &pb.SubscribeRequest{
Transactions: transactions,
Commitment: &commitment,
}

fmt.Println("Connecting to Solana gRPC...")
stream, err := client.Subscribe(context.Background())
if err != nil {
fmt.Printf("Failed to subscribe to Solana gRPC: %v\n", err)
return
}

fmt.Println("Sending subscription request for SOL/USDC pool transactions...")
if err = stream.Send(subReq); err != nil {
fmt.Printf("Failed to send subscription request: %v\n", err)
return
}

// Print header for pool statistics
fmt.Printf("\n%-12s %-12s %-15s %-20s\n",
"Slot", "TX Count", "TX/sec", "Total Time (ms)")
fmt.Println(strings.Repeat("-", 65))

// Print stats every 5 seconds
lastPrintTime := time.Now()
printInterval := time.Second * 5

fmt.Println("Monitoring SOL/USDC liquidity pool transactions...")
for {
m, err := stream.Recv()
if err != nil {
fmt.Printf("Failed to receive Solana gRPC message: %v\n", err)
return
}

if tx := m.GetTransaction(); tx != nil {
// Extract the transaction signature
signature := extractSignature(tx)

// Process the transaction using the slot to keep track
slot := tx.GetSlot()
now := time.Now()

// Maintain a simplified tracking approach based on slot
statsMutex.Lock()
slotStr := fmt.Sprintf("%d", slot)
if _, exists := poolStats[slotStr]; !exists {
poolStats[slotStr] = &PoolTxStats{
firstTxTime: now,
lastTxTime: now,
}
}

stats := poolStats[slotStr]
stats.mutex.Lock()
stats.txCount++
stats.lastTxTime = now
stats.mutex.Unlock()
statsMutex.Unlock()

// Print transaction info with signature
fmt.Printf("Pool transaction detected at Slot=%d\nSignature=%s\n", slot, signature)

// Print statistics periodically
if now.Sub(lastPrintTime) >= printInterval {
printPoolStats()
lastPrintTime = now

// Cleanup old statistics
cleanupOldStats()
}
}
}
}

func printPoolStats() {
statsMutex.RLock()
defer statsMutex.RUnlock()

fmt.Println("\nTransaction Statistics:")
fmt.Printf("\n%-12s %-12s %-15s %-20s\n",
"Slot", "TX Count", "TX/sec", "Total Time (ms)")
fmt.Println(strings.Repeat("-", 65))

for slotStr, stats := range poolStats {
stats.mutex.Lock()
duration := stats.lastTxTime.Sub(stats.firstTxTime).Milliseconds()
var txPerSec float64
if duration > 0 {
txPerSec = float64(stats.txCount) / (float64(duration) / 1000.0)
}

fmt.Printf("%-12s %-12d %-15.2f %-20d\n",
slotStr,
stats.txCount,
txPerSec,
duration,
)
stats.mutex.Unlock()
}
fmt.Println()
}

func cleanupOldStats() {
statsMutex.Lock()
defer statsMutex.Unlock()

now := time.Now()
for slotStr, stats := range poolStats {
stats.mutex.Lock()
// Remove statistics older than 1 minute
if now.Sub(stats.lastTxTime) > time.Minute {
delete(poolStats, slotStr)
}
stats.mutex.Unlock()
}
}

Comprender el código

Analicemos los componentes clave de nuestra aplicación:

Configuración y puesta en marcha

  1. Direcciones de la piscina: Definimos una lista de direcciones de pools SOL/USDC a modo de ejemplo; en este caso, utilizamos un pool activo de Raydium. Puedes añadir más pools añadiendo nuevas entradas al archivo SolUsdcPoolAddresses porción.

  2. Autenticación: El tokenAuth La estructura gestiona la autorización mediante tu token de Quicknode.

  3. Configuración de la conexión: Configuramos los parámetros de conexión de gRPC con TLS, compresión y keepalive.

Suscripción a transacciones

El núcleo de nuestro sistema de seguimiento es la solicitud de suscripción:

transactions["sol_usdc_pool_txs"] = &pb.SubscribeRequestFilterTransactions{
Vote: boolPtr(false),
Failed: boolPtr(false),
AccountInclude: SolUsdcPoolAddresses,
AccountExclude: []string{},
AccountRequired: []string{},
}

Este filtro:

  • No incluye las transacciones de voto
  • Solo incluye las transacciones realizadas con éxito
  • Supervisa la actividad relacionada con los identificadores del programa DEX o con cuentas de fondo comunes específicas.

Procesamiento de transacciones

Para cada transacción recibida:

  1. Extraemos la firma utilizando el extraerFirma función
  2. Realizar un seguimiento del número de transacciones y de los tiempos por franja horaria
  3. Calcular y mostrar periódicamente las estadísticas (transacciones por segundo, duración total)
  4. Limpiar las estadísticas antiguas para gestionar el uso de la memoria

Cómo utilizar el monitor

Para ejecutar el monitor:

ejecuta main.go

Deberías ver un resultado similar a este:

Conectando con Solana gRPC...
Enviando solicitud de suscripción para las transacciones del pool SOL/USDC...

Slot N.º de transacciones Transacciones/seg. Tiempo total (ms)
-----------------------------------------------------------------
Supervisando las transacciones del pool de liquidez SOL/USDC...
Transacción del pool detectada en la ranura = 212439883
Firma=4ZV7JsQTwQfLWtN9YMu2EJkTKjyAC9Yjjd1TGY8X5qvqYhfpfTKk3PUK5NZ2P9HFfxXUE2mRJsW2LcUfTF1oTBcP

Estadísticas de la transacción:

Slot N.º de transacciones Transacciones/seg. Tiempo total (ms)
-----------------------------------------------------------------
212439883 1 0,20 5000

Mejoras en el monitor

¿Quieres seguir mejorando tu equipo? Aquí tienes algunas ideas para mejorar tu monitor:

1. Añadir más piscinas

Puedes añadir fácilmente más grupos de monitores incluyendo sus direcciones en el SolUsdcPoolAddresses porción:

var SolUsdcPoolAddresses = []cadena{
"3ucNos4NbumPLZNWztqGHNFFgkHeRMBQAVemeeomsUxv", // Ejemplo de grupo SOL/USDC de Raydium
// Añade más direcciones de pool según sea necesario
}

2. Analizar los datos de las transacciones

En lugar de limitarte a contar las transacciones, podrías analizar los datos de las mismas para extraer los importes de los swaps, el impacto en los precios y otros detalles:

// Añade esta función para descodificar los datos de la transacción
func decodeTransaction(tx *pb.SuscribirseActualizarTransacción) {
// Analizar los datos de la transacción en función del ID del programa
// Los distintos DEX (Orca, Raydium, Jupiter) tienen estructuras de transacción diferentes
}

3. Almacenar datos para su análisis

Conecta el monitor a una base de datos para almacenar los datos de las transacciones con vistas a su posterior análisis:

// Añadir integración con la base de datos
func storeTransactionData(slot uint64, signature cadena, detalles mapa[cadena]interfaz{}) {
// Insertar en la base de datos (PostgreSQL, InfluxDB, etc.)
}

4. Configurar alertas de precios

Tras analizar los datos de la cuenta, podrías añadir una lógica para detectar movimientos significativos en los precios o actividades inusuales:

// Add price monitoring
func detectPriceAnomaly(currentPrice, previousPrice float64) bool {
// Implement anomaly detection logic
percentChange := (currentPrice - previousPrice) / previousPrice * 100
return math.Abs(percentChange) > 1.0 // Alert on 1% price change
}

Conclusión

El uso de Solana gRPC con Go ofrece una forma eficaz de supervisar los fondos de liquidez de Solana con una latencia extremadamente baja. Este enfoque permite realizar un seguimiento en tiempo real de la actividad de los DEX en múltiples programas de forma simultánea, lo que te proporciona los datos que necesitas para operaciones bursátiles, análisis o aplicaciones de supervisión.

En el caso de los bots de trading o los sistemas de arbitraje, en los que cada milisegundo cuenta, el enfoque de streaming gRPC de Solana ofrece una ventaja significativa frente a los métodos RPC tradicionales. Al acceder directamente al flujo continuo de datos de la cadena de bloques de Solana, puedes mantenerte al tanto de los movimientos del mercado en tiempo real.

¡Nos encanta recibir comentarios!

Si tienes algún comentario o sugerencia sobre nuevos temas, no dudes en hacérnoslo saber. Nos encantaría saber tu opinión.

Recursos adicionales

Si tienes alguna duda o necesitas ayuda para implementar tu dApp de Solana, ¡únete a nuestra comunidad de Discord o ponte en contacto con nuestro equipo de asistencia!

Comparte esta guía