Passer au contenu principal
Change page

Anatomie des contrats intelligents

Un contrat intelligent est un programme qui s'exécute à une adresse sur Ethereum. Ils sont constitués de données et de fonctions qui peuvent s'exécuter à la réception d'une transaction. Voici un aperçu de ce qui compose un contrat intelligent.

Prérequis

Assurez-vous d'avoir lu l'article sur les contrats intelligents au préalable. Ce document suppose que vous êtes déjà familier avec des langages de programmation tels que JavaScript ou Python.

Données

Toutes les données d'un contrat doivent être assignées à un emplacement : soit à storage soit à memory. Il est coûteux de modifier le stockage dans un contrat intelligent, vous devez donc réfléchir à l'endroit où vos données doivent résider.

Stockage

Les données persistantes sont appelées stockage et sont représentées par des variables d'état. Ces valeurs sont stockées de façon permanente sur la chaîne de blocs. Vous devez déclarer le type afin que le contrat puisse garder une trace de la quantité de stockage sur la chaîne de blocs dont il a besoin lors de sa compilation.

// Exemple Solidity
contract SimpleStorage {
    uint storedData; // Variable d'état
    // ...
}
# Exemple Vyper
storedData: int128

Si vous avez déjà programmé dans des langages orientés objet, vous serez probablement familier avec la plupart des types. Cependant, address devrait être nouveau pour vous si vous débutez dans le développement sur Ethereum.

Un type address peut contenir une adresse Ethereum, ce qui équivaut à 20 octets ou 160 bits. Il est renvoyé en notation hexadécimale précédé d'un 0x.

Les autres types incluent :

  • booléen
  • entier
  • nombres à virgule fixe
  • tableaux d'octets de taille fixe
  • tableaux d'octets de taille dynamique
  • littéraux rationnels et entiers
  • littéraux de chaînes de caractères
  • littéraux hexadécimaux
  • énumérations

Pour plus d'explications, jetez un œil à la documentation :

Mémoire

Les valeurs qui ne sont stockées que pendant la durée d'exécution d'une fonction de contrat sont appelées variables de mémoire. Comme elles ne sont pas stockées de façon permanente sur la chaîne de blocs, elles sont beaucoup moins chères à utiliser.

Apprenez-en plus sur la façon dont l'EVM stocke les données (stockage, mémoire et pile) dans la documentation de Solidity (opens in a new tab).

Variables d'environnement

En plus des variables que vous définissez dans votre contrat, il existe des variables globales spéciales. Elles sont principalement utilisées pour fournir des informations sur la chaîne de blocs ou la transaction en cours.

Exemples :

PropriétéVariable d'étatDescription
block.timestampuint256Horodatage de l'époque du bloc actuel
msg.senderadresseExpéditeur du message (appel actuel)

Fonctions

En termes plus simples, les fonctions peuvent obtenir ou définir des informations en réponse aux transactions entrantes.

Il existe deux types d'appels de fonctions :

  • internal – ils ne créent pas d'appel EVM
    • Les fonctions internes et les variables d'état ne sont accessibles qu'en interne (c'est-à-dire depuis le contrat actuel ou les contrats qui en dérivent)
  • external – ils créent un appel EVM
    • Les fonctions externes font partie de l'interface du contrat, ce qui signifie qu'elles peuvent être appelées depuis d'autres contrats et via des transactions. Une fonction externe f ne peut pas être appelée en interne (c'est-à-dire que f() ne fonctionne pas, mais this.f() fonctionne).

Elles peuvent également être public ou private

  • Les fonctions public peuvent être appelées en interne depuis le contrat ou en externe via des messages
  • Les fonctions private ne sont visibles que par le contrat dans lequel elles sont définies et non dans les contrats dérivés

Les fonctions et les variables d'état peuvent toutes deux être rendues publiques ou privées.

Voici une fonction permettant de mettre à jour une variable d'état sur un contrat :

// Exemple Solidity
function update_name(string value) public {
    dapp_name = value;
}
  • Le paramètre value de type string est passé à la fonction : update_name
  • Elle est déclarée public, ce qui signifie que n'importe qui peut y accéder
  • Elle n'est pas déclarée view, elle peut donc modifier l'état du contrat

Fonctions view

Ces fonctions promettent de ne pas modifier l'état des données du contrat. Des exemples courants sont les fonctions « accesseurs » (getters) – vous pouvez l'utiliser pour obtenir le solde d'un utilisateur par exemple.

// Exemple Solidity
function balanceOf(address _owner) public view returns (uint256 _balance) {
    return ownerPizzaCount[_owner];
}
dappName: public(String[64])

@view
@external
def readName() -> String[64]:
  return self.dappName

Ce qui est considéré comme modifiant l'état :

  1. Écrire dans des variables d'état.
  2. Émettre des événements (opens in a new tab).
  3. Créer d'autres contrats (opens in a new tab).
  4. Utiliser selfdestruct.
  5. Envoyer de l'ether via des appels.
  6. Appeler toute fonction non marquée view ou pure.
  7. Utiliser des appels de bas niveau.
  8. Utiliser de l'assembleur en ligne contenant certains opcodes.

Fonctions de constructeur

Les fonctions constructor ne sont exécutées qu'une seule fois lorsque le contrat est déployé pour la première fois. À l'instar de constructor dans de nombreux langages de programmation basés sur les classes, ces fonctions initialisent souvent des variables d'état à leurs valeurs spécifiées.

# Exemple Vyper

@deploy
def __init__(_beneficiary: address, _bidding_time: uint256):
    self.beneficiary = _beneficiary
    self.auctionStart = block.timestamp
    self.auctionEnd = self.auctionStart + _bidding_time

Fonctions intégrées

En plus des variables et des fonctions que vous définissez dans votre contrat, il existe des fonctions intégrées spéciales. L'exemple le plus évident est :

  • address.send() – Solidity
  • send(address) – Vyper

Celles-ci permettent aux contrats d'envoyer des ETH à d'autres comptes.

Écrire des fonctions

Votre fonction nécessite :

  • la variable du paramètre et son type (si elle accepte des paramètres)
  • une déclaration interne/externe
  • une déclaration pure/view/payable
  • un type de retour (si elle renvoie une valeur)

Un contrat complet pourrait ressembler à ceci. Ici, la fonction constructor fournit une valeur initiale pour la variable dapp_name.

Événements et journaux

Les événements permettent à votre contrat intelligent de communiquer avec votre interface utilisateur (frontend) ou d'autres applications abonnées. Une fois qu'une transaction est validée et ajoutée à un bloc, les contrats intelligents peuvent émettre des événements et inscrire des informations dans un journal, que l'interface utilisateur peut ensuite traiter et utiliser.

Exemples commentés

Voici quelques exemples écrits en Solidity. Si vous souhaitez jouer avec le code, vous pouvez interagir avec eux dans Remix (opens in a new tab).

Hello world

Jeton

Actif numérique unique

Pour aller plus loin

Consultez la documentation de Solidity et de Vyper pour un aperçu plus complet des contrats intelligents :