Si el módulo de Inventario es la 'memoria' del robot, entonces el módulo Scout es su 'ojo'. En la turbulencia de miles de cambios de estado por segundo que genera Solana, la tarea de Scout es seleccionar, filtrar y decodificar rápidamente las señales que realmente importan para estrategias de arbitraje.

En el mundo de MEV, la velocidad no lo es todo, pero sin velocidad no hay nada. Este artículo explorará en profundidad cómo construir un sistema de escucha y análisis de transacciones de baja latencia y alta concurrencia.

1. Escuchar filosofía: bisturí vs. red de pesca grande

En Solana, generalmente enfrentamos dos necesidades de escucha completamente diferentes, que corresponden a diferentes caminos técnicos:

1.1 accountSubscribe: Cuchillo quirúrgico preciso (modo Arb)

Para arbitraje entre protocolos (Arbitrage), ya hemos bloqueado pozos específicos mediante Inventory. En este momento, no necesitamos observar toda la red, solo debemos vigilar estrechamente los cambios en el campo Data de estas cuentas de pozos.

  • Mecanismo: tan pronto como cambie el saldo de tokens o el precio dentro del pozo, el nodo RPC enviará inmediatamente los datos actualizados de la cuenta.

  • Ventaja: la señal es extremadamente directa, evitando el tedioso análisis de transacciones, siendo la ruta más rápida para el arbitraje de alta frecuencia.

1.2 logsSubscribe: La red de pesca a gran escala que cubre toda la red (modo Sniper)

Para el sniper de nuevos pozos (Sniping), no podemos prever la dirección del pozo, por lo que solo podemos escuchar los Program Logs de protocolos específicos (como Raydium o Orca) para capturar señales de "nuevo pozo" o "injeción inicial de liquidez".

  • Mecanismo: escanear palabras clave específicas en los registros (como initialize2).

  • Desafío: gran ruido, y tras el acierto generalmente se requiere un procesamiento de "ruta lenta" (como solicitar getTransaction) para completar la información del pozo.

2. Arquitectura principal: multiplexión de flujos (Stream Multiplexing)

En un sistema maduro, es posible que debas suscribirte simultáneamente a cientos de actualizaciones de pozos. Si se abre un hilo para cada suscripción, la sobrecarga del sistema explotaría instantáneamente.

2.1 Fusión de flujos asíncronos (Select All)

Utilizamos el ecosistema asíncrono de Rust (Tokio + Futures), aprovechando select_all para fusionar cientos o incluso miles de flujos de suscripción WebSocket en un único flujo de eventos. Es como reunir las imágenes de cientos de cámaras de seguridad en una sola pantalla, con un único bucle principal (Event Loop) que distribuye y procesa todos los eventos de forma centralizada.

2.2 Modelo de hilos y separación de la "ruta lenta"

La velocidad de respuesta del bucle principal determina el límite de latencia del sistema.

  • Ruta rápida (Hot Path): recibir datos -> decodificación en memoria -> activar cálculo.

  • Ruta lenta (Long Path): si se necesita solicitar información adicional del RPC (como en el modo Sniper), debe usarse tokio::spawn para mover inmediatamente la tarea al fondo, sin bloquear el bucle principal de escucha.

3. Análisis extremo: omitir información innecesaria

Los datos de cuenta de Solana (Account Data) suelen ser una cadena de búfer binario. El enfoque ineficiente consiste en deserializarlos en objetos completos, mientras que el enfoque extremo es el "análisis bajo demanda".

3.1 Cero copia y localización por desplazamiento

Por ejemplo, al escuchar Orca Whirlpool, solo podríamos necesitar los campos sqrt_price y tick_current_index.

  • No necesitamos analizar todo el estado del pozo (cientos de bytes), solo necesitamos leer directamente los 16 bytes en una posición específica (desplazamiento) del flujo de datos.

  • En Rust, combinando bytemuck o simples desplazamientos de punteros, se puede extraer los parámetros clave de precios en microsegundos.

3.2 El arte del filtro

En la etapa de logsSubscribe, utilizando el filtro mentions proporcionado por el RPC, se puede filtrar el 90% de los registros irrelevantes directamente en el nodo, reduciendo enormemente la carga de E/S de red en el lado del Searcher.

4. Puntos de optimización de rendimiento: obtener milisegundos desde la implementación

  1. Suscripción por fragmentos (Sharding): ante las limitaciones de conexión de nodos RPC comunes, Scout dividirá automáticamente los pozos de la lista blanca en fragmentos, recibiendo concurrentemente a través de múltiples conexiones WebSocket para evitar la congestión (Backpressure) en una sola conexión.

  2. Mecanismo de reducción de ruido: para pozos con cambios frecuentes, se implementa una lógica simple de descarte o combinación (Coalescing). Si un mismo pozo genera múltiples actualizaciones dentro de 1 ms, solo se procesará el estado final, ahorrando recursos computacionales en la capa de estrategia.

  3. Índice de prelectura: al analizar los registros, se carga previamente la información de Decimals de los tokens comunes, evitando solicitudes secundarias durante el cálculo de diferencias de precios.

5. Demo técnica: lógica de fusión de flujos de eventos múltiples (simulación en Python)

Aunque el núcleo de alto rendimiento está en Rust, su lógica de combinación y distribución "uno a muchos" puede expresarse perfectamente con asyncio:

import asyncio
import random

async def pool_monitor(pool_id: str):
"""Simular un flujo de suscripción de una cuenta independiente"""
while True:
await asyncio.sleep(random.uniform(0.01, 0.1)) # Simular envío aleatorio
yield {"pool": pool_id, "data": random.random()}

async def main_scout_loop():
# Simular obtener la lista de seguimiento desde Inventory
watchlist = ["Pool_A", "Pool_B", "Pool_C"]

# Fusionar todos los flujos en una sola cola
queue = asyncio.Queue()

async def producer(pool_id):
async for update in pool_monitor(pool_id):
await queue.put(update)

# Iniciar todas las tareas de productores
for p in watchlist:
asyncio.create_task(producer(p))

print("[*] El motor Scout ha iniciado, escuchando señales múltiples...")

# Bucle principal de consumo: distribución y procesamiento de estrategias
while True:
event = await queue.get()
# En este momento se activa inmediatamente el cálculo asíncrono a nivel de estrategia
asyncio.create_task(execute_strategy(event))

async def execute_strategy(event):
print(f"⚡️ Se ha capturado una señal: {event['pool']} -> activando el cálculo del modelo de precios")

if name == "__main__":
asyncio.run(main_scout_loop())

6. Conclusión: el radar más sensible

El nivel de diseño del módulo Scout determina directamente la "velocidad de arranque" del robot. Un buen Scout debería:

  • Ser suficientemente amplio: puede capturar nuevas oportunidades a través de los registros.

  • Ser suficientemente preciso: puede bloquear con precisión las fluctuaciones de precios mediante suscripciones a cuentas.

  • Ser suficientemente rápido: utiliza una arquitectura asíncrona y análisis binario para mantener la latencia en el rango de microsegundos.

Próximo aviso

Se ha capturado la señal y se ha obtenido los datos originales. ¿Qué hacemos a continuación? Debemos convertir los datos binarios en precios reales de activos. En el próximo artículo, entraremos en el módulo AMM, revelando cómo las fórmulas de producto constante de Raydium y el modelo matemático de liquidez concentrada de Orca funcionan rápidamente en memoria.

Este artículo fue escrito por Levi.eth, dedicado a compartir el arte extremo de la ingeniería en el campo de MEV de Solana.

$SOL $JTO

JTO
JTOUSDT
0.4113
-6.75%

SOL
SOL
143.25
-0.76%