
Introduction
Pour contourner ces limites, plusieurs solutions de couche 2, comme Base, ont été développées et sont contrôlées par l'évolutivité et la réduction des frais de gaz. Même si les L2 permettent de réduire considérablement les frais par rapport au réseau principal Ethereum, les développeurs de contrats intelligents ne peuvent pas ignorer l'optimisation du gaz dans leur développement.
Ce tutoriel va essayer de consolider l'expérience utilisateur et de créer des applications décentralisées plus compétitives grâce à des stratégies avancées qui réduisent le coût du gaz sur les contrats intelligents qui seront testés. Les exemples fournis sont simulés avec des contrats simples et prennent principalement en compte les prix du gaz au moment de l'exécution, car les coûts de déploiement varient beaucoup selon la taille d'un contrat.
L'optimisation du gaz est super importante pour les développeurs, les utilisateurs et le succès à long terme des projets. Utiliser intelligemment le gaz des contrats intelligents peut rendre les protocoles plus rentables et évolutifs, et moins vulnérables aux menaces de sécurité comme les attaques par déni de service.
Pour utiliser les contrats intelligents, il faut vérifier chaque contrat de manière complète et systématique.
L'importance de l'optimisation du gaz Solidity
L'optimisation du gaz permet aux contrats intelligents, aux protocoles et aux projets de fonctionner dans des conditions de réseau encombré et les rend à la fois bon marché et efficaces. En améliorant le code des contrats, il est possible de révéler d'éventuelles vulnérabilités et les protocoles et les utilisateurs sont plus rassurés.
L'optimisation du gaz est aussi un point important du développement. Ce n'est pas juste un truc sympa à avoir, mais un élément clé pour le succès et la sécurité à long terme des contrats intelligents. Le fait qu'on puisse construire sur des L2 avec des frais relativement bas ne veut pas dire que les frais de gaz ne seront pas beaucoup plus élevés que ceux des concurrents.
Tous les tests utilisent Foundry et Solidity version 0.8.13, le nœud blockchain local Anvil, les commandes de test forge et 100 exécutions d'optimisation.
Les cabinets d'audit professionnels devraient être impliqués dans les contrôles de sécurité complets, ce guide devrait être considéré comme un outil auxiliaire.
Réduire les données sur la chaîne
Les jetons non fongibles (NFT) sont devenus populaires en 2021 et ont suscité l'intérêt pour les NFT entièrement sur chaîne. Contrairement aux NFT traditionnels qui utilisent des données hors chaîne, comme les métadonnées et les références d'images, les NFT sur chaîne placent toutes les infos directement sur la blockchain.
Ces jetons sont connus pour être chers à utiliser et les solutions hybrides sont le choix par défaut quand les frais pèsent sur les utilisateurs. Que tu crées des NFT, des jeux ou des protocoles DeFi, tu dois toujours réfléchir aux données qui doivent vraiment être stockées sur la blockchain et aux compromis entre les deux options.
Réduisez considérablement l'utilisation de gaz en stockant les infos hors chaîne, ce qui nécessite moins de stockage de variables. Une méthode spécifique consiste à utiliser des événements pour stocker les données hors chaîne plutôt que sur le stockage réel en chaîne.
Les coûts de gaz des transactions augmentent à cause des fonctions d'émission supplémentaires liées aux événements ; mais comme les infos ne sont pas stockées sur la chaîne, les économies réalisées dépassent généralement les coûts.
Prenons l'exemple d'un contrat intelligent qui permet aux utilisateurs de voter « vrai » ou « faux ». Le premier stocke les votes des utilisateurs dans des structures en chaîne. Une démo du test de la fonction de vote avec Foundry, avec plus de 100 itérations, montre certains coûts de gaz.
Comparez avec un contrat intelligent qui ne stockerait pas d'informations sur la chaîne, mais émettrait un événement lorsque la fonction de vote est invoquée. Un test impliquant Foundry plus de 100 fois dans la nouvelle fonctionnalité de vote fournit des résultats.
La réduction des données sur la chaîne a permis de baisser la consommation moyenne de gaz de 90,34 %.
Pour accéder aux données hors chaîne sur la chaîne, les fonctions Chainlink et les solutions d'assistance peuvent s'intégrer aux réseaux L2 les plus populaires, comme Base.
Utilisez les mappages plutôt que les tableaux
Il y a deux structures de données principales dans Solidity : les tableaux et les mappages.
- Les tableaux servent à stocker des ensembles d'éléments qui sont associés à certains indices.
- Les mappages clé-valeur sont des structures de données qui permettent d'accéder directement aux données à l'aide de clés uniques
Même si les tableaux peuvent être pratiques pour stocker des vecteurs et d'autres données similaires, les mappages sont souvent préférés parce qu'ils consomment moins de gaz. Ils sont particulièrement adaptés aux situations où on a besoin de données à la demande, par exemple par nom, adresse de portefeuille ou solde de compte.
Pour bien comprendre la consommation de gaz quand on utilise des tableaux ou des mappages, il faut peut-être regarder le gaz utilisé par les opcodes EVM associés. Les opcodes sont des instructions de bas niveau que la machine virtuelle Ethereum exécute dans le cadre des contrats intelligents, chaque opcode ayant un coût en gaz.
Pour accéder aux valeurs du tableau, il faut payer par unité de gaz utilisée par les opcodes EVM. Pour stocker les adresses des utilisateurs et les soldes correspondants sous forme de tableau, il faut faire une boucle sur tous les éléments du tableau, vérifier si l'adresse utilisateur et l'argument fourni correspondent et renvoyer le solde en cas de correspondance.
En passant directement au solde spécifique de l'utilisateur, on évite d'avoir à parcourir tous les éléments du tableau. Une fois les tableaux remplacés par des mappages, 89 % de gaz en moins ont été utilisés pour accéder aux données.
Comparaison de la consommation de gaz : tableaux vs mappages
| Méthode | Avant l'optimisation | Après optimisation | Économies de carburant |
|---|---|---|---|
| Accès aux données | 30 586 | 3 081 | 89 % |
| Ajout de données | - | - | 93 % |
Utilisez des constantes et des éléments immuables pour réduire les coûts de gaz des contrats intelligents
Une autre astuce d'optimisation serait d'utiliser des variables constantes et immuables. En déclarant les variables comme immuables ou constantes, leurs valeurs ne sont fournies qu'au moment de la création du contrat et plus tard.
Contrairement à d'autres variables, elles ne prennent pas de place dans l'EVM. On peut directement encoder les valeurs dans le bytecode smart contract, ce qui veut dire que le coût de stockage des données est réduit, car les variables comme maxSupply et owner n'ont pas les mots-clés constant ou immutable.
Quand les tests sont faits 100 fois, le coût moyen du gaz est de 112 222 unités. Comme maxSupply et owner sont des valeurs connues qui ne sont pas censées changer, déclarer l'offre maximale et le propriétaire qui n'utilisent pas d'espace de stockage est la meilleure option.
Les tests sur les variables constantes ou immuables permettent de gagner en moyenne 35,89 % (de 112 222 à 71 940 unités de gaz).
Optimise les variables inutilisées
C'est un conseil évident pour optimiser les variables dans les contrats intelligents. Mais souvent, les variables inutiles sont gardées quand on exécute le contrat, ce qui fait qu'on utilise du gaz inutilement.
Supprimer les variables uniques inutilisées peut aider à réduire les coûts de gaz, en moyenne de 18 %, surtout quand on pense aux contrats avec des variables inutilisées définies et manipulées dans des fonctions, mais jamais appelées ailleurs.
Résultats de l'optimisation des variables inutilisées
| Phase d'optimisation | Consommation de gaz | Économies |
|---|---|---|
| Avant l'optimisation | 32 513 | - |
| Après optimisation | 27 429 | 18 % |
Remboursement Solidity Gas : supprimer les variables inutilisées
Supprimer les variables inutilisées, c'est essayer d'attribuer des valeurs par défaut aux variables après qu'elles ont été complètement calculées sans que les données soient stockées en mémoire. Par exemple, la valeur par défaut des variables de type uint est 0.
En ne détruisant pas les variables de la fonction lorsque les fonctions se terminent, le coût moyen en gaz est de 100 300 unités pour attribuer des variables aux données. En moyenne, 19 % d'économies de gaz en utilisant la touche Supprimer pour supprimer les variables de données (pour définir les variables sur 0).
Utilisez des tableaux de taille fixe plutôt que des tableaux dynamiques pour réduire les coûts de gaz des contrats intelligents
Utilisez des mappages autant que possible pour réduire les coûts de gaz des contrats intelligents. Mais, quand des tableaux sont nécessaires, les tableaux de taille fixe sont plus économiques en termes de gaz que les tableaux de taille dynamique, car ces derniers peuvent être agrandis à l'infini, ce qui fait grimper les coûts de gaz.
Les tableaux dynamiques peuvent être développés, donc l'EVM doit surveiller les longueurs et les actualiser quand de nouveaux éléments sont ajoutés.
Pense à utiliser du code qui définit des tableaux de taille dynamique et les met à jour avec des fonctions updateArray. Les instructions require s'assureront que les indices fournis se trouvent dans une plage de tableaux de taille fixe. Quand on fait 100 répétitions d'un test, la quantité moyenne de gaz utilisée est de 12 541.
Changer les tableaux en taille fixe 5 crée des tableaux de taille fixe de longueur 5, de type uint256. L'EVM sait que fixedArray avec une variable d'état de taille 5 a 5 emplacements, et la longueur n'est pas stockée dans la mémoire. Remplacer les tableaux dynamiques par des tableaux fixes permet d'économiser 17,99 % de gaz.
Optimisez vos contrats intelligents dès aujourd'hui
Commence à mettre en place ces techniques d'optimisation du gaz pour réduire les coûts jusqu'à 90 % sur Base et d'autres chaînes L2.
Utiliser uint8 à la place de uint256
C'est moins efficace et peut coûter plus cher à utiliser que uint256 à cause du fonctionnement de l'EVM. L'EVM a des tailles de mots de 256 bits. Les opérations avec des entiers de 256 bits (uint256) sont généralement les plus efficaces, car elles correspondent à la taille de mot de l'EVM, tandis que les types plus petits (tels que uint8) nécessitent que le compilateur Solidity crée des opérations supplémentaires pour adapter les types plus petits à un seul emplacement de stockage de 256 bits.
En général, même si les petits types comme uint8 peuvent être pratiques pour le stockage (plusieurs petits types peuvent être regroupés en une seule opération avec un emplacement de stockage de 256 bits), on a vu que les types plus petits ne sont utiles que pour le stockage. Les économies de stockage peuvent être annulées par la conversion vers et depuis uint256 pour faire des calculs.
Combiner des variables inférieures à 256 bits
Combiner des variables de moins de 256 bits n'est généralement pas aussi efficace que les variables de 256 bits. Mais quand ça ne marche pas, il faut utiliser des types moins puissants dans des tailles plus petites, comme les booléens qui prennent 1 octet ou 8 bits pour être stockés.
Quand tu utilises des types moins puissants, tu peux déclarer des variables d'état en tenant compte de l'espace de stockage, et Solidity peut les regrouper et les stocker en utilisant le même emplacement de stockage. L'avantage du regroupement des variables est généralement pris en compte dans les opérations de stockage, plutôt que dans les opérations de mémoire ou de pile.
Comme les tailles des combinaisons bidirectionnelles sont de 16 bits, soit 240 bits de moins que la capacité de stockage d'un seul emplacement, Solidity peut être utilisé pour regrouper les variables dans le même emplacement afin de minimiser l'utilisation de gaz de déploiement en ne nécessitant pas beaucoup d'emplacements pour stocker les variables d'état.
Réorganiser les déclarations de variables permet d'optimiser en moyenne 13 % de gaz.
Résultats variables d'emballage
| Étape | Consommation de gaz | Économies |
|---|---|---|
| Pré-optimisation | 1 678 | - |
| Après optimisation | 1 447 | 13 % |
Choisis le modificateur de visibilité externe.
Pour optimiser le gaz des contrats intelligents, c'est une bonne idée de choisir la bonne visibilité des fonctions. Les modificateurs de visibilité externes peuvent être plus efficaces en termes d'utilisation du gaz que les modificateurs publics, à cause de la façon dont une fonction publique gère ses arguments et dont les données sont transférées aux fonctions.
Les fonctions externes peuvent accéder à calldata, qui est un espace temporaire en lecture seule dans l'EVM contenant les paramètres d'un appel de fonction. Les fonctions publiques peuvent être appelées :
- En externe (quand ils sont appelés en externe par une transaction utilisant calldata)
- En interne (quand ils sont appelés dans un contrat en utilisant calldata)
Comme les fonctions sont publiques, elles doivent recevoir des tableaux en mémoire, ce qui peut coûter cher en gaz si les tableaux sont volumineux. Le passage des fonctions à l'externe permet la réception de tableaux dans les données d'appel, ce qui est plus rentable dans les situations impliquant des tableaux volumineux, avec une économie moyenne de 0,3 % d'unités de gaz par appel.
Stockage pour relire la même valeur
Une bonne astuce pour économiser du gaz, c'est de ne pas lire plusieurs fois la même valeur dans le stockage. Lire depuis le stockage coûte plus cher que lire dans la mémoire. Relire plusieurs fois les mêmes variables dans le stockage et écrire dans le stockage quand ce n'est pas nécessaire peut être un gaspillage de gaz dans les contrats intelligents.
Pour éviter ça, tu peux définir des variables qui sont en mémoire au début des fonctions et leur donner des valeurs de variables numériques.
Ça permet d'économiser 17 % du gaz utilisé par les fonctions sumNumbers, mais plus le tableau est grand, plus il y aura d'itérations et plus ça consommera de gaz.
Variables de mise en cache Résultats
| Étape | Consommation de gaz | Économies |
|---|---|---|
| Avant l'optimisation | 3 527 | - |
| Après optimisation | 2 905 | 17 % |
N'initialisez pas les variables avec des valeurs par défaut
Quand tu déclares des variables d'état sans les initialiser (c'est-à-dire sans leur donner de valeur initiale), ces variables sont automatiquement initialisées avec des valeurs par défaut :
- uint : 0
- bool : faux
- adresse : adresse(0)
Cette dernière option est moins chère que de déclarer des valeurs par défaut et de les mettre à jour quand l'utilisateur interagit avec le système, ce qui n'est pas très efficace en termes de consommation de gaz. Sans initialisation des variables, l'optimisation permet de économiser en moyenne 4 % de gaz.
Prise en charge de l'optimisation du compilateur Solidity
Solidity a des compilateurs avec des paramètres faciles à changer pour optimiser le code compilé. La compilation optimisée tourne plusieurs centaines de fois, optimisant le code et le convertissant en formes moins coûteuses qui demandent moins de gaz pour fonctionner.
Les compilateurs peuvent être ajustés pour trouver le bon équilibre entre les coûts de déploiement et d'exécution. Définir le nombre estimé de contrats à exécuter à l'aide des commandes d'exécution :
- Les niveaux supérieurs sont les meilleurs pour avoir des prix du gaz bas quand on applique le contrat.
- Moins il y a de chiffres, mieux c'est pour réduire les coûts de gaz dans le déploiement du contrat
Optimiser les indicateurs plus la valeur de run=200 dit aux compilateurs de rendre le code optimal pour économiser du gaz quand ils exécutent les fonctions incrementCount 200 fois. Classe ces paramètres selon les besoins uniques de l'application.
Bonus Optimisation du gaz Solidity : Assemblage
Quand tu écris des contrats intelligents dans Solidity, ils sont compilés en bytecodes, une séquence d'opcodes EVM. Grâce à l'assemblage, tu peux simplement écrire du code qui s'exécute à des niveaux plus compatibles avec les opcodes et, dans certains cas, la possibilité d'optimiser manuellement les opcodes lui donne un avantage par rapport au bytecode Solidity.
Même si c'est pas super facile d'écrire du code à un niveau aussi bas, ça a l'avantage de permettre d'optimiser les opcodes à la main, ce qui est souvent mieux que le bytecode Solidity. Ce niveau d'optimisation rend l'exécution du contrat plus efficace et performante.
Même dans les cas simples où les deux fonctions sont censées additionner deux nombres, les versions Solidity et Assembly auront quelques différences, mais les versions Assembly sont moins chères.
La mise en place de projets sur des L2 comme Base permettra aux utilisateurs de payer moins cher pour utiliser les protocoles, mais c'est aux développeurs de s'assurer qu'ils utilisent les techniques d'optimisation du gaz. Ces astuces peuvent vraiment réduire les coûts des transactions, améliorer l'évolutivité et rendre le contrat plus efficace en général.
Assembly peut être utilisé pour optimiser l'exécution des contrats intelligents utilisant du gaz, mais il peut arriver que les versions Assembly entraînent la création d'un code non sécurisé. Il est fortement recommandé de faire appel à des experts en sécurité des contrats intelligents pour examiner les contrats avant leur déploiement.


