Wenn der Inventory-Modul das 'Gedächtnis' des Roboters ist, dann ist der Scout-Modul dessen 'Augen'. In der turbulente Umgebung von Solana, in der pro Sekunde Zehntausende Zustandsänderungen auftreten, besteht die Aufgabe von Scout darin, extrem schnell Signale auszuwählen, zu filtern und zu entschlüsseln, die für Arbitrage-Strategien wirklich relevant sind.
In der Welt von MEV ist Geschwindigkeit nicht alles, aber ohne Geschwindigkeit ist nichts möglich. Dieser Artikel untersucht detailliert, wie ein System zur Transaktionsüberwachung und -Analyse mit geringer Latenz und hoher Parallelität aufgebaut werden kann.
1. Philosophie des Überwachens: Skalpell vs. großer Fischernetz
Bei Solana stehen wir normalerweise vor zwei ganz unterschiedlichen Anforderungen an die Überwachung, die jeweils unterschiedliche technische Ansätze erfordern:
1.1 accountSubscribe: Präzise Chirurgie (Arb-Modus)
Beim cross-protocol Arbitrage haben wir bereits bestimmte Pools über Inventory gesperrt. Zu diesem Zeitpunkt müssen wir nicht das gesamte Netzwerk beobachten, sondern nur genau auf Änderungen im Data-Feld dieser Pool-Konten achten.
Mechanismus: Sobald sich der Token-Bestand oder der Preis im Pool ändert, sendet der RPC-Node sofort die neuesten Kontodaten.
Vorteil: Die Signale sind extrem direkt, da die aufwändige Transaktionsanalyse umgangen wird, was den schnellsten Weg für Hochfrequenz-Arbitrage darstellt.
1.2 logsSubscribe: Ein riesiger Fangnetz für die gesamte Netzwerk (Sniper-Modus)
Beim Sniping neuer Pools (Sniping) können wir die Pool-Adresse nicht vorhersehen. Stattdessen müssen wir spezifische Protokolle (z. B. Raydium oder Orca) über deren Program Logs überwachen, um Anweisungssignale für „neue Pools“ oder „erste Liquiditätszufuhr“ zu erfassen.
Mechanismus: Durchsuche die Logs nach bestimmten Schlüsselwörtern (z. B. initialize2).
Herausforderung: Sehr viel Rauschen, und nach einer Treffermeldung muss typischerweise ein „langsamster Pfad“-Prozess (z. B. Anfrage an getTransaction) durchgeführt werden, um zusätzliche Pool-Informationen zu erhalten.
2. Kernarchitektur: Multiplexing von Streams
In einem reifen System müssen Sie möglicherweise gleichzeitig Updates von Hunderten von Pools abonnieren. Wenn für jedes Abonnement ein Thread eröffnet wird, explodiert der Systemaufwand sofort.
2.1 Asynchrone Stream-Verschmelzung (Select All)
Wir nutzen die asynchrone Ökosystem von Rust (Tokio + Futures), um mithilfe von select_all Hunderte oder Tausende von WebSocket-Abonnement-Streams zu einem einzigen Ereignis-Stream zusammenzuführen. Es ist, als würde man die Bilder von Hunderten Überwachungskameras auf eine einzige Anzeigetafel zusammenführen, die von einer zentralen Schleife (Event Loop) gleichmäßig verarbeitet werden.
2.2 Thread-Modell und Trennung der „langsamen Pfade“
Die Reaktionsgeschwindigkeit der Haupt-Schleife bestimmt die obere Grenze der Systemverzögerung.
Schneller Pfad (Hot Path): Datenempfang -> Speicher-Entschlüsselung -> Auslösung der Berechnung.
Langsamer Pfad (Long Path): Wenn zusätzliche RPC-Anfragen zur Vervollständigung der Informationen erforderlich sind (z. B. im Sniper-Modus), müssen diese sofort mittels tokio::spawn in einen Hintergrundtask übertragen werden. Die Hauptschleife darf auf keinen Fall blockiert werden.
3. Extremes Parsen: Überspringen unnötiger Informationen
Die Solana-Kontodaten (Account Data) sind normalerweise eine Folge von Binärdaten. Eine ineffiziente Methode ist die vollständige Deserialisierung in ein Objekt. Die extrem effiziente Methode ist die „Bedarfsanalyse“.
3.1 Null-Kopier- und Offset-Positionierung
Zum Beispiel, beim Abonnieren des Orca Whirlpool benötigen wir möglicherweise nur die Werte von sqrt_price und tick_current_index.
Wir müssen nicht den gesamten Pool-Status (hunderte Bytes) analysieren, sondern können direkt die 16 Bytes an einem bestimmten Offset (Offset) aus dem Datenstrom lesen.
In Rust lässt sich mithilfe von bytemuck oder einfachen Zeigeroffsets die Extraktion der kritischen Preisparameter innerhalb von Mikrosekunden durchführen.
3.2 Die Kunst der Filter
Im logsSubscribe-Stadium kann mithilfe des mentions-Filter, den die RPC bereitstellt, bereits auf Serverseite 90 % der irrelevanten Logs ausgeschlossen werden, was den Netzwerk-IO-Aufwand auf der Searcher-Seite erheblich reduziert.
4. Leistungs-Optimierung: Von der ingenieurtechnischen Umsetzung auf Millisekunden
Fragmentierte Abonnements (Sharding): Um die Beschränkungen der gemeinsam genutzten RPC-Verbindungen zu umgehen, teilt Scout die Pool-Whitelist automatisch in Fragmente auf und empfängt gleichzeitig über mehrere WebSocket-Verbindungen, um eine Überlastung (Backpressure) bei einer einzelnen Verbindung zu vermeiden.
Rauschunterdrückung: Für Pools mit hoher Aktualisierungshäufigkeit wird eine einfache Paketverlust- oder Zusammenfassungslogik (Coalescing) implementiert. Wenn innerhalb von 1 ms mehrere Aktualisierungen für denselben Pool auftreten, wird nur der letzte Status verarbeitet, um die Rechenressourcen auf der Strategieebene zu sparen.
Vorabrufen von Indizes: Beim Parsen der Logs werden die häufig verwendeten Token-Decimal-Informationen bereits im Voraus geladen, um zusätzliche Anfragen bei der Preis-Differenz-Berechnung zu vermeiden.
5. Technische Demonstration: Logik der Verschmelzung mehrerer Ereignis-Streams (Python-Simulation)
Obwohl der Hochleistungs-Teil in Rust implementiert ist, lässt sich die „One-to-Many“-Zusammenführung und -Verteilungslogik perfekt mit asyncio ausdrücken:
import asyncio
import random
async def pool_monitor(pool_id: str):
"""Simuliert einen unabhängigen Abonnement-Stream für ein Konto"""
while True:
await asyncio.sleep(random.uniform(0.01, 0.1)) # Simuliert zufällige Pushes
yield {"pool": pool_id, "data": random.random()}
async def main_scout_loop():
# Simuliert die Liste der zu überwachenden Pools aus Inventory
watchlist = ["Pool_A", "Pool_B", "Pool_C"]
# Vereinigt alle Streams in einer einzigen Warteschlange
queue = asyncio.Queue()
async def producer(pool_id):
async for update in pool_monitor(pool_id):
await queue.put(update)
# Starte alle Producer-Aufgaben
for p in watchlist:
asyncio.create_task(producer(p))
print("[*] Scout-Engine gestartet, hört nun auf mehrfache Signale...")
# Hauptverbrauchsschleife: Strategieverteilung und -verarbeitung
while True:
event = await queue.get()
# Aktiviere sofort die asynchrone Berechnung auf der Strategieebene
asyncio.create_task(execute_strategy(event))
async def execute_strategy(event):
print(f"⚡️ Signal erfasst: {event['pool']} -> Auslösen der Preisberechnung")
if name == "__main__":
asyncio.run(main_scout_loop())
6. Zusammenfassung: Der schärfste Radar
Die Qualität des Scout-Moduls bestimmt direkt die „Startgeschwindigkeit“ des Roboters. Ein guter Scout sollte:
ausreichend breit: Kann neue Chancen durch Logs erfassen.
ausreichend genau: Kann Preisänderungen durch Kontenabonnements genau lokalisieren.
ausreichend schnell: Nutzt eine asynchrone Architektur und binäre Analyse, um die Latenz auf Mikrosekunden zu drücken.
Nächster Schritt - Ankündigung
Das Signal wurde erfasst und die Rohdaten erhalten. Was nun? Wir müssen die Binärdaten in echte Asset-Preise umwandeln. In einem nächsten Artikel werden wir uns dem AMM-Modul widmen und die konstante Multiplikationsformel von Raydium sowie das mathematische Modell der konzentrierten Liquidität von Orca im Speicher genauer untersuchen.
Dieser Artikel wurde von Levi.eth verfasst und widmet sich der Weitergabe der höchsten Ingenieurkunst im Bereich Solana MEV.

