Lors de la construction d'un système MEV Solana, les développeurs sont souvent confrontés à un dilemme classique : la vitesse de Rust face à la flexibilité de Python.
Pour pouvoir être aussi rapide qu'un guépard dans la "forêt sombre" (performance d'exécution) tout en étant aussi agile qu'un renard (flexibilité de planification), nous avons adopté une architecture en deux couches : un plan de contrôle (Control Plane) construit en Python, chargé de l'orchestration des stratégies et de la gestion de configuration, et un plan de données (Data Plane) construit en Rust, chargé du traitement de données à haut débit.
Cet article détaillera la logique sous-jacente à cette architecture, ainsi que la manière dont on peut implémenter un moteur de planification de stratégies de niveau industriel en Python.
1. Pourquoi avons-nous besoin d'un "Plan de contrôle" ?
Si nous comparons un robot MEV à une voiture de course, le moteur d'exécution Rust est ce V12 capable de supporter des régimes élevés, tandis que le plan de contrôle Python est le tableau de bord et le levier de vitesses dans la cabine.
1.1 Découplage de la configuration et de la logique
Les stratégies MEV (comme l'arbitrage, le sniping, le liquidation) impliquent de nombreux paramètres : adresse du nœud RPC, montant des pourboires Jito, tokens en liste blanche, contrôle du slippage maximal, etc.
Point de douleur : si nous codons en dur ces configurations dans Rust, chaque ajustement de paramètre nécessiterait une recompilation. Dans un marché en constante évolution, quelques secondes de temps de compilation suffisent à laisser passer une opportunité.
Solution : Python est responsable de la lecture de la configuration YAML/JSON, de la logique de prétraitement, puis de l'injection dans le processus Rust sous forme de paramètres de ligne de commande ou de variables d'environnement.
1.2 Point d'entrée unifié et gestion de multiples stratégies
Un système mature exécute souvent plusieurs stratégies simultanément.
Arb (arbitrage) : fonctionne longtemps, surveille les pools principaux.
Sniper (sniping) : démarrage temporaire, ciblant les nouveaux tokens.
Le Plan de contrôle, en tant que planificateur unifié (Commander), peut démarrer d'un simple clic différentes instances de stratégie en fonction des conditions du marché, réalisant "stratégie comme plugin".
2. Aperçu de l'architecture : frontières et interfaces entre les langages
L'interaction centrale du système suit le principe de **"déduction unidirectionnelle, isolation des processus"** :
sequenceDiagram
participant Dev as Développeur
participant CP as Plan de contrôle Python (Commander)
participant RS as Plan d'exécution Rust (Scavenger)
participant Node as Nœud Solana/Jito
Dev->>CP: Exécutez la commande (par exemple, --strategy arb)
CP->>CP: 1. Localisez automatiquement le fichier de configuration correspondant (arb.yaml)
CP->>CP: 2. Vérifiez les produits de compilation Rust (Binaire Release)
CP->>RS: 3. Démarrez le processus Rust (paramètre : --config )
RS->>Node: 4. Établissez une écoute WebSocket et une connexion gRPC
Note over RS,Node: Traitement des flux de données à haute concurrence
Le plan de contrôle a pour mission : vérification de l'environnement, déduction automatique des chemins, gestion du cycle de vie des processus, sortie gracieuse (Graceful Shutdown).
Les responsabilités du plan d'exécution : analyse de l'état du compte, calcul des prix localement, construction des transactions, soumission des Bundles.
3. Détails de mise en œuvre technique
3.1 Adaptation des chemins et retour à la compilation
Dans un environnement de production, nous exécutons directement le fichier binaire Rust Release précompilé pour obtenir la vitesse de démarrage la plus rapide. Mais lors de la phase de développement et de débogage, nous souhaitons qu'il puisse détecter automatiquement.
Logique de planification en pseudo-code :
Vérifiez s'il existe un binaire sous target/release/.
S'il existe, exécutez-le directement avec subprocess.spawn.
S'il n'existe pas, revenez à cargo run --release.
3.2 Isolation de l'environnement et contraintes de répertoire de travail
Les robots MEV ont souvent besoin de lire le portefeuille local (Keypair) et les fichiers cache. Pour garantir la sécurité et la cohérence, le plan de contrôle doit strictement contraindre le répertoire de travail actuel du processus Rust (CWD). Cela permet d'éviter efficacement les dérives de chemin dans différents environnements (Docker vs machine physique).
4. Exemple de code d'un planificateur de niveau industriel
Voici un exemple simplifié d'implémentation du plan de contrôle Python. Il démontre comment gérer des sous-processus et injecter dynamiquement des configurations.
import argparse
import os
import subprocess
import sys
from pathlib import Path
class BotCommander:
def init(self, strategy: str, config_name: str):
self.strategy = strategy
self.config_path = Path(f"configs/{config_name}.yaml").absolute()
self.root_dir = Path(__file__).parent.parent # Répertoire racine du projet
self.engine_dir = self.root_dir / "engine_rust" # Répertoire source Rust
def findbinary(self) -> list:
"""Sélectionnez la commande à exécuter : privilégiez le binaire release, sinon revenez à cargo run"""
release_bin = self.engine_dir / "target" / "release" / "mev_engine"
if release_bin.exists():
print(f"[*] Utilisation du binaire précompilé : {release_bin}")
return [str(release_bin)]
print("[!] Binaire release introuvable, tentative de lancement via cargo run...")
return ["cargo", "run", "--release", "--bin", "mev_engine", "--"]
def run(self):
# Assemblez la commande d'exécution complète
base_cmd = self._find_binary()
args = [
"--strategy", self.strategy,
"--config", str(self.config_path)
]
full_cmd = base_cmd + args
print(f"[*] Démarrage de la stratégie [{self.strategy}]...")
try:
# Utilisez subprocess pour démarrer le plan d'exécution et verrouiller le répertoire de travail
subprocess.run(full_cmd, cwd=self.engine_dir, check=True)
except KeyboardInterrupt:
print("\n[!] Signal d'arrêt reçu, fermeture du robot...")
except subprocess.CalledProcessError as e:
print(f"[X] Le moteur d'exécution a planté, code de sortie : {e.returncode}")
if name == "__main__":
parser = argparse.ArgumentParser(description="Contrôle du plan MEV Solana")
parser.add_argument("--strategy", default="arbitrage", help="Sélectionnez la stratégie d'exécution")
parser.add_argument("--config", default="mainnet_alpha", help="Nom du fichier de configuration")
cmd_args = parser.parse_args()
commander = BotCommander(cmd_args.strategy, cmd_args.config)
commander.run()
5. Optimisation de la performance et réflexion sur l'exploitation
Dans la production réelle, la conception du plan de contrôle doit également prendre en compte les éléments suivants :
Préparation (Warm-up) : avant de lancer officiellement l'écoute des arbitrages, le plan de contrôle peut d'abord exécuter un simple script Python pour vérifier la latence des nœuds RPC et le solde des portefeuilles, s'assurant que tout est en ordre avant de passer le relais à Rust.
Routage des journaux : Rust produit des journaux JSON structurés, tandis que Python est responsable de leur collecte et de leur envoi à la surveillance à distance (comme Loki ou Telegram Bot).
Mise à jour à chaud des stratégies : pour les "tokens en liste noire" qui ne nécessitent pas de modification de la logique du code, un mécanisme de surveillance de fichiers (Watcher) peut être utilisé. Lorsque Python modifie le fichier de configuration, Rust peut le recharger en temps réel via la bibliothèque notify, sans avoir besoin de redémarrer le processus.
6. Annonce de la prochaine étape
Avec le plan de contrôle comme garantie, nous pouvons entrer en toute confiance dans le monde de Rust. Dans le prochain article, nous analyserons "Surveillance basée sur l'inventaire" - comment établir un index efficace de tous les tokens et pools de liquidité avec Rust, en verrouillant instantanément les opportunités gagnantes au milieu d'un flux de transactions massif.
Cet article a été écrit par Levi.eth. Dans le monde de MEV Solana, un petit pas d'optimisation de l'architecture est souvent un grand pas vers des bénéfices accrus.