Tutoriel développement Ethereum : Etape 1 Le smart contract

6

Maintenant que nous avons installé notre environnement de développement nous allons créer notre premier smart contract, en français contrat intelligent.

L’application décentralisée (Dapp) que nous allons réaliser consistera en un module de gestion d’adoption d’animaux.

La contexte : un élevage possède 16 enclos pour chiens à adopter. Pour garantir un suivi efficace, nous allons créer une application qui permet d’associer à chaque animal une personne qui l’adopte. (c’est un peu plus intéressant qu’un simple Hello World, et pas plus compliqué)

Nous allons donc créer le smart contract, le compiler, le déployer sur la blockchain, puis créer l’interface web pour interagir avec, ce qui permettra d’avoir une vue d’ensemble d’une dApp, de sa création à son utilisation réelle.

Solicity, le langage de développement pour la blockchain Ethereum

Il est possible d’utiliser plusieurs langages pour développer des smart contracts, le plus connu et le plus répandu, que nous allons utilisé dans ce tutoriel est Solidity, dont la syntaxe est assez proche du javascript. Vous verrez qu’un smart contract ressemble en fait à une ‘grosse classe’, et ce langage est de typé statique.
Une fois le code créé, il sera compilé en Bytecode qui sera exécuté par l’EVM (l’Ethereum Virtual Machine).
Pour info, mais ce ne sera pas développé ici, il est aussi possible d’utiliser les langages Serpent, LLL, et Mutan pour développer des smart contrats. Enfin il existe un langage pour Ethereum, pour l’instant très expérimental et qui ressemble beaucoup à Python appelé Vyper. Il est déconseillé de tenter de l’utiliser pour l’instant, ou alors par curiosité en ayant conscience qu’il en au stage de la recherche.

– Commencez par créer un répertoire qui accueillera notre projet, appelez le ‘pet-shop-tutorial’, directement à la racine de C:, il sera alors accessible ici : C:\pet-shop-tutorial
Ouvrez ensuite une invite de commande (tapez CMD dans la barre de recherche Windows 10), puis se rendre dans le répertoire que l’on vient de créer en tapant :

 cd C:\pet-shop-tutorial

Nous allons maintenant installer Truffle, qui se veut être le couteau suisse du développement Ethereum, c’est en fait un framework qui permet de gagner énormément de temps au moment de compiler et déployer notre application, il permet aussi de créer des tests automatisés pour nos smart contrats (nous utiliserons cette fonctionnalité ensuite). De plus, ils ont la bonne idée chez Truffle de mettre à disposition des ‘Truffle Boxes’, qui contiennent des modules et librairies pour les smart contracts ou pour gérer le front end.  Ce tutorial est d’ailleurs une libre adaptation de celui (en anglais) disponible ici.  Nous allons donc aborder tout le développement Ethereum sous l’angle d’une application web.

Commençons donc par installer Truffle avec la commande suivante :

 npm install -g truffle

Si un message d’erreur s’affiche, vous n’avez probablement pas installé l’environnement de développement comme indiqué ici.

Pour vérifier que tout est bien installé, vous pouvez taper :

 truffle version

Si tout s’est bien passé, vous devriez voir ceci :

Nous allons maintenant installer la truffle box avec tous les outils nécessaires, en tapant ceci :

 truffle unbox pet-shop

Et vous verrez alors :

C:\pet-shop-tutorial truffle unbox pet-shop
Downloading...
Unpacking...
Setting up...
Unbox successful. Sweet!

Commands:

Compile: truffle compile
 Migrate: truffle migrate
 Test contracts: truffle test
 Run dev server: npm run dev

C:\pet-shop-tutorial

Nous allons donc commencer à écrire notre smart contract. Rendez vous dans le répertoire ‘contracts’ qui vient d’être créé à l’étape précédente. C’est dans ce répertoire que doivent être inclus les fichiers .sol,  qui sont les fichiers en langage Solidity.

Créer donc un nouveau fichier Adoption.sol puis ouvrez le avec votre éditeur de texte préféré pour y inclure ceci :

pragma solidity ^0.4.17; 
contract Adoption {

}

La première ligne indique au compileur la version minimum à utiliser. Ensuite on déclare notre contrat.

Nous devons ensuite commencer par déclarer nos variables (rappelez vous que Solidity est typé statique), nous allons commencer par une variable propre à ce langage de type ‘address’, il s’agit en fait d’une adresse Ethereum de 20 bytes. Tous les comptes et smart contract ont une adresse sous ce format, qui peut être utilisé pour recevoir et envoyer de l’ETHER (la monnaie de cette blockchain). Ajoutez cette ligne dans le contrat :

address[16] public adopters;

Dans la structure d’un contrat, après avoir déclaré celui-ci et ses variables, il faut créer les fonctions propres au fonctionnement du contrat. La première sera celle d’adoption d’un chien :

// Adopter un animal
function adopt(uint petId) public returns (uint) { 
  require(petId >= 0 &amp;&amp; petId <= 15); 
  adopters[petId] = msg.sender; 
  return petId; 
}

Dans Solidity, il est toujours indispensable d’indiquer le type de variable que la fonction accepte et celui qu’elle renvoie. Ici, notre fonction prend l’id d’un animal. Nous vérifions grâce à la structure de contrôle ‘require’ que l’animal appartient à la liste des id possibles, qui rappelons-le ici n’a que 16 valeurs possibles(correspondants aux 16 enclos de notre élevage), soit de 0 à 15. (notre page web se chargera de déclarer les id correspondants aux animaux).
Enfin, nous déclarons le nouveau propriétaire de l’animal avec msg.sender, qui récupère l’adresse ethereum de celui qui a appelé la fonction. Le petId est retourné comme confirmation.

Nous souhaitons aussi pouvoir récupérer la liste de tous ceux qui ont adopté un animal, ce que nous ferons avec cette fonction :

// La liste des propriétaires
function getAdopters() public view returns (address[16]) {
  return adopters;
}

On remarque ici l’utilisation de ‘view’ ce qui signifie que la fonction ne peut modifier l’état de la variable, et que rien ne sera modifié par cette fonction dans la blockchain.

Maintenant que notre contrat est prêt, il doit ressemble à ceci :

 
pragma solidity ^0.4.17; 
contract Adoption { 

  address[16] public adopters;

  function adopt(uint petId) public returns (uint) { 
   require(petId >= 0 &amp;&amp; petId <= 15); 
   adopters[petId] = msg.sender; 
   return petId; 
  }

  function getAdopters() public view returns (address[16]) {
    return adopters;
  }

} 

Solidity est un langage compilé et nous devons donc compiler notre contrat pour qu’il puisse être utilisé sur l’Ethereum Virtual Machine (EVM).
C’est la que truffle devient génial, puisqu’il transforme de longues et fastidieuses étapes de compilation en une ligne de commande, à executer depuis le répertoire : « C:\pet-shop-tutorial ».
La commande à executer :

 truffle.cmd compile

Si vous n’êtes pas sur windows utilisez simplement ‘truffle’ au lieu de ‘truffle.cmd’

C:\pet-shop-tutorial>truffle.cmd compile
Compiling .\contracts\Adoption.sol...
Compiling .\contracts\Migrations.sol...

Vous verrez apparaître éventuellement quelques warnings qui concernent Migrations.sol que vous pouvez ignorer.

Cette étape aura créé 2 fichiers JSON dans le répertoire ‘build/contracts’ qui contiennent les bytecode de nos contrats.

Maintenant que nous avons compilé notre premier contrat, il est temps de le déployer sur la blockchain, ce que nous allons faire à l’aide d’un script de migrations, qui sont en fait des fichiers javascript pour déployer le code solidity. (ici aussi Truffle se révèle indispensable pour gagner du temps. Vous pouvez voir dans le répertoire ‘migrations’ que notre truffle box a déjà créé le script de migrations de Migrations.sol.

Nous allons donc créer un fichier dans ce répertoire migrations un autre fichier ‘2_deploy_contracts.js’ avec le contenu suivant :

var Adoption = artifacts.require("Adoption");

module.exports = function(deployer) {
  deployer.deploy(Adoption);
};

Nous sommes prêts à déployer notre contrat, il faut pour cela au préalable lancer notre blockchain local, appelé Ganache, que nous avons téléchargé à l’étape précédente. Il vous suffit alors de double cliquer sur l’icône ganache pour voir cette fenêtre :

Ganache nous permet d’avoir une dizaines de comptes Ethereum avec 100 ethers chacun, ce qui permet de faire des tests facilement, l’avantage aussi est de disposer des logs détaillés de tout ce qu’il se passe que la blockchain, c’est donc un outils indispensable lors du développement de toute dApp.

truffle.cmd migrate 

(comme tout à l’heure, utilisez truffle au lieu de truffle.cmd si vous n’êtes pas sur Windows)

Si tout se passe bien vous devriez voir ceci :

C:\pet-shop-tutorial>
truffle.cmd migrate
Using network 'development'.

Running migration: 1_initial_migration.js
Deploying Migrations...
... 0x08a422b62d9bab9af9ffc5e5890dc7ce3fc56f8f3fd036ebfa4755ad21a4ab6a
Migrations: 0x8cdaf0cd259887258bc13a92c0a6da92698644c0
Saving successful migration to network...
... 0xd7bc86d31bee32fa3988f1c1eabce403a1b5d570340a3a9cdba53a472ee8c956
Saving artifacts...
Running migration: 2_deploy_contracts.js
Deploying Adoption...
... 0xb34665e299ac7179dd527378f6d44c6cfd6a884dca9fa22bb318a4e42d878951
Adoption: 0x345ca3e014aaf5dca488057592ee47305d9b3e10
Saving successful migration to network...
... 0xf36163615f41ef7ed8f4a8f192149a0bf633fe1a2398ce001bf44c43dc7bdda0
Saving artifacts...

C:\pet-shop-tutorial>

On voit donc que notre smart contract a été déployé sur notre blockchain local, cela provient du fichier truffle.js à la racine de notre projet qui contient par défaut l’adresse de la blockchain local pour l’environnement développement. La puissance de Truffle réside aussi dans le fait de pouvoir configurer plusieurs environnements dans le même projet, il suffit de les ajouter dans le fichier truffle.js.

Du côté de Ganache, on peut voir que le ‘current block’ est passé de 0 à 4, et que le nombre d’ETH du premier compte a légèrement diminué. Il ne faut pas trop s’en préoccuper pour l’instant, Ganache gère tout seul le minage des blocks (lorsque c’est utile uniquement) et l’utilisation des Ethers.

Procédure de test des smarts contrats

Nous allons maintenant voir comment Truffle nous permet d’interagir et de tester nos smart contracts.
Créez tout d’abord un fichier TestAdoption.sol dans le répertoire ‘test’ de notre application, et ajoutez le code suivant :

pragma solidity ^0.4.17;

import "truffle/Assert.sol";
import "truffle/DeployedAddresses.sol";
import "../contracts/Adoption.sol";

contract TestAdoption {
  Adoption adoption = Adoption(DeployedAddresses.Adoption());

}

Nouveauté importante ici, la commande ‘import’ nous permet d’utiliser d’autres fichiers Solidity, et donc d’interagir avec d’autres contrats. Truffle nous propose notamment Assert.sol qui permet de tester les égalités, inégalités, si quelque chose est vide, etc.
Concernant DeployedAddresses.sol, il permet simplement de créer une nouvelle instance du contrat dans la blockchain pour notre test, et d’en récupérer l’adresse. Enfin, on importe bien sur le contrat Adoption.sol sur lequel vont avoir lieu les tests.

Le test de la fonction d’adoption

Lorsque nous avons créé notre fonction d’adoption nous lui avons demandé de nous renvoyer l’ID pour vérifier qu’elle s’est bien exécutée. Nous allons donc pour tester la fonction et entrer une ID ‘en dur’ (comprise entre 0 et 15) et vérifier que l’ID qui est retournée est bien la même, grâce à la méthode ‘Assert.equal’.

Créons donc cette nouvelle fonction :

function testUserCanAdoptPet() public {
  uint returnedId = adoption.adopt(8);

  uint expected = 8;

  Assert.equal(returnedId, expected, "L'animal 8 a bien été adopté.");
}

Le test de la variable adopters

Nous allons aussi vérifier que l’adresse du propriétaire du chien 8 est bien la même que celui qui a exécuté le contrat, grâce au mot clé ‘this’, voici la fonction :

  function testGetAdopterAddressByPetId() public {
    // Expected owner is this contract
    address expected = this;

    address adopter = adoption.adopters(8);

    Assert.equal(adopter, expected, "Adresse Ethereum du propriétaire du chien numéro 8.");
  }

Le test de la liste des propriétaires

Dernière fonction que nous avions créée dans notre contrat, la possibilité de récupérer la liste de tous les propriétaires, nous allons aussi la tester :

function testGetAdopterAddressByPetIdInArray() public {

  address expected = this;

  address[16] memory adopters = adoption.getAdopters();

  Assert.equal(adopters[8], expected, "Adresse Ethereum du propriétaire du chien numéro 8.");
}

Vous voyez ici que nous utilisons un type ‘memory’ pour stocker la liste des propriétaires, ce qui indique à Solidity de la garder en mémoire plutôt que de la stocker dans le contrat.

Pour rappel et vérification, je vous mets le contenu complet du fichier TestAdoption.sol :

pragma solidity ^0.4.17;

import "truffle/Assert.sol";
import "truffle/DeployedAddresses.sol";
import "../contracts/Adoption.sol";

contract TestAdoption {
  Adoption adoption = Adoption(DeployedAddresses.Adoption());

  function testUserCanAdoptPet() public {
    uint returnedId = adoption.adopt(8);

    uint expected = 8;

    Assert.equal(returnedId, expected, "Adoption of pet ID 8 should be recorded.");
  }

  function testGetAdopterAddressByPetId() public {
    // Expected owner is this contract
    address expected = this;

    address adopter = adoption.adopters(8);

    Assert.equal(adopter, expected, "Adresse Ethereum du propriétaire du chien numéro 8.");
  }

  function testGetAdopterAddressByPetIdInArray() public {

  address expected = this;

  address[16] memory adopters = adoption.getAdopters();

  Assert.equal(adopters[8], expected, "Adresse Ethereum du propriétaire du chien numéro 8.");
  }
}

Il n’y a plus qu’à lancer notre test :

truffle.cmd test

Si tout s’est bien passé, vous devriez voir ceci :

Si vous avez un message d’erreur, tentez avant tout de relancer Ganache puis de relancer votre test, il se pourrait que vos tests précédents aient un peu trop utilisés l’ETHER.

L’étape 1 touche à sa fin, Bravo ! vous savez désormais comment créer un contrat, le compiler, le déployer, et le tester, grâce au langage Solidity et au Framework Truffle.

Ce qu’il faut absolument retenir :
– Un smart contract est, de façon très très schématique une classe avec des variables (qu’il faut déclaré avant utilisation) et des fonctions, écrit dans le langage Solidity
– Chaque fonction doit déclarer le type variable reçu et retourné
– Truffle nous permet de tester, compiler et déployer notre contrat intelligent dans la blockchain, sans effort.
– Ganache nous permet de simuler une Blockchain localement.

Nous verrons dans l’étape 2 comment développer l’interface web pour interagir avec la blockchain Ethereum et les smart contracts, grâce à Web3, une API Javascript pour communiquer avec une blockchain par l’intermédiaire d’une page web. Nous verrons aussi qu’il est nécessaire d’avoir une extension dans notre navigateur qui nous donnera ainsi une adresse Ethereum utilisable dans les dApp, pour être sûr d’être authentifié en utilisateur unique.

6 Commentaires

  1. I have this Error: when I run the command : truffle.cmd test
    Error: Could not find artifacts for path\Adoption.sol from any sources
    It is definitely not about the name of file and contract, because they are the same.

    /*
    Truffle v5.0.2 (core: 5.0.2)
    Solidity v0.5.0 (solc-js)
    Node v10.15.0
    */

  2. Hello,

    avec une version plus récente de solidity j’ai le message d’erreur suivant lorsque je lance la compilation du fichier de test :
    truffle.cmd test

    Compiling .\contracts\TestAdoption.sol

    /2. pet-shop-tutorial/contracts/TestAdoption.sol:18:9: TypeError: Type contract TestAdoption is not implicitly convertible to expected type address.
    address expected = this;
    ^———————^

    Quelqu’un a une idée pour corriger le pb ?

    Merci d’avance !

  3. CompileError: /C/cryptocar/contracts/Adoption.sol:13:47: TypeError: Data location must be « memory » for return parameter in function, but none was giv
    en.
    function getAdopters() public view returns (address[16]) {

    can you help me ?

    • Yes, since the Solidity 0.5 version, you should give the place for the return function :
      Try this one for the last solidity version :
      pragma solidity >=0.4.22 <0.9.0;

      contract Adoption {

      address[16] public adopters;

      function adopt(uint petId) public returns (uint) {
      require(petId >= 0 && petId <= 15); adopters[petId] = msg.sender; return petId; } function getAdopters() public view returns (address[16] memory) { return adopters; } }

LAISSER UN COMMENTAIRE

Please enter your comment!
Please enter your name here

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.