Solidity permet de pro­gram­mer des contrats in­tel­li­gents so­phis­ti­qués sur la blo­ck­chain Ethereum. Il propose des approches in­té­res­santes uniques qui le dis­tin­guent des autres langages.

Qu’est-ce que Solidity ?

Solidity est un langage de pro­gram­ma­tion de haut niveau pour la création de contrats in­tel­li­gents (ou smart contracts) sur la blo­ck­chain Ethereum. Les contrats in­tel­li­gents sont des contrats à exécution au­to­ma­tique qui au­to­ma­ti­sent l’échange d’actifs entre des parties. La par­ti­cu­la­rité est qu’aucun in­ter­mé­diaire n’est requis pour assurer le respect de ce contrat.

Le code source de Solidity est compilé en code à octets et mis à dis­po­si­tion sur la blo­ck­chain Ethereum en tant que contrat in­tel­li­gent. Le contrat in­tel­li­gent peut alors être exécuté à partir de chaque nœud du réseau, mais l’état est en­re­gis­tré sur la blo­ck­chain. Voici à titre d’exemple un contrat simple pour un dis­tri­bu­teur 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 ap­pli­ca­tions Solidity peut-il être utilisé ?

Solidity a été spé­cia­le­ment conçu pour la création d’ap­pli­ca­tions dé­cen­tra­li­sées, dApp (« ap­pli­ca­tions dis­tri­buées »), qui sont ex­ploi­tées sur des machines vir­tuelles Ethereum (EVM). Les contrats in­tel­li­gents con­vien­nent notamment pour la gestion des actifs nu­mé­riques, la création de marchés boursiers dé­cen­tra­li­sés et la mise en œuvre de systèmes de vote.

Hé­ber­ge­ment Web
Hé­ber­ge­ment Web de pointe au meilleur prix
  • 3x plus rapide, 60 % d'éco­no­mie
  • Haute dis­po­ni­bi­lité >99,99 %
  • Seulement chez IONOS : jusqu'à 500 Go inclus

Finance dé­cen­tra­li­sée (DeFi)

Solidity est utilisé pour le dé­ve­lop­pe­ment d’ap­pli­ca­tions DeFi telles que des marchés boursiers dé­cen­tra­li­sés, des pla­te­formes de crédit et de location, des marchés pré­dic­tifs et des cryp­to­mon­naies. La DeFi s’est dé­ve­lop­pée jusqu’à devenir l’une des ap­pli­ca­tions préférées pour la tech­no­lo­gie blo­ck­chain. Solidity est ainsi devenu l’un des outils in­dis­pen­sables pour la mise en place d’ap­pli­ca­tions 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 po­pu­la­rité depuis 2020. Les NFT sont des actifs nu­mé­riques uniques en­re­gis­trés sur la blo­ck­chain. Il s’agit donc par exemple d’œuvres d’art nu­mé­riques, de souvenirs sportifs ou d’artefacts de l’industrie du jeu. Solidity est utilisé pour créer des contrats in­tel­li­gents basés sur les NFT.

Gestion de la chaîne lo­gis­tique

Grâce à Solidity, des contrats in­tel­li­gents peuvent être générés pour le contrôle et la gestion des chaînes lo­gis­tiques. Ils sont utilisés pour au­to­ma­ti­ser dif­fé­rents processus de la chaîne lo­gis­tique parmi lesquels le suivi du mouvement de stock, le contrôle de l’au­then­ti­cité des produits et l’exécution des paiements entre les parties.

Systèmes de vote

Avec Solidity, vous pouvez générer des contrats in­tel­li­gents qui mettent en place des systèmes de vote sûrs et trans­pa­rents sur la blo­ck­chain. Les contrats peuvent être utilisés pour vous assurer que les voix ont bien été comp­ta­bi­li­sées et que le processus électoral est juste et trans­pa­rent.

Quels sont les avantages et les in­con­vé­nients de Solidity ?

Dans l’ensemble, Solidity est un langage per­for­mant pour la création de contrats in­tel­li­gents sur la blo­ck­chain Ethereum. Comme toutes les tech­no­lo­gies, Solidity présente toutefois des avantages et des in­con­vé­nients spé­ci­fiques que les dé­ve­lop­peurs doivent connaître lors de la création de contrats in­tel­li­gents. Quoi qu’il en soit, le dé­ve­lop­pe­ment de contrats in­tel­li­gents sûrs requiert un certain niveau de con­nais­sances tech­niques et de rigueur.

Nous prenons l’exemple d’un contrat in­tel­li­gent agissant comme un trou noir : tout ether envoyé au contrat sera ir­ré­mé­dia­ble­ment « 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

  • Flexi­bi­lité : Solidity est un langage po­ly­va­lent. Il vous permet de dé­ve­lop­per des contrats in­tel­li­gents très dif­fé­rents pour une multitude d’ap­pli­ca­tions.
  • 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 ex­cep­tions et les mé­ca­nismes de dé­fail­lance qui aident les dé­ve­lop­peurs à rédiger des contrats sûrs.
  • Com­pa­ti­bi­lité avec Ethereum : Solidity est ac­tuel­le­ment le langage prio­ri­taire pour la création de contrats in­tel­li­gents sur la blo­ck­chain Ethereum.
  • Vaste com­mu­nauté : une im­por­tante com­mu­nauté de dé­ve­lop­peurs blo­ck­chain s’est formée autour de Solidity. Elle abrite ainsi de nom­breuses res­sources pour apprendre et résoudre vos problèmes.

In­con­vé­nients de Solidity

  • Courbe d’ap­pren­tis­sage : Solidity présente une courbe d’ap­pren­tis­sage re­la­ti­ve­ment abrupte pour les dé­ve­lop­peurs néophytes en création de blo­ck­chain et de contrats in­tel­li­gents.
  • Inal­té­ra­bi­lité : dès qu’un contrat in­tel­li­gent est mis à dis­po­si­tion sur la blo­ck­chain, il ne peut plus être modifié. Lors de la rédaction et des tests, les dé­ve­lop­peurs doivent donc être ex­trê­me­ment vigilants.
  • Absence de vé­ri­fi­ca­tion formelle : Solidity ne dispose d’aucun outil intégré pour procéder à des vé­ri­fi­ca­tions formelles du code. Cela signifie que les dé­ve­lop­peurs sont tri­bu­taires d’outils externes pour assurer l’exac­ti­tude de leurs contrats.
  • Outils limités : l’éco­sys­tème d’outils de Solidity est encore re­la­ti­ve­ment peu développé. Cela génère des problèmes po­ten­tiels avec les en­vi­ron­ne­ments de dé­ve­lop­pe­ment intégrés (IDE), les cadres de test et les autres outils de dé­ve­lop­pe­ment.

À quoi ressemble la syntaxe de base de Solidity ?

Solidity est un langage orienté objet, spé­cia­lisé pour les contrats in­tel­li­gents, qui a été influencé par Ja­vaS­cript, Python et C++. La syntaxe du langage s’apparente à celle de Ja­vaS­cript tout en affichant des ca­rac­té­ris­tiques uniques in­té­res­santes.

Les variables en Solidity

De prime abord, les variables en Solidity semblent fonc­tion­ner comme dans les langages si­mi­laires. Une dif­fé­rence notable existe toutefois : la machine virtuelle Ethereum (ou Ethereum Virtual Machine (EVM) en anglais) est utilisée comme en­vi­ron­ne­ment d’exécution. Toutes les opé­ra­tions ef­fec­tuées dans l’EVM, ainsi que l’en­re­gis­tre­ment des données con­som­ment un volume donné de « gaz ». Cela sous-entend que lors de la pro­gram­ma­tion, 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 cons­tantes qui doivent être définies lors de la com­pi­la­tion. Les cons­tantes re­quiè­rent moins de gaz que l’en­re­gis­tre­ment :

// 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 com­por­tent de manière similaire. Elles con­som­ment également moins de gaz et ne peuvent pas être modifiées après l’as­sig­na­tion. Con­trai­re­ment aux variables « cons­tantes », l’as­sig­na­tion doit se dérouler en cours d’exécution.

Les ins­truc­tions de commande en Solidity

En tant que langage de pro­gram­ma­tion impératif, Solidity prend en charge les ins­truc­tions de commande connues, comme les ra­mi­fi­ca­tions et les boucles. Voici le code per­met­tant 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 cor­res­pond à la syntaxe connue de Ja­vaS­cript ou C++ :

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

La boucle « while » fonc­tionne 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 stan­dar­disé de type statique qui prend en charge les types gé­né­ra­le­ment existants dans les langages de pro­gram­ma­tion. Parmi les types simples qui re­pré­sen­tent des valeurs in­di­vi­duelles figurent les booléens, les nombres et les chaînes de ca­rac­tères.

En Solidity, les booléens re­pré­sen­tent les valeurs « true » (vrai) et « false » (faux). Ils peuvent être combinés avec les opé­ra­teurs booléens connus cor­res­pon­dants et utilisés dans les ins­truc­tions « if » (si) :

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

Solidity est com­pa­tible avec un large spectre de types nu­mé­riques. Avec les nombres entiers, une dif­fé­rence est faite entre les nombres signés (« int ») et les nombres non signés (« uint »), ces derniers devant toutefois être uni­que­ment positifs. De plus, la plage d’un chiffre est dé­ter­mi­née par in­cré­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é­ra­tion de messages d’état. Le langage est com­pa­tible avec les guil­le­mets simples et doubles, ainsi qu’avec les ca­rac­tères Unicode :

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

Les fonctions en Solidity

Comme dans la plupart des langages de pro­gram­ma­tion, les fonctions font partie in­té­grante de Solidity. La dé­fi­ni­tion d’une fonction rappelle Ja­vaS­cript, mais les types d’arguments doivent être indiqués ex­pli­ci­te­ment. Par ailleurs, le mot-clé « returns » est utilisé pour ca­rac­té­ri­ser 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

Cu­rieu­se­ment, 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 cor­res­pon­dantes dans le corps de la fonction. Il n’est alors plus né­ces­saire 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 « cons­tantes » ou « immuables », en Solidity, les fonctions peuvent être marquées comme non mo­di­fiantes. 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 in­tel­li­gents en Solidity

Outre les types courants, Solidity connaît également quelques types spé­ci­fiques aux contrats in­tel­li­gents. Le type de base est « address » et re­pré­sente les adresses Ethereum. Les adresses de type « payable » peuvent bé­né­fi­cier de trans­ferts 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 si­mi­laires à des classes dans les langages de pro­gram­ma­tion orientés objet. Les contrats re­grou­pent ainsi des données d’état et des fonctions, et les protègent du monde extérieur. Ils sont com­pa­tibles avec plusieurs héritages, comme c’est le cas en Python ou C++.

Les contrats débutent gé­né­ra­le­ment par une ligne « pragma » qui indique la version autorisée de Solidity, suivie de la dé­fi­ni­tion pro­pre­ment 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 in­tel­li­gents 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 dis­po­nibles :

  • « public » : pos­si­bi­lité d’accès en lecture et en écriture à la variable depuis le contrat. De plus, une fonction « view » est au­to­ma­ti­que­ment 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 in­té­grante de l’interface du contrat et sert pour l’accès par l’extérieur. L’im­por­tante fonction « receive », qui permet de recevoir des ethers, est un exemple bien connu :

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

Les mo­di­fi­ca­teurs en Solidity

Les mo­di­fi­ca­teurs cons­ti­tuent un élément de langage in­té­res­sant en Solidity. La fonction rappelle les dé­co­ra­teurs de Python. Comme pour ces derniers, les mo­di­fi­ca­teurs sont utilisés pour modifier l’appel d’une fonction. Ils sont fré­quem­ment 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 tran­sac­tions avec Solidity

Solidity dispose d’un système intégré de gestion des tran­sac­tions. 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é­ti­tion (roll-back) d’une tran­sac­tion. 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 uti­li­sa­tion est similaire à celle de la fonction « revert » :

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