Audit de smart contracts par LLM : réduction de contexte via élagage de l’AST avec Slither

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 :

  1. Dépassement de la fenêtre de contexte – concaténer l’intégralité des fichiers .sol dépasse rapidement les plafonds de 128k ou 200k tokens ;
  2. 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 onlyOwner ou 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 event contiennent des informations cruciales sur les flux de valeur et aident le modèle à raisonner sur l’intension métier.

Étiquettes: Slither smart-contract LLM token-compression AST-pruning

Publié le 18 octobre à 14h18