Solidity : le langage de programmation pour les contrats intelligents

Solidity permet de programmer des contrats intelligents sophistiqués sur la blockchain Ethereum. Il propose des approches intéressantes uniques qui le distinguent des autres langages.

Qu’est-ce que Solidity ?

Solidity est un langage de programmation de haut niveau pour la création de contrats intelligents (ou smart contracts) sur la blockchain Ethereum. Les contrats intelligents sont des contrats à exécution automatique qui automatisent l’échange d’actifs entre des parties. La particularité est qu’aucun intermédiaire n’est requis pour assurer le respect de ce contrat.

Le code source de Solidity est compilé en code à octets et mis à disposition sur la blockchain Ethereum en tant que contrat intelligent. Le contrat intelligent peut alors être exécuté à partir de chaque nœud du réseau, mais l’état est enregistré sur la blockchain. Voici à titre d’exemple un contrat simple pour un distributeur de jetons non fongibles (non-fungible tokens ou NFT en anglais) :

pragma Solidity 0.8.7;
contract NFTVendingMachine {
    // Declare state variables
    address public owner;
    mapping (address => uint) public nftBalance;
    // Run on deployment
    constructor() {
        owner = msg.sender;
        nftBalance[address(this)] = 100;
    }
    // Allow the owner to restock the NFT balance
    function restock(uint amount) public {
        require(msg.sender == owner, "Only the owner can restock.");
        nftBalance[address(this)] += amount;
    }
    // Allow anyone to purchase NFTs
    function purchase(uint amount) public payable {
        require(msg.value >= amount * 1 ether, "You must pay at least 1 ETH per NFT");
        require(nftBalance[address(this)] >= amount, "Not enough NFTs in stock to complete this purchase");
        nftBalance[address(this)] -= amount;
        nftBalance[msg.sender] += amount;
    }
}
solidity

Pour quelles applications Solidity peut-il être utilisé ?

Solidity a été spécialement conçu pour la création d’applications décentralisées, dApp (« applications distribuées »), qui sont exploitées sur des machines virtuelles Ethereum (EVM). Les contrats intelligents conviennent notamment pour la gestion des actifs numériques, la création de marchés boursiers décentralisés et la mise en œuvre de systèmes de vote.

Hébergement Web avec conseiller personnel !

Hébergement Web puissant, flexible et performant avec boîte email, conseiller personnel et domaine inclus la 1ère année !

Domaine
Certificat SSL
Assistance 24/7

Finance décentralisée (DeFi)

Solidity est utilisé pour le développement d’applications DeFi telles que des marchés boursiers décentralisés, des plateformes de crédit et de location, des marchés prédictifs et des cryptomonnaies. La DeFi s’est développée jusqu’à devenir l’une des applications préférées pour la technologie blockchain. Solidity est ainsi devenu l’un des outils indispensables pour la mise en place d’applications DeFi dans le réseau Ethereum.

Jetons non fongibles

Le jeton non fongible (JNF, non-fungible token ou NFT en anglais) jouit d’une grande popularité depuis 2020. Les NFT sont des actifs numériques uniques enregistrés sur la blockchain. Il s’agit donc par exemple d’œuvres d’art numériques, de souvenirs sportifs ou d’artefacts de l’industrie du jeu. Solidity est utilisé pour créer des contrats intelligents basés sur les NFT.

Gestion de la chaîne logistique

Grâce à Solidity, des contrats intelligents peuvent être générés pour le contrôle et la gestion des chaînes logistiques. Ils sont utilisés pour automatiser différents processus de la chaîne logistique parmi lesquels le suivi du mouvement de stock, le contrôle de l’authenticité des produits et l’exécution des paiements entre les parties.

Systèmes de vote

Avec Solidity, vous pouvez générer des contrats intelligents qui mettent en place des systèmes de vote sûrs et transparents sur la blockchain. Les contrats peuvent être utilisés pour vous assurer que les voix ont bien été comptabilisées et que le processus électoral est juste et transparent.

Quels sont les avantages et les inconvénients de Solidity ?

Dans l’ensemble, Solidity est un langage performant pour la création de contrats intelligents sur la blockchain Ethereum. Comme toutes les technologies, Solidity présente toutefois des avantages et des inconvénients spécifiques que les développeurs doivent connaître lors de la création de contrats intelligents. Quoi qu’il en soit, le développement de contrats intelligents sûrs requiert un certain niveau de connaissances techniques et de rigueur.

Nous prenons l’exemple d’un contrat intelligent agissant comme un trou noir : tout ether envoyé au contrat sera irrémédiablement « absorbé ». Il n’existe aucun moyen de retirer l’ether :

// SPDX-License-Identifier: GPL-3.0
pragma solidity >= 0.9.0;
// This contract swallows all Ether sent to it
contract Blackhole {
    event Received(address, uint);
    receive() external payable {
            emit Received(msg.sender, msg.value);
    }
}
solidity

Avantages de Solidity

  • Flexibilité : Solidity est un langage polyvalent. Il vous permet de développer des contrats intelligents très différents pour une multitude d’applications.
  • Sécurité : Solidity a été développé dans un objectif de sécurité. Le langage contient des fonctions telles que les contrôles d’accès, la gestion des exceptions et les mécanismes de défaillance qui aident les développeurs à rédiger des contrats sûrs.
  • Compatibilité avec Ethereum : Solidity est actuellement le langage prioritaire pour la création de contrats intelligents sur la blockchain Ethereum.
  • Vaste communauté : une importante communauté de développeurs blockchain s’est formée autour de Solidity. Elle abrite ainsi de nombreuses ressources pour apprendre et résoudre vos problèmes.

Inconvénients de Solidity

  • Courbe d’apprentissage : Solidity présente une courbe d’apprentissage relativement abrupte pour les développeurs néophytes en création de blockchain et de contrats intelligents.
  • Inaltérabilité : dès qu’un contrat intelligent est mis à disposition sur la blockchain, il ne peut plus être modifié. Lors de la rédaction et des tests, les développeurs doivent donc être extrêmement vigilants.
  • Absence de vérification formelle : Solidity ne dispose d’aucun outil intégré pour procéder à des vérifications formelles du code. Cela signifie que les développeurs sont tributaires d’outils externes pour assurer l’exactitude de leurs contrats.
  • Outils limités : l’écosystème d’outils de Solidity est encore relativement peu développé. Cela génère des problèmes potentiels avec les environnements de développement intégrés (IDE), les cadres de test et les autres outils de développement.

À quoi ressemble la syntaxe de base de Solidity ?

Solidity est un langage orienté objet, spécialisé pour les contrats intelligents, qui a été influencé par JavaScript, Python et C++. La syntaxe du langage s’apparente à celle de JavaScript tout en affichant des caractéristiques uniques intéressantes.

Les variables en Solidity

De prime abord, les variables en Solidity semblent fonctionner comme dans les langages similaires. Une différence notable existe toutefois : la machine virtuelle Ethereum (ou Ethereum Virtual Machine (EVM) en anglais) est utilisée comme environnement d’exécution. Toutes les opérations effectuées dans l’EVM, ainsi que l’enregistrement des données consomment un volume donné de « gaz ». Cela sous-entend que lors de la programmation, vous devez réfléchir, le cas échéant, à la manière la plus efficace possible de mettre en œuvre une opération.

Outre les variables « normales », Solidity dispose de constantes qui doivent être définies lors de la compilation. Les constantes requièrent moins de gaz que l’enregistrement :

// Regular variable can be declared without defining
int a;
// Constant needs to be defined at declaration
int constant b = 51;
solidity

Les variables « immuables » se comportent de manière similaire. Elles consomment également moins de gaz et ne peuvent pas être modifiées après l’assignation. Contrairement aux variables « constantes », l’assignation doit se dérouler en cours d’exécution.

Les instructions de commande en Solidity

En tant que langage de programmation impératif, Solidity prend en charge les instructions de commande connues, comme les ramifications et les boucles. Voici le code permettant de choisir le plus grand de deux chiffres (entre « a » et « b ») :

int largerNumber = 0;
// If-else statement
if (a > b) {
    largerNumber = a;
} else {
        largerNumber = b;
}
solidity

La boucle « for » en Solidity correspond à la syntaxe connue de JavaScript ou C++ :

// Loop 10 times
for (int i = 0; i < 10; i++) {
    // …
}
solidity

La boucle « while » fonctionne elle aussi comme d’habitude. Nous combinons ici une condition d’arrêt à une variable de compteur numérique :

bool continueLoop = true;
int counter = 0;
// Loop at most 10 times
while (continueLoop && counter < 10) {
    // …
    counter++;
}
solidity

Les types simples en Solidity

Solidity est un langage standardisé de type statique qui prend en charge les types généralement existants dans les langages de programmation. Parmi les types simples qui représentent des valeurs individuelles figurent les booléens, les nombres et les chaînes de caractères.

En Solidity, les booléens représentent les valeurs « true » (vrai) et « false » (faux). Ils peuvent être combinés avec les opérateurs booléens connus correspondants et utilisés dans les instructions « if » (si) :

bool paymentReceived = true;
bool itemsStocked = true;
bool continueTransaction = paymentReceived && itemsStocked;
if (continueTransaction) {
    // ...
}
solidity

Solidity est compatible avec un large spectre de types numériques. Avec les nombres entiers, une différence est faite entre les nombres signés (« int ») et les nombres non signés (« uint »), ces derniers devant toutefois être uniquement positifs. De plus, la plage d’un chiffre est déterminée par incréments de 8 bits, de « int8 » à « int16 » jusqu’à « int265 ».

uint8 smallNumber = 120;
int8 negativeNumber = -125;
int8 result = smallNumber + negativeNumber;
assert(result == -5)
solidity

Les chaînes sont surtout utilisées en Solidity lors de la génération de messages d’état. Le langage est compatible avec les guillemets simples et doubles, ainsi qu’avec les caractères Unicode :

string message = 'Hello World';
string success = unicode"Transfer sent ";
Solidity

Les fonctions en Solidity

Comme dans la plupart des langages de programmation, les fonctions font partie intégrante de Solidity. La définition d’une fonction rappelle JavaScript, mais les types d’arguments doivent être indiqués explicitement. Par ailleurs, le mot-clé « returns » est utilisé pour caractériser les types de valeurs de retour :

// Define a function
function addNumbers(int a, int b) returns (int) {
    return a + b;
}
solidity

L’appel d’une fonction se déroule comme suit :

// Call the function
int result = addNumbers(2, 3);
solidity

Curieusement, les valeurs de retour peuvent également être nommées, de manière similaire aux arguments nommés. Dans ce cas, il suffit d’assigner les variables correspondantes dans le corps de la fonction. Il n’est alors plus nécessaire de procéder à un retour explicite par « return » :

function divideNumbers(int dividend, int divisor) returns (int quotient) {
    quotient = dividend / divisor;
    // No `return` necessary
}
solidity

Comme pour les variables « constantes » ou « immuables », en Solidity, les fonctions peuvent être marquées comme non modifiantes. Les mots-clés « view » et « pure » sont alors utilisés. Une fonction « view » ne modifie pas l’état tandis que la fonction « pure » veille en plus à ne pas lire de variables d’état.

Contrats intelligents en Solidity

Outre les types courants, Solidity connaît également quelques types spécifiques aux contrats intelligents. Le type de base est « address » et représente les adresses Ethereum. Les adresses de type « payable » peuvent bénéficier de transferts d’ethers. Les adresses de type « payable » mettent en place les méthodes « balance() » et « transfer() ».

// Get address of this contract
address mine = address(this);
// Get payable external address
address payable other = payable(0x123);
// Transfer if balances fulfill conditions
if (other.balance < 10 && mine.balance >= 100) {
    other.transfer(10);
}
solidity

Basé sur le type « address », le type « contract » fait office d’élément de langage central. Les contrats sont plus ou moins similaires à des classes dans les langages de programmation orientés objet. Les contrats regroupent ainsi des données d’état et des fonctions, et les protègent du monde extérieur. Ils sont compatibles avec plusieurs héritages, comme c’est le cas en Python ou C++.

Les contrats débutent généralement par une ligne « pragma » qui indique la version autorisée de Solidity, suivie de la définition proprement dite :

// Make sure Solidity version matches
pragma Solidity >=0.7.1 <0.9.0;
// Contract definition
contract Purchase {
    // Public state variables
    address seller;
    address buyer;
    
    // View-function
    function getSeller() external view returns (address) {
        return seller;
    }
}
solidity

Les contrats intelligents peuvent définir des données d’état et des fonctions. Tout comme en C++ et Java, vous pouvez définir un niveau d’accès parmi les trois disponibles :

  • « public » : possibilité d’accès en lecture et en écriture à la variable depuis le contrat. De plus, une fonction « view » est automatiquement générée comme moyen d’obtention de l’accès en lecture depuis l’extérieur.
  • « internal » : la variable est protégée contre tout accès extérieur. L’accès est autorisé en lecture et en écriture depuis un contrat et depuis un contrat hérité.
  • « private » : similaire au niveau « internal », mais ne comporte aucun accès depuis des contrats hérités.

Les fonctions peuvent également être définies comme « external ». Une fonction « external » agit comme partie intégrante de l’interface du contrat et sert pour l’accès par l’extérieur. L’importante fonction « receive », qui permet de recevoir des ethers, est un exemple bien connu :

// Define without `function` keyword
receive() external payable {
    // Handle Ether
}
solidity

Les modificateurs en Solidity

Les modificateurs constituent un élément de langage intéressant en Solidity. La fonction rappelle les décorateurs de Python. Comme pour ces derniers, les modificateurs sont utilisés pour modifier l’appel d’une fonction. Ils sont fréquemment utilisés pour veiller à ce qu’une condition soit respectée avant qu’une fonction ne soit exécutée :

contract Sale {
    uint price;
    address payable owner;
    modifier onlyOwner {
        // Will throw error if called by anyone other than the owner
        require(
            msg.sender == owner,
            "Only owner can call this function."
        );
        // The wrapped function's body is inserted here
        _;
    }
    
    // `onlyOwner` wraps `changePrice`
    function changePrice(uint newPrice) public onlyOwner {
        // We'll only get here if the owner called this function
        price = newPrice;
    }
}
solidity

Gestion des transactions avec Solidity

Solidity dispose d’un système intégré de gestion des transactions. Cela permet de s’assurer qu’un transfert d’ethers a été exécuté en totalité ou pas du tout. Le langage utilise pour ce faire le mot-clé spécial « revert » qui déclenche la répétition (roll-back) d’une transaction. Le mot-clé « error » vous permet de définir vos propres codes d’erreur :

// Custom error definition
error InsufficientPayment(uint256 paid, uint256 required);
// Contract representing a sale
contract Sale {
    uint price;
    // Purchase if enough ether transferred
    function purchase() public payable {
        if (msg.value < price) {
            revert InsufficientPayment(msg.value, price);
        }
        // Complete purchase
    }
}
solidity

Autre « modèle fréquent » : la fonction « require() ». Son utilisation est similaire à celle de la fonction « revert » :

// Using `require()` function
if (!condition) revert("Error message");
// Equivalent to
require(condition, "Error message");
solidity