L’audit de projets Solidity massifs (plusieurs dizaines de milliers de lignes, comme les écosystèmes Uniswap V4, Aave V3 ou Compound) à l’aide de grands modèles de langage (LLM) se heurte à deux limites critiques :
- Dépassement de la fenêtre de contexte – concaténer l’intégralité des fichiers
.soldépasse rapidement les plafonds de 128k ou 200k tokens ; - Dilution de l’attention et coût prohibitif – noyé dans des dépendances parasites (bibliothèques OpenZeppelin, interfaces, contrats utilitaires), le modèle passe à côté de vulnérabilités métier tout en générant une facture API démesurée.
Une solution chirurgicale consiste à exploiter l’arbre syntaxique abstrait (AST) et le graphe d’appel produits par Slither pour pratiquer un élagage sémantique des dépendances : on ne conserve que les flux de contrôle et les variables d’état réellement mobilisés par les fonctions d’entrée cibles, ce qui élimine en moyenne 85 % du code superflu et fait chuter la consommation de tokens de plus de 80 %.
Pipeline de compression par élagage de l’AST
Le processus peut se décrire ainsi :
Projet complet (50 fichiers, ~60 000 lignes)
│
└──► Analyse Slither : construction de l’AST global et du graphe d’appel
│
└──► Sélection du contrat à auditer (ex. LendingPool.sol)
│
└──► Élagueur de dépendances
├── Collecte des points d’entrée public/external
├── Trace de toutes les fonctions internes atteignables
├── Relevé des variables d’état lues ou écrites
└── Suppression des interfaces non appelées, bibliothèques standard, commentaires purs
│
└──► Contexte minimal haute densité (~3 000 lignes)
│
└──► Soumission au LLM : tokens -82 %, attention concentrée
Script Python d’élagage sémentique
Le script ci‑dessous utilise l’API Slither pour parcourir le graphe d’appel à partir des fonctions exposées et ne retenir que le code essentiel :
# scripts/elaguer_contrat.py
import sys
from slither.slither import Slither
def elaguer_contrat(chemin_fichier, nom_contrat):
analyseur = Slither(chemin_fichier)
# Récupération du contrat cible
cible = None
for contrat in analyseur.contracts:
if contrat.name == nom_contrat:
cible = contrat
break
if cible is None:
print("Contrat introuvable")
return
fonctions_atteintes = set()
variables_etat = set()
def parcourir(fonction):
if fonction in fonctions_atteintes:
return
fonctions_atteintes.add(fonction)
# Variables d’état touchées
for v in fonction.state_variables_read:
variables_etat.add(v)
for v in fonction.state_variables_written:
variables_etat.add(v)
# Propagation aux appels internes
for appel in fonction.internal_calls:
if appel and appel != fonction:
parcourir(appel)
# Démarrer depuis les points d’entrée publics/externes (hors constructeur)
for f in cible.functions_entry_points:
if not f.is_constructor:
parcourir(f)
# Génération du code élagué
code_elague = f"// [Slice élagué] Analyse focalisée sur {cible.name}\n"
code_elague += f"contract {cible.name} {{\n"
code_elague += " // --- Variables d’état pertinentes ---\n"
for var in variables_etat:
code_elague += f" {var.type} {var.visibility} {var.name};\n"
code_elague += "\n // --- Fonctions atteignables ---\n"
for func in cible.functions:
if func in fonctions_atteintes:
code_elague += f"\n {func.source_mapping.content.strip()}\n"
code_elague += "}\n"
with open("cible_elaguee.sol", "w") as fichier_sortie:
fichier_sortie.write(code_elague)
print(f"[✓] Élagage terminé pour {nom_contrat}")
if __name__ == "__main__":
elaguer_contrat("src/LendingPool.sol", "LendingPool")
Comparaison des performances d’audit
Les mesures suivantes ont été obtenues sur les routeurs principaux d’Uniswap V3 et d’Aave V3 :
| Indicateur | Projet complet brut | Après élagage Slither | Gain |
|---|---|---|---|
| Tokens consommés par requête | ~118 500 | ~19 200 | ‑83,8 % |
| Coût unitaire d’audit (API) | ~1,20 USD | ~0,19 USD | ‑84 % |
| Temps d’inférence | 42 s | 8,5 s | ×5 plus rapide |
| Rappel des vulnérabilités métier | 68 % (dilution) | 94 % (focus élevé) | +26 points |
Trois règles impératives pour l’élagage
- Conserver intégralement les modifiers et les définitions de structures – supprimer un
onlyOwnerou une erreur personnalisée fausse l’analyse des permissions par le LLM. - Garder les signatures des interfaces héritées – pour les standards (ERC‑20, ERC‑4626, etc.), seule la déclaration des fonctions est nécessaire, pas l’implémentation des bibliothèques.
- Préserver les déclarations d’événements – les paramètres des
eventcontiennent des informations cruciales sur les flux de valeur et aident le modèle à raisonner sur l’intension métier.