# Introduction
Qu'est-ce que la blockchain ? Si vous ne le savez pas, vous devriez ; si vous le savez, il est probable que vous ayez encore besoin de précisions sur son fonctionnement. Cette technologie risque de changer le monde et ne représente rien de moins que la deuxième génération de l'internet et a le potentiel de transformer l'argent, les affaires, le gouvernement et la société. Pour ne pas râter ce virage, les entreprises d'aujourd'hui doivent s'y intéresser de près et ne pas se laisser distancer par des potentielles nouvelles licornes qui peuvent devenir les géants de demain. C'est dans cette optique que les développeurs devraient, à minima, connaître cette technologie et pourquoi pas s'y spécialiser se préparant à une demande forte dans les prochaines années.
Ce cours est destiné à des développeurs/étudiants de niveau intermédiaire et vise à apporter des connaissances afin de les aider à mener à bien leurs futurs projets dans le domaine de la blockchaine. Ainsi dans ce cours de ==solidity== nous allons commencer par essayer de comprendre ce qu'est une blockchaine et tous les mechanismes qui régissent son fonctionnement, puis nous allons nous intéresser à la partie fun de ce cours -Coder :smile:-. Durant ce cours nous allons expliquer la différence entre les langages qu'on connait habituellement et ==solidity==, nous allons apprendre comment utiliser les fonctions, manipuler leurs portées, créer des contrats, les déployer, les tester, les connecter à une interface utilisateurs...
Ce cours sera accompagné de vidéos expliquant certains aspects parallèls à notre apprentissage, tel que la création des NFTs ou la connexion à un portefeuille ==metamask== -Et pas de panique, Nous allons nous faméliariser avec tous ces termes durant ce cours :smiley:-
A la fin, avec les connaissances que vous aurez acquises, vous serez capables de travailler sur un projet solidity ou plus globalement vous saurez vous adapter pour le faire sur un projet blockchaine quelconque.
HERE WE GO!!!
# Principe
La blockchain est un moyen de stockage de données numériques, c'est comme un coffre-fort numérique.
Ce stockage de données doit être :
- Pérenne : c'est-à-dire que les données seront toujours là tant que la blockchain fonctionne toujours.
- Infalsifiable : Personne ne peut les modifier même pas le développeur de la blockchain, une fois qu'elles sont rentrées dans la blockchain elles vont rester comme ça pour toute la vie de la blockchain.
- Architecture distribuée : les utilisateurs sont reliés entre eux (ou plusieurs d'entre eux) ce qui assure le bon fonctionnement du réseau même si des utilisateurs se déconnectent ou disparaissent.

Coffre-fort numérique central où les données sont envoyées et stockées et renvoyées à la demande.Serveur-Client : Google, facebook… | Centralisé |
| --- | --- |
|
**Données partagées entre des nœuds type P2P, torrents…** | **Decentralisé** |
|
**Chaque client est lié à plusieurs voisins interconnectés type BlockChain** | **Distribué** |
Alors pourquoi on connait la blockchain, pourquoi on en parle tout le temps ? C'est parce que c'est ce qui permet de stocker les données des transactions qui ont eu lieu en bitcoin depuis la création du bitcoin, et jusqu'à présent ça n'a pas été falsifié, personne n'a réussi à trouver un moyen de frauder, ou à se donner des bitcoins qu'il n'a pas, et donc c'est prouvé que c'est fiable dans le temps.
1. L'outil qui fait tout marcher, la fonction de hachage (en algorithme SHA-256) :
- Qu'est ce que c'est :
Permet à partir de n'importe quel type de fichier (texte, vidéo…) d'obtenir une empreinte(hash) qui est une petite chaine de caractères en hexadécimal (généralement 64 caractères =\> 256 bits)
- Ça sert à quoi :
On ne sait pas prédire ce qu'une fonction de hashage va donner à partir d'un fichier mais elle va toujours donner le même résultat. Et donc cette fonction permet avec une toute chaine de caractères de reconnaître un fichier parmi tous les fichiers présents à l'instar de l'ADN, qui permet d'identifier une personne à partir d'un tout petit volume.
- Cas d'utilisation :
- Signer le hash revient à signer le fichier : Pas besoin d'envoyer/échanger un fichier volumineux pour le signer numériquement, il suffit de signer son hash.
- Stocker des mots de passe.
- Vérifier l'intégrité des fichiers : en calculant le hash de deux ou plusieurs fichiers puis les comparant on peut facilement savoir si le fichier a été modifié ou pas.
- Créer une blockchain « évidemment ».
- Encore plus loin :
- Les portes logiques :
Pour comprendre comment fonctionnent « partiellement » les fonctions de hashage, il faudra comprendre ce que c'est les portes logiques ou au moins avoir une vague idée.
Une porte logique est une fonction qui prend en entrée une ou plusieurs valeurs de bit puis génère un bit de résultat selon les valeurs entrées.
Dans le tableau suivant on vous présente les portes logiques les plus utilisées (il en existe d'autres bien sûr)

Maintenant qu'on connait un peu les portes logiques, voyons voir comment ça fonctionne :
La fonction de hashage exécute deux opérations distinctes :
- Le pré-traitement qui consiste à :
- Rendre le message en entrée compatible au SHA-256 « en le complétant avec des 0 s'il le faut ou si la taille dépasse 256bits, on découpe en morceaux de 256, on fait le traitement puis on les ajoute à la fin et on refait la même opération ».
- Initialiser des variables de travail.
- La génération d'un tableau à partir du message complété en exécutant la 2ème opération, Le calcul du condensé :
A partir d'un message de 256bits, on coupe en 8 morceaux de 32bits qui sont les input « les lettres i dans le schéma », et on exécute le schéma en dessous en boucle 64 fois :

- K est une constante itérative qu'on ajoute à chaque tour et qui change selon la valeur de t.
- W le t « ième » mot du tableau généré du message.
- Puis les symboles :

ROTR « Rotate Right » est l'action de la rotation à droite où x est un mot de 32bits.

Ce symbole signifie le décalage binaire à droite de 2 pas, en ajoutant 2 zéros à gauche et supprimant le surplus à droite.
- Encore plus loin :
Le choix de ce type de hashage est tout simplement dû au risque infiniment petit qu'il y ait des collisions. Jusqu'à présent on n'a jamais réussi à trouver 2 fichiers différents qui donnent le même hash, et on ne sait même en fabriquer non plus. Par exemple, une équipe chinoise en 2004 a réussi à développer un programme qui calcule et produit des collisions pour MD5 en une heure. Le Sha-256 reste imbattable malgré le manque de preuves mathématiques tangibles qu'il est difficile ou impossible de calculer des collisions.
2. La blockChain
Un ensemble de données numériques regroupées au sein d'une suite de blocs ordonnée car chacun contient « commence par » le hash du bloc précédent et se termine par un hash calculé selon certaines règles, ainsi chaque bloc trouve sa position relative par rapport au précédent.

- Le bloc:
Un bloc est constitué essentiellement des éléments suivants : (on peut avoir plus d'éléments selon le type de la blockchain)

Chaque bloc correspond à :
1. Bloc 1335 : l'identifiant du bloc
2. Le hash du bloc précédent
3. Les données qu'on souhaite stocker dans la blockChain
4. a signature du créateur du bloc en cours
5. Le résultat d'un calcul afin de produire le hash du bloc en cours
Le hash fini du bloc en cours
Le nouveau bloc sera accepté après avoir effectué des calculs longs « preuve de travail », ces calculs garantissent une latence pour éviter que des blocs valides soient créés rapidement.
- La preuve de travail :
Pour qu'un bloc soit accepté on doit créer, ajouter, changer des caractères afin de modifier le hash du bloc pour répondre à certaines conditions.
En pratique, on fixe une valeur v (le nombre de 0 par lesquels le hash du bloc doit commencer), et le hash doit être inférieur à cette valeur.
| Imaginons qu'on souhaite créer bloc avec un hash qui commence par 8 zéros |
| --- |
|    |    |  |
| On prend notre bloc et on rajoute un mot « Test », on obtient un hash qui commence par un seul zéro « 7 = 0111 en binaire », donc on doit recalculé un nouveau hash | Cette fois ci, on obtient un hash qui commence par 4 zéros. Mais toujours pas 8. | Finalement on obtient 8 zéros en binaire ce qui nous permet d'envoyer ce bloc sur le réseau pour qu'il soit vérifié et validé par les validateurs |
Pour obtenir un hash valide il faut « actuellement pour le bitcoin» en moyenne 271 calculs de la fonction SHA256. Un simple ordinateur prendra des milliers d'années, tandis que ça prend 10min à l'ensemble des mineurs du réseau Bitcoin.
Et si plusieurs blocs sont validés en même temps ?
C'est extrêmement rare car la probabilité gaussienne de la fonction sha256 démontre qu'il faut 10min en moyenne au maximum de la densité du nombre de validateur pour créer un hash valide. Mais ce n'est pas impossible, dans ce cas, plusieurs versions de la blockchain vont circuler, et elles auront un ou plusieurs blocs différents à la fin de leur chaine. Ces différentes blockchains continueront à se développer et en ajouter des blocs chacune de son coté jusqu'au moment où elles rentrent en conflit, à ce stade, on garde la chaine la plus longue, et les données de la/les chaine(s) courte(s) retournent à la liste des données non-ordonnées et feront l'objet -à nouveau- de validation. Ces opérations généralement prennent quelques minutes ou plusieurs dizaines de minutes

----
# Solidity

## Les contrats
Le code des contrats est écrit avec un langage nommé SOLIDITY. Ce code est encapsulé à l'intérieur du contrat, c'est la partie centrale de toute application basée sur ethereum.
**Toutes les variables, toutes les fonctions appartiennent au contrat.**
Un exemple de contrat vide est:
```solidity=
pragma solidity >=0.5.0 <0.6.0;
contract EmptyContract {
}
```
Il est nécessaire de spécifier la version de solidity utilisée au début du contrat pour éviter les problèmes d'incompatibilité du code avec de futures versions.
## Les bases
### Les variables
Dans Solidity, des variables d'état sont utilisées afin de stocker les données du contrat. C'est comme utiliser une base de donnée.
```solidity=1
contract ExempleVar {
// Stocker de manière permanante dans la blockchain
uint walletAmount = 10000;
}
```
Ici on a déclaré une variable de type entier non signé, c'est à dire un nombre entier toujours positif.
On peux aussi déclarer des entiers négatif avec `int`.
#### Les types des données
Pour comprendre la logique, nous devons passer en revue les types de données de Solidity, pas seulement les choses ordinaires, mais les caractéristiques de ces types. Cet exercice devrait être utile aux développeurs de Smart Contract.
Solidity est un langage typé, ce qui signifie que le type de chaque variable (état et locale) doit être spécifié à la compilation. Les types sont divisés en types simples (types de valeur) et types de référence. La différence entre eux est que lorsque vous assignez ou transférez un paramètre à une fonction, la première copie est la valeur qui y est stockée, et la seconde ne fait que transmettre une référence à l'endroit où la valeur à laquelle ils se réfèrent (point) est stockée. Nous allons les analyser :
##### Booléens
Ce type peut stocker une des deux constantes VRAI ou FAUX. En fait, il existe une conversion implicite des types dans Solidity et les valeurs numériques supérieures à zéro, se réfère à TRUE, et 0 à FALSE.
##### Entiers
int*/uint* : Entiers signés et non signés de différentes tailles. Les différences entre les types signés et non signés sont les suivantes : int8 peut prendre des valeurs de -127 à 128, et uint8 - de 0 à 255. * signifie un nombre, qui indique la taille de ce type numérique de 8 à 256. Par exemple : int16, uint32, uint128, int256. Mais vous pouvez écrire sans numéro et ce sera la même chose que int/uint - int256/uint256.
Vous pouvez vous demander pourquoi nous avons besoin de ces nombres ? C'est nécessaire pour une utilisation économique de la mémoire vu qu'elle coûte de l'argent à chaque utilisation.
Solidity dispose d'une conversion automatique de type. C'est le cas lorsqu'au lieu d'une définition de type explicite, on écrit le mot-clé var (comme en JavaScript), et que le compilateur essaie de définir et de lui attribuer un type.
Regardons ce code de près:
```solidity=
var i = 0; // sera pris comme “uint8 = 0;”
for(var i = 0; i < 3000; i++) {}
```
Cette écriture ne provoquera pas d'erreur, la boucle va juste tourner à l'infini, car le type uint8 ne peut pas contenir une si grande valeur de 3000. Donc à chaque fois que `i == 255` la boucle reprendra à 0, faisant le tour de la mémoire.
```
##### Enums
Les Enums dans Solidity sont un moyen de créer des types définis par l'utilisateur. Les Enums sont explicitement convertibles en types entiers, mais pas implicitement. Les valeurs des Enums sont numérotées dans l'ordre où elles sont définies, en commençant par 0.
Les Enums ne font pas partie de l'ABI (Application Binary Interface - on développera ce sujet plus loin dans le cours, mais il s'agit essentiellement de la façon dont vous encodez le code Solidity pour la machine virtuelle Ethereum, et comment vous récupérez les données). Cela signifie que si votre fonction renvoie un enum par exemple, il sera automatiquement converti en uint8 dans les coulisses. Le nombre entier retourné est juste assez grand pour contenir toutes les valeurs de l'enum. Avec plus de valeurs, la taille augmente également (uint16 et plus).
Le code ci-dessous, extrait de la documentation de Solidity, définit un enum avec quatre valeurs possibles, crée une variable de cet enum nommée choice et une constante appelée defaultChoiceth qui contiendra une valeur par défaut.
```solidity=
enum ActionChoices { GoLeft, GoRight, GoStraight, SitStill }
ActionChoices choice;
ActionChoices constant defaultChoice = ActionChoices.GoStraight;
```
Maintenant nous pouvons définir quelques fonctions pour interagir avec notre enum.
```solidity=
function setGoStraight() public {
choice = ActionChoices.GoStraight;
}
function setChoice(ActionChoices newChoice) public {
choice = newChoice;
}
```
La première définit simplement le choix sur GoStraight, tandis que la seconde le définit sur le choix que l'appelant transmet à la fonction. Après le déploiement, la fonction setChoice va attendre une valeur uint8, qui correspondra à la valeur de l'enum déclarée à ce numéro.
Si nous voulons récupérer la valeur de `choice` et `defaultChoice`, nous pouvons définir les fonctions suivantes:
```solidity=
function getChoice() public view returns (ActionChoices) {
return choice;
}
function getDefaultChoice() public pure returns (uint) {
return uint(defaultChoice);
}
```
#### Conversion de type
Solidity vous permet de convertir entre les types de données. "cette conversion passe par le *cast*" Par exemple :
```solidity=
uint8 val8_1 = 5;
uint val = 6;
// Lève une erreur parce que val8_2 * val va retourner explicitement uint et pas uint8
uint8 val8_2 = val8_2 * val;
// Il faudra 'caster' val en uint8 pour que ça fonctionne
uint8 val8_2 = val8_2 * uint8(val);
### Opération mathématique
Il est simple de procéder à des opérations mathématiques avec solidity.
Il est possible de faire des:
- addition: `12 + 1`
- soustraction: `x - y`
- multiplication: `23 * 4`
- division: `12 / 3`
- modulo: `20 % 4`
- puissance: `5 ** 2 = 25`
### Boucle
La syntaxe des boucles For est similaire a celle de Javascript :-1:
```solidity=
//Incremente de 1 à 10
for (uint i = 1; i <= 10; i++) {
//Traitement
}
```
### Structure
Il est possible de créer des structures de données plus avancées que de simples variables de chiffre ou de lettre.
Par exemple, si on veut modéliser une transation, on mettra plusieurs propriétées telles que:
```solidity=
struct Transaction {
string from;
string to;
uint amount;
}
```
### Tableau
Les tableaux sont aussi disponibles sur Solidity.
On peut faire des tableaux de taille fixe ou dynamique.
On peut ausssi évidemment faire des tableaux de structures.
On peut ajouter public devant le tableau afin d'avoir des getters / setters sur ce dernier.
```solidity=
uint[2] fixedSizeIntArray;
string[5] fixedSizeStringArray;
uint[] dynamicIntArray;
Transaction[] privateTransactions;
Transaction[] public publicTransactions;
```
### Challenge I
#### énoncé
Créer un contrat, et nommer le `Auction`, la version de ce contrat doit être supérieure à 0.7.0 et inférieur à 0.9.0
Créer une variable d'état nommée : `bidIncrement` de type entier
Créer un Enum nommé State contenant 4 valeurs : Started, Running, Ended, et Canceled.
Créer un State nommé auctionState.
#### Solution proposée
```solidity=
pragma solidity >=0.7.0 <0.9.0;
contract Auction {
uint bidIncrement;
enum State {Started, Running, Ended, Canceled}
State public auctionState;
}
```
## Les fonctions
La déclaration d'une fonction en solidity est telle que:
- le mot clé function
- le nom de la fonction
- argument commençant par _
- un argument peut être précédé du mot clé memory pour spécifier que la variable est une copie et non un pointeur
- visibilité: public / external / private / internal
- le code entre { ... }
```solidity=
// fonction nom(arg) visibilité { code }
function nomDeLaFonctionEnCamelCase(int memory _arg1, string _arg2) public {
// code avec return
}
```
### Constructor
Comme dans les autres langages, le constructeur est une fonction unique exécutée directement après la création du contrat et avant son déploiement sur la blockchaine. Cette execution protège toute fonction appelée par le constructeur d'être dévoilée lors du déploiement. si on veut le contract abstrait *parfois on a besoin de ce genre de comportement pour protéger le constructeur s'il est soumis à un Builder -un design pattern assez connu dans le langage objet-* il suffit de le marquer de portée `interne` sinon il est de portée `public` par défaut, et s'il ne le définit pas, il sera présent avec 0 instruction.
```solidity=
pragma solidity >=0.7.0 <0.9.0;
// Un contrat avec un constructeur sans arguments
contract Vide{
constructor() public {
}
}
/* Un contrat avec un constructeur avec argument(s)
> Quand on crée ce contract on est obligé de passer l'argument de manière direct
*/
contract AvecArguement{
string argument;
constructor(string _argument) public {
argument = _argument;
}
}
contract AvecArgumentDirect is AvecArgument("mon super argument DIRECTE") {
constructor() public{
// Autres instructions
}
}
// Ou indirect, dans ce cas ce contrat va lui aussi être abstrait
contract AvecArgumentIndirect is AvecArgument() {
constructor(string _argumentAPasser) AvecArgument(_argumentAPasser* _argumentAPasser) public {
// Autres instructions
}
}
```
### keccak256
keccak256 est une fonction de hachage intégrée à Ethereum et est une version de SHA3. Elle a de nombreuses fonctions dans Ethereum, et la plus importante est la génération de nombre aléatoire pour l'utilisateur.
Notez que keccak256 attend un seul paramètre de type bytes. Cela nous oblige à "emballer" nos paramètres avant d'appeler keccak256 :
```solidity=
//6e91ec6b618bb462a4a6ee5aa2cb0e9cf30f7a052bb467b0ba58b8748c00d2e5
keccak256(abi.encodePacked("aaaab"));
//b1f078126895a1424524de5321b339ab00408010b7cf0e6ed451514981e58aa9
keccak256(abi.encodePacked("aaaac"));
```
### Les modificateurs
#### Modificateurs de visibilité
Comme dans la plupart des langages solidity fournit une palette de modificateurs de visibilié avec lesquels on contrôle qui, quand et d'où on peut appeler la fonction
##### private
La fonction ne peut être appelée que de l'intérieur de du contrat.
##### internal
Le fontion peut être appelée depuis l'intérieur du contrat en plus des contrats qui en héritent.
##### external
La fonction ne peut être appelé que depuis l'extérieur du contrat.
##### public
Comme dans tous les langages où on trouve ce terme, la fonction peut être appelée de n'importe où.
> Si une fonction est privée, elle est accessible uniquement dans le contrat En internal un contrat A qui va heriter d'un autre contrat B contenant des fonctions internal, aura accès au fonction internal du contrat B, en privée l'accès n'aurait pas été possible.
>
#### Modificateurs d'état:
##### view
Ce modificateur nous certifie que l'appel de cette fonction ne modifie aucune donnée. Cela permet à ce type de fonctions d'être appelées sans coûter de frais de gas puisqu'elles ne font que lire sur la blockchain.
Il faut noter que si cette fonction est appelée par une fonction interne qui n'est pas `view`, alors la fontion coûtera du gas car la fonction appelante créera une transaction sur la blockchain ce qui conduit à la vérication des fonctions appelées par chaque noeud.
Les fonctions `view` sont des fonctions en lecture seule, ce qui garantit que les variables d'état ne peuvent pas être modifiées après les avoir appelées.
Si les instructions qui modifient les variables d'état :
- émettent des événements,
- créent d'autres contrats,
- utilisent la méthode d'autodestruction,
- transfèrent des éthers via des appels,
- appellent une fonction qui n'est pas « view ou pure »,
- utilisent des appels de bas niveau, etc.
==> le compilateur emettra un avertissement.
Par défaut, une méthode get est une fonction view.
```solidity=
function getResult(
) public view returns(uint product, uint sum){
uint num1 = 10;
uint num2 = 16;
product = num1 * num2;
sum = num1 + num2;
}
```
##### pure
Ce modificateur permet à la fonction d'exécuter et retourner une valeur à partir des arguments données sans avoir accès aux données du contrat. Elle peut aussi coûter du gas si elle est appelée par une fonction qui demande validation sur la blockchain.
Les fonctions pures ne lisent pas et ne modifient pas les variables d'état, elles renvoient les valeurs uniquement en utilisant les paramètres passés à la fonction ou les variables locales présentes dans celle-ci.
Si les instructions présentes dans des fonctions pures qui lisent les variables d'état :
- accèdent à l'adresse ou au solde,
- accèdent à n'importe quel bloc de variable globale ou msg,
- appellent une fonction qui n'est pas pure, etc.
==> le compilateur émet un avertissement.
```solidity=
function getResult(
) public pure returns(uint product, uint sum){
uint num1 = 2;
uint num2 = 4;
product = num1 * num2;
sum = num1 + num2;
}
```
#### Un modificateur spécial
##### Payable
La co-existence de l'Ether "La monnaie de la blockchain Ethereum" et les contrats sur la même blockchain facilite grandement le paiement tout en exécutant une fonction *Oui oui, on peut payer un service (fonction), demander des frais..., au moment de son exécution*
``` solidity=
contract ChainedStore {
function buySoftwareLicense(uint _idSoftware) external payable {
require(msg.value == 2 ether);
transferLicense(msg.sender, _idSoftware);
}
}
```
> Dans cette exemple on appelle la fonction de l'extérieur `external` en lui passant le paramètre `_idSoftware`, et en indiquant combien d'*ether* en souhaite payer. Le require assure la validité de la valeur payée sinon il retourne null et arrête l'exécution. Puis on execute la fonction `transferLicence()`
> L'appel de la fonction peut venir d'une interface js de la DApp **Decentralized application** comme suit :
```javascript=
ChainedStore.buySoftwareLicense({from: web3.eth.defaultAccount, value: web3.utils.toWei(1)})
```
Si on a assez d'*ether* alors on vient d'effectuer une transaction à partir d'une interface js et en contrepartie on a reçu la valeur demandée.
Si la fonction n'est pas `payable`, elle n'exécute rien et la transaction échoue.
### Événements
Les événements permettent à votre contrat de communiquer avec le front-end de votre application, qui peut écouter certains événements et effectuer des actions lorsqu'elles se produisent.
```solidity=
// declare the event
event IntegersAdded(uint x, uint y, uint result);
function add(uint _x, uint _y) public returns (uint) {
uint result = _x + _y;
// fire an event to let the app know the function was called:
emit IntegersAdded(_x, _y, result);
return result;
}
```
Une implémentation JavaScript de cet événement sur le frontend pourrait ressembler à ceci :
```javascript=
YourContract.IntegersAdded(function (error, result) {
// do something with result
});
```
### Temps
En solidity le principe de temps se gère avec des unités. Il peut permettre d'instaurer des temps de latence entre chaque utilisation de fonctions par exemple.
Il existe en solidity, différnetes fonctions permmettant prinicpalement d'avoir des informations sur nos smart contracts.
La variable now retourne l'horodatage unix actuel.
Une approche encore plus sécurisée et se base sur la notion de la solidité de la blockchaine et de compter à partir du numéro du bloc. On sait qu'un bloc est validé toutes les 15 secondes sur la blockchaine Ethereum. donc au lieu de compter sur un timestamp manipulable sur les machines des hotes, on peut tout simplement compter le nombre de blocs construits depuis celui qui nous intéresse puis multiplier ce nombre par 15 pour avoir comme résultat le nombre de secondes passés depuis le bloc souhaité.
### Challenge II
#### Enoncé
Dans ce challenge on va nous intéresser à la création des fonctions en utilisant les outils qu'on a vu dans le dernier chapitre.
- Créer une constante nommée ONE_WEEK de type uint_256 avec une valeur de 40320, cette pratique doit être éviter dans vos projets futurs, car une telle déclaration coûte de l'espace mémoire et donc de l'argent par conséquent.
- Créer une variable de type uint nommée startBlock et qui va être public, cette variable va être utilisée pour stocker le numéro du bloc dans lequel va se trouver le contrat au moment de sa création.
- De même, créer une variable endBlock.
- Créer un constructeur public qui va :
- Dans un premier temps, initier la valeur de la variable du startBlock.
- Setter la valeur de endBlock bassée sur startBlock et ONE_WEEK faisant en sorte que le dernier block visée soit créé une semaine après le startBlock.
- En récupérant le code du challenge I, initier la variable auctionState à Running, et la variable bidIncrement à 100.
- Créer une fonction pure, et interne nommée `min` qui prend deux arguments `uint value1, uint value2` et qui retounre un uint. La valeur retournée doit être la plus petite des deux.
- Creer une fonction nommée cancelAuction qui set la variable `auctionState` à `Canceled`
#### Solution proposée
```solidity=
address payable public owner;
//lorsque l'enchère démarre, on a le timestamp(15sec blocktime)
uint public startBlock;
uint public endBlock;
uint public constant ONE_WEEK = 40320;
uint bidIncrement;
constructor(){
auctionState = State.Running;
startBlock = block.number;
endBlock = startBlock + ONE_WEEK;
bidIncrement = 100;
}
function min(uint value1, uint value2)pure internal returns(uint){
if(value1 <= value2) {
return value1;
}
return value2;
// il est possible d'utiliser les conditions ternaires pour retourner une valeur en solidity comme ceci:
// return value1 <= value2 ? value1 : value2;
}
function cancelAuction() public onlyOwner {
auctionState = State.Canceled;
}
```
----
## Notions solidity
### Mapping & Adresse
La blockchain Ethereum est comme une banque constituée de comptes. Un compte à un montant d'Ether (monnaie utilisée sur la blockchain Ethereum), et vous pouvez effectuer des transaction avec cette derniere. Un identifiant unique est associé à chaque compte (address) comme celui-ci : 0x0cE446255506E92DF41614C46F1d6df9Cc969183
```solidity=
// uint correspond au solde d'un compte utilisateur :
mapping (address => uint) public accountBalance;
// Où il permet de rechercher un nom d'utilisateur.
mapping (uint => string) userIdToName;
```
Le mapping est un stockage clé-valeur pour enregister et rechercher des données. Dans cette exemple, on retrouve le couple clé-address qui a pour valeur uint et le couple clé-uint de valeur string.
### Variable globales
#### msg
Dans Solidity, il existe des variables et des fonctions spéciales. Les variables et fonctions spéciales sont toujours disponibles globalement et sont principalement utilisées pour fournir des informations sur la blockchain (c.-à-d., transactions, informations sur les contrats, informations sur les adresses, gestion des erreurs, fonctions mathématiques et cryptographiques).
Les variables globales msg en particulier sont des variables globales spéciales qui contiennent des propriétés permettant d'accéder aux contrats de la blockchain, à leurs fonctions et à leurs valeurs, il en existe plusieurs et les principales sont:
##### msg.data
Le calldata complet qui est une zone non-modifiable, non persistante où les arguments de la fonction sont stockés et se comportent principalement comme la mémoire
##### msg.gas
Renvoie le gaz disponible restant pour une transaction en cours
##### msg.sig
Les quatre premiers octets des données d'appel d'une fonction qui spécifie la fonction à appeler (c'est-à-dire l'identifiant de la fonction).
##### msg.value
Le montant de wei envoyé avec un message à un contrat (wei est une dénomination d'ETH)
```solidity=
function isMyValueBiggerThanOne() public returns (bool) {
// Mettre à jour notre mappage `number` pour stocker `_myNumber` sous `msg.sender` (adresse de l'appelant)
return msg.value > 1;
// ^ La syntaxe pour stocker des données dans un mappage est la même qu'avec les tableaux
}
```
##### msg.sender
Fait référence à l'address de la personne (ou du smart contract) qui a appelée la fonction actuelle.
Un contrat va juste rester là dans la blockchain à ne rien faire jusqu'à ce que quelqu'un appelle une de ses fonctions. Pour l'appeler un msg.sender est obligatoire.
Voici un exemple d'utilisation de msg.sender pour mettre à jour un mapping.
```solidity=
mapping (address => uint) favoriteNumber;
function setMyNumber(uint _myNumber) public {
// Mettre à jour notre mappage `number` pour stocker `_myNumber` sous `msg.sender` (adresse de l'appelant)
number[msg.sender] = _myNumber;
// ^ La syntaxe pour stocker des données dans un mappage est la même qu'avec les tableaux
}
function whatIsMyNumber() public view returns (uint) {
// On récupère la valeur stockée à l'adresse de l'expéditeur
// Qui sera `0` si l'expéditeur n'a pas encore appelé `setMyNumber`
return favoriteNumber[msg.sender];
}
```
Dans cet exemple trivial, n'importe qui pourrait appeler setMyNumber et stocker un uint dans notre contrat, qui serait lié à son adresse. Ils pourraient ensuite appeler whatIsMyNumber, et ils auraient en retour le uint qu'ils ont stocké.
Utiliser msg.sender apporte de la sécurité à la blockchain Ethereum - la seule manière pour quelqu'un de modifier les données d'un autre serait de lui voler sa clé privée associée à son adresse Ethereum.
#### require.require
Comment pouvons-nous faire pour qu'une fonction soit appelée seulement si certaines conditions sont vérifiées ?
Pour cela, nous allons utiliser require.require qui va faire en sorte que la fonction s’arrête et renvoie une erreur si ces conditions ne sont pas respectées :
```solidity=
function sayHiToVitalik(string _name) public returns (string) {
// Regarde si _name est égal à "Vitalik". Renvoie une erreur et quitte si ce n'est pas le cas.
// (Remarque : Solidity n'a pas de comparateur de `string` nativement,
// nous comparons donc leurs hachages keccak256 pour voir si les `string` sont égaux)
require(keccak256(_name) == keccak256("Vitalik"));
// Si c'est vrai, on continue avec la fonction :
return "Hi!";
}
```
Si vous appelez la fonction avec sayHiToVitalik("Vitalik"), elle va renvoyer "Hi!". Si vous l'appelez avec un autre argument, elle va renvoyer une erreur et ne elle ne va pas s’exécuter.
Ainsi require est pratique pour vérifier que certaines conditions soient vraies avant d'exécuter une fonction.
### Stockage vs mémoire
En solidity, il est possibles d'enregistrer ses variables à deux localisations différentes, le storage (stockage) et la memory (mémoire). L'utilisation efficace de la mémoire est l'un des concepts les plus déroutants de Solidity.
La comparaison entre les deux est comme le disque dur pour le *storage* et la mémoire vive pour *memory*. La première étant utilisée pour stocker des variables permanentes dans le temps tandis que la deuxième est ephèmere, les variables stockées en mémoire sont éffacées à l'appel de fonction extérieure au contrat.
> Le *storage* est emmagasiné sous forme de clé-valeurs de 32 octets chacune.
> *Memory* est un tableau d'octets extensibles par tranche de 32 octects en essayant d'accéder ou de stocker à des indices dépassant la taille actuelle. Cette mémoire disparait immédiatement après l'exécution de la fonction. Et on conseille de minimiser au maximum sa taille de départ quand cela est possible.
>> Il existe également un 3ème type *pile* : utilisé spécialement pour contenir un nombre limité de variable localement utilisées et coûte autant que *memory*
Par défault, Solidity gère seul l'attribution de la mémoire, les variables déclarées en dehors des fonctions sont par défaut storage alors que les variables déclarées à l'intérieur des fonctions sont memory.
Toutefois, il est possible de forcer l'enregistrement dans l'une des deux localisations différentes. Les mots cléfs dont "storage" et "memory".
A l'instar de la plupart des langages de programmation, chaque fonction coûte un certain montant (argent réel), de ce faites il est préfarables dans certains cas de reconstruire un tableau dans la mémory car appeler une fonction pour récuperer ce tableau depuis le storage est couteux. Ou d'utiliser une fonction external view, puisqu'une view ne coute rien.
Il ne faut jamais oublier que lorsqu'on écrit, ou change un bout d'information dans la Blockchain, c'est de manière définitive. Cette masse d'informations va être stocker sur les disques durs de milliers de personnes à travers le monde et tout ceci coute de l'argent.
### Gas
Un gas est une unité de "monnaie d'échange" qui est dépensé à chaque fois qu'une fonction de notre DApp est utilisée, ou une valeur est stockée.
Au niveau du stockage on a deux coûts différents selon le type de la mémoire sollicitée:
- *Storage* : le principe est simple:
- 20 000 gas quand la valeur est non-nulle à partir de 0.
- 5 000 gas quand on écrit une valeur non-nulle dans le stockage existant ou quand on remet une valeur à 0.
- 5 000 gas est remboursés lorsqu'une valeur non-nulle est remise à zéro.
> Il est bien sûr possible d'économiser de la mémoire en l'occupant au maximum, ainsi, au lieu de stocker 32 uint_8 séparément dans 32 clés, on peut tous les réunir dans une seule case de uint_256. On peut faire de même avec les 16,32...
- *Memory* : La règle exposée dans le <span style="color:#00ACFF">*YellowPaper*</span> est la suivante :
> 
> Cette formule décrit un coût de gaz pour la quantité totale de mémoire allouée dans un appel de contrat (c'est-à-dire le plus grand emplacement de mémoire qui contient une valeur non nulle. La mise à zéro de la mémoire après son utilisation ne diminue pas la quantité totale de mémoire allouée).
> Toujours d'après le <span style="color:#00ACFF"><a href="https://ethereum.github.io/yellowpaper/paper.pdf">*YellowPaper*</a></span> Gmemory est une constante égale à 3.
> `a` est l'emplacement mémoire maximum écrit dans un appel de contrat. Notez que a est exprimé en mots de 32 octets.
Notez bien que l'utilisation de la *memory* reste insignifiante comparé au *storage* donc, préviligiez la bonne pratique d'effectuer vos calculs intermédiaires en utilisant la mémoire avant de mettre le résultat final dans le *storage*.
La quantité de gas dépensée pour une fonction dépend de la complexité de celle-ci.
En effet l'optimisation de notre code en solidity est importante pour faire baisser les coûts en gas.
Vous pouvez savoir plus sur le gas <span><a href="https://www.cryptocompare.com/coins/guides/what-is-the-gas-in-ethereum/">**ici**</a></span>.
### plusieurs valeurs de retour
Il est possible d'attribuer plusieurs valeurs de retour a nos fonctions.
```solidity=
function multipleReturns() internal returns(uint a, uint b, uint c) {
return (1, 2, 3);
}
```
Dans cette exemple multipleReturns() retournera a = 1, b = 2, c = 3. Afin de récuperer les differentes données il faut faire comme ceci :
```solidity=
function processMultipleReturns() external {
uint a;
uint b;
uint c;
// C'est comme ça que vous faites une affectation multiple :
(a, b, c) = multipleReturns();
}
```
Ici, la fonction attribura aux variables les valeurs retourné par rapport a la position, c'est a dire que a = 1, etc.
Cependant si l'on change l'ordre les donées elles ne seront pas changer :
```solidity=
(c, a, b) = multipleReturns();
```
On aura c = 1, a = 2, b = 3. On peux dans le meme principe récuperer seulement la valeur voulu.
```solidity=
// Si nous voulons seulement une des valeurs ci dessus :
function getLastReturnValue() external {
uint c;
// Nous pouvons laisser les autres champs vides :
(,,c) = multipleReturns();
}
```
### Modificateur de fonction onlyOwner
Le modificateur onlyOwner ressemble à une fonction mais ne s'appelle pas de la même manière. En effet ce modificateur se place à la fin de la déclaration d'une fonction. Lorsqu'elle est appelé, le block du onlyOwner s'exécute en premier, puis le reste de la fonction en second.
Le onlyOwner permet au propriétaire uniquement de lancer la fonction.
```solidity=
//onlyOwner se place à la fin de la fonction
function setMyContractAdress(address _address) external onlyOwner{
myContract = myInterface(_address);
}
```
### Immutabilité des smart contracts
Une fois déployé, un smart contract n'est plus modifiable. On dit de celui-ci qu'il est immuable. C'est pour cette raison qu'il est nécéssaire d'avoir un contrat sécurisé, on évitera le plus possible d'écrire nos addresses en dur pour avoir la possibilité de changer en cas d'erreur.
### Contrat propriétaire
Un contrat peut être appelé par n'importe qui. On peut créer un contrat Ownable qui s'appelle uniquement par son propriétaire.
Pour se faire, on utilise la librarie Solidity OpenZeppelin. Un ownable permettra en tant que propriétaire d'avoir des privilèges spéciaux sur un contrat.
```solidity=
contract Proprietaire {
address public proprio;
event TransfererPropriteEvent(address indexed ancienProprio, address indexed nouveauProprio);
//On initiliase un contrat avec Ownable et on créé son construteur en déclarant son owner avec l'adresse du sender.
function Ownable() public {
proprio = msg.sender;
}
```
```solidity=
//On s'assure que le contrat ne peut être appelé que par l'owner.
modifier onlyOwner() {
require(msg.sender == proprio);
_;
}
```
Un `modifier` peut être ajouté à n'importe quelle fonction pour apporter la modification souhaitée, dans le code ci-dessous on va utiliser le `modifier` créé précédemment pour garantir que la fontion ne s'exécutera que de la part du propriétaire de ce contrat.
```solidity=
//On permet à l'owner de transférer le controle du contrat au nouvel owner avec son addresse
function transfererPropriete(address nouveauProprio) public onlyOwner {
require(nouveauProprio != address(0));
TransfererPropriteEvent(proprio, nouveauProprio);
proprio = nouveauProprio;
}
}
```
### Challenge III
#### Enonce
Dans ce challenge on va nous intéresser aux notions liées directement à Solidity comme on vient de voir dans chapitre, pour ce, commencez par récupérer notre contrat "Auction.sol" qui contient notre code des challenges précédents, puis:
- Créer un mapping entre l'adresse d'un compte et l'enchère en `uint` qui provient de cette adresse. Nommer là `bids`
- Créer un modifier appelé `onlyOwner()` qui garantit que le sender n'est autre que le owner du contrat courant.
- Créer un modifier appelé `notOwner` qui va empêcher le owner d'intéragir avec certaines fonctions.
- Créer un modifier appelé `afterStart` qui nous assure que l'enchère actuelle a déjà commencé.
- Créer un modifier appelé `beforeEnd` qui nous assure que l'enchère actuelle n'est pas encore finie.
- Créer une fonction public, payable qui permettra de placer des enchères. Appeler la `placeBid`, cette fontion devrait empêcher le owner d'enchérir, et doit s'assuer que l'enchère est toujours en cours, dans cette fonction, on devrait:
- S'assurer que l'enchère est dans l'état Running.
- S'assurer que la valeur envoyé par l'enchérisseur est supérieure à 100 Wei.
- Dans cette fonction il faut s'assurer que la mise de l'enchérisseur est supérieure à l'enchère la plus haute
## Notions généarles
### Héritage
Une des fonctionnalités de Solidity *et de pleins d'autres langages* qui rend les contrats plus facile à gérer est l'héritage :
```solidity=
contract First {
function hello() public returns (string) {
return "hello";
}
}
contract Anotherfirst is First {
function anotherhello() public returns (string) {
return "hello you";
}
}
```
anotherfirst hérite de First. Cela veut dire que Anotherfirst a accès a toutes les fonctions publiques de First.
### Importation
Solidity utilise le mot clé import afin d'importer des fichiers dans un autre :
```solidity=
import "./OtherContrat.sol";
```
### Utilisation d'une interface
Une interface est ce qui va nous permettre de communiquer avec un contrat externe. Imaginons un contrat Role qui permet de stocker son role. Il est associé à l'adresse Etherum grâce à msg.sender, on peut stocker son rôle ou récuperer le rôle d'une personne si l'on connait son adresse.
```solidity=
contract Role {
mapping(address => string) roles;
function setRole(string _role) public {
roles[msg.sender] = _role;
}
function getRole(address _Address) public view returns (string) {
return roles[_Address];
}
}
```
Si nous voulons lire les roles depuis un contrat externe il faudra d'abord créer une interface de Role :
```solidity=
contract RoleInterface {
function getRole(address _Address)public view returns (string);
}
```
Cele ressemble fortement a la définition d'un contract mais il n'y a pas de corps de fonction et nous déclarement seulement les méthodes que nous voulons utiliser.
## Bonnes pratiques
il est temps de considérer certains modèles de sécurité qui sont spécifiques au langage de programmation Solidity. Dans ce tour d'horizon, nous nous concentrerons sur les recommandations de développement sécurisé pour Solidity qui peuvent également être instructives pour le développement de contrats intelligents dans d'autres langages.
### Utiliser correctement assert(), require(), revert()
Les fonctions pratiques assert et require peuvent être utilisées -comme on l'a expliqué- pour vérifier des conditions et lever une exception si la condition n'est pas remplie.
La fonction assert ne doit être utilisée que pour tester les erreurs internes et pour vérifier les invariants.
La fonction require doit être utilisée pour s'assurer que les conditions valides, telles que les entrées ou les variables d'état du contrat, sont remplies, ou pour valider les valeurs de retour des appels aux contrats externes.
En suivant ce paradigme, les outils d'analyse formelle peuvent vérifier que l'opcode invalide ne peut jamais être atteint: cela signifie qu'aucun invariant du code n'est violé et que le code est formellement vérifié.
Pour aller loin <span><a href="https://swcregistry.io/docs/SWC-110">**cliquez ici**</a></span> et <span><a href="https://swcregistry.io/docs/SWC-123">**ici**</a></span>
### Utilisez les modificateurs uniquement pour les contrôles
Le code à l'intérieur d'un modificateur est généralement exécuté avant le corps de la fonction, donc tout changement d'état ou appel externe violera le pattern Checks-Effects-Interactions <span><a href="https://solidity.readthedocs.io/en/develop/security-considerations.html#use-the-checks-effects-interactions-pattern">**"Regardez la doc pour plus de détails"**</a></span>. De plus, ces déclarations peuvent également passer inaperçues pour le développeur, car le code du modificateur peut être éloigné de la déclaration de la fonction. Par exemple, un appel externe dans le modificateur peut conduire à l'attaque de *double-entrée* ou *multi$ -selon les traductions- :
```solidity=
contract Inscription {
address proprio;
function peutSInscrire(address _addresse) external returns(bool) {
// Quelques lignes de code
}
}
contract Action {
Insctiption inscription;
modifier canSubscribe(address _addresse) {
require(inscription.peutSInscrire(_addresse));
_;
}
function sInscrire() canSubscribe(msg.sender) public {
// Quelques lignes de code
}
}
```
Dans ce cas, le contrat de registre peut effectuer une attaque par "reetrancy" en appelant Action.sInscrire() à l'intérieur de peutSInscrire(). Vous devez donc traiter toute fonction qui appelle un contrat non fiable comme étant elle-même non fiable. Une fonction `external` en fait certainement partie. Ce sujet est très bien expliqué dans cet article <span><a href="https://solidity.readthedocs.io/en/develop/security-considerations.html#use-the-checks-effects-interactions-pattern">**cliquez ici**</a></span>.
> Remarque :
> Utilisez les `modifiers` pour remplacer les vérifications de conditions en double dans plusieurs fonctions, comme isOwner(), sinon utilisez require ou revert à l'intérieur de la fonction. Cela rend le code de votre contrat intelligent plus lisible et plus facile à vérifier.
### Attention aux arrondis avec la division d'entiers
Toutes les divisions de nombres entiers sont arrondies à l'entier inférieur le plus proche. Si vous avez besoin de plus de précision, envisagez d'utiliser un multiplicateur, ou stockez à la fois le numérateur et le dénominateur.
> Solidity prévoit d'ajouter un type à virgule fixe. <span><a href="https://solidity.readthedocs.io/en/develop/types.html#fixed-point-numbers">**"Plus de détails par ici"**</a></span>
```solidity=
// Mauvais code
uint n = 5 / 4;
// le résultat sera 1 et on perdra le reste à jamais
/* 1ère solution : L'utilisation d'un multiplicateur empêche l'arrondi à l'unité inférieure, ce multiplicateur doit être pris en compte lorsqu'il faudra traiter avec la valeur de n dans le futur */
uint multiplicateur = 100;
uint n = (5 * multiplicateur) / 4;
/* 2ème solution : Stocker le numérateur et le dénominateur, vous pouvez calculer le résultat du numérateur/dénominateur à l'extérieur de la blockchaine au moment d'un appel du front par exemeple */
uint numérateur = 5;
uint dénominateur = 4;
```
### Soyez conscient des compromis entre les contrats abstraits et les interfaces
Les interfaces et les contrats abstraits fournissent une approche personnalisable et réutilisable pour les smart contracts. Les interfaces, qui ont été introduites dans Solidity 0.4.11, sont similaires aux contrats abstraits mais ne peuvent pas avoir de fonctions implémentées. Les interfaces ont également des limitations telles que l'impossibilité d'accéder au stockage ou d'hériter d'autres interfaces, ce qui rend généralement les contrats abstraits plus pratiques. Cependant, les interfaces sont certainement utiles pour concevoir des contrats avant leur mise en œuvre. En outre, il est important de garder à l'esprit que si un contrat hérite d'un contrat abstrait, il doit implémenter toutes les fonctions non implémentées par le biais d'une surcharge, sinon il sera également abstrait *comme on l'avait expliqué précédemment*.
### Les fonctions fallback
#### Gardez les fontions Fallback simples
Les fonctions Fallback sont appelées lorsqu'un contrat reçoit un message sans argument, ou si aucune autre fonction du contrat n'est explicitement appelée, cette fonction est soit `payable` ou `external`. Elles n'ont accès qu'à 2 300 gaz lorsqu'elles sont appelées par un `send()` ou un `transfer()`. Si vous souhaitez être en mesure de recevoir de l'Ether à partir d'un `send()` ou d'un `transfer()`, le mieux que vous puissiez faire dans une fonction Fallback est d'enregistrer un événement. Utilisez une fonction appropriée si un calcul de plus de gaz est nécessaire.
Puisque cette fontion est appelée en premier au moment de l'activation du contrat, solidity prend un peu de temps avant de récupérer tout le gas de l'appelant, entre-temps l'appelant effectuera plusieurs appels et puisque le contrat est active avec un gas *ou balance* encore supéruieur à 0, L'appelant peut récupérer des sommes *ou autres objets du contrats* importantes allant jusqu'à vider le compte du contrat, tout en payant une seule fois le gas.
Le code suivant peut aider à éviter cette mésaventure:
```solidity=
// Mauvais code
function() payable {
balance[msg.sender] += msg.value;
}
// Idée d'un bon code
function payer() payable external {
balance[msg.sender] += msg.value;
}
function() payable {
require(msg.data.length == 0);
emit PaiementRecu(msg.sender);
}
```
#### Vérification de la longueur des données
Étant donné que les fonctions Fallback ne sont pas seulement appelées pour des transferts d'éther simples (sans données) mais aussi lorsqu'aucune autre fonction ne correspond, vous devez vérifier que les données sont vides si la fonction Fallback est destinée à être utilisée uniquement dans le but d'enregistrer l'éther reçu. Sinon, les appelants ne remarqueront pas que votre contrat est utilisé de manière incorrecte et que des fonctions qui n'existent pas sont appelées.
```solidity=
// Mauvais code
function() payable {
emit PaiementRecu(msg.sender);
}
// Idée d'un bon code
function() payable {
require(msg.data.length == 0);
emit PaiementRecu(msg.sender);
}
```
### Verrouiller les pragmas à une version spécifique du compilateur
Les contrats doivent être déployés avec la même version de compilateur et les mêmes flags que ceux avec lesquels ils ont été le plus testés. Le verrouillage du pragma permet de s'assurer que les contrats ne sont pas accidentellement déployés en utilisant, par exemple, le dernier compilateur, qui peut présenter des risques plus élevés de bugs non découverts. Les contrats peuvent également être déployés par d'autres personnes et le pragma indique la version du compilateur prévue par les auteurs originaux.
```solidity=
// Code à éviter
pragma solidity ^0.5.0;
// Code à préviligier
pragma solidity 0.5.0;
```
### Use events to monitor contract activity
Il peut être utile de disposer d'un moyen de surveiller l'activité du contrat après son déploiement. Une façon d'y parvenir est d'examiner toutes les transactions du contrat, mais cela peut être insuffisant, car les appels de messages entre les contrats ne sont pas enregistrés dans la blockchain. De plus, cela ne montre que les paramètres d'entrée, et non les changements réels apportés à l'état. Les événements pourraient également être utilisés pour déclencher des fonctions dans l'interface utilisateur.
### Sachez que les "Built-ins" peuvent être masqués
Il est actuellement possible de masquer les "built-ins" globaux dans Solidity. Cela permet aux contrats de remplacer la fonctionnalité des modules intégrés tels que msg et revert(). Bien que cela soit voulu, cela peut induire en erreur les utilisateurs d'un contrat quant au véritable comportement de ce dernier.
### Dépendre de Timestamp
Trois considérations principales entrent en compte lorsqu'on utilise un timestamp pour exécuter une fonction critique dans un contrat, en particulier lorsque les actions impliquent un transfert de fonds.
#### Manipulation du timestamp
Il faut savoir que le timestamp du bloc peut être manipulé par un mineur. Considérez ce contrat:
```solidity=
uint256 constant private temps = block.timestamp;
function hasard(uint base) constant private returns (uint256 resultat){
uint256 a = temps * 100 / base;
uint256 b = temps * block.number/(temps % 5) ;
uint256 s = block.number/3 + (temps % 300) + y;
uint256 c = uint256(block.blockhash(s));
return uint256((c / a)) % base + 1; //loterie pour avoir un nombre aléatoire basée sur timestamp
}
```
Lorsque le contrat utilise le timestamp pour créer un nombre aléatoire, le mineur peut envoyer un timestamp dans les 15 secondes suivant la validation du bloc, ce qui lui permet effectivement de précalculer une option plus favorable à la loterie. Les timestamp ne sont pas aléatoires et ne devraient pas être utilisés dans ce contexte.
#### La règle des 15 secondes
Le Yellow Paper ne spécifie pas de contrainte sur la dérive temporelle des blocs, mais il précise que chaque horodatage doit être plus grand que l'horodatage de son parent. Les implémentations populaires du protocole Ethereum Geth et Parity rejettent toutes deux les blocs dont l'horodatage est supérieur à 15 secondes dans le futur. Par conséquent, une bonne règle pour évaluer l'utilisation de l'horodatage est la suivante : si l'échelle de votre événement dépendant du temps peut varier de 15 secondes et maintenir l'intégrité, il est sûr d'utiliser un bloc.timestamp.
### SafeMath et débordement
Nous allons ici expliquer ce qui se cache derrière la notion de buffer overflow.
Pour comprendre ce qu'est un stack overflow, il faut comprendre ce qu'un stack et ce que signifie overflower un stack.
Voici un stack (ou un tas):

Si je met dans cet espace mémoire de la donnée, par exemple 24 'A':

La tout se passe bien, mais si je met plus de donnée que prévu initialement par la pile, que se passe t'il ?

Le programme crash, et un hacker peut écrire sur la valeur de retour que le processeur récupèrera à la fin de la fonction. Dans l’état actuel, à la fin du programme, il va tenter d’aller à l’adresse AAAA qui, en hexadécimal, est 0x41414141. Il y a de fortes chances pour qu’il n’ait pas le droit d’accéder à cette case mémoire, ou que cette zone mémoire ne soit pas mappée, et vous obtiendrez un joli SEGFAULT.
Mais cela veut dire que l’on peut écrire la valeur que l’on veut, donc on peut rediriger le fil d’exécution du programme vers un morceau de code que nous aurons préparé. Ce morceau de code peut par exemple ouvrir un shell.
Nous voulons absolument éviter d'écrire plus de donnée que prévu dans la mémoire.
Mathématiquement, cela pose aussi quelques soucis de sécurité:
Imaginons que nous ayons un int de 8 bits, ce qui signifie que notre int ne pourra stocker comme plus grand nombre que 255.
que se passera t'il si on increment notre uint à 256?
Et bien le nombre sera overflowed et prendra la valeur 0, car 255 en binaire c'est 11111111.
Et en binaire, 11111111 + 1 = 00000000 comme quand l'horloge passe de 23:59 à 00:00.
La meme chose se produit quand on soustrait 1 à 0, on arrivera à 255.
Pour faire des opérations mathématiques en évitant tout risque de sécurité, une libraire existe: SAFEMATH
Safemath possède 4 fonctions:
- add pour +
- sub pour -
- mul pour *
- div pour /
Voilà le code derrière ces 4 fonctions:
```solidity=
library SafeMath {
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
assert(c / a == b);
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// assert(b > 0); // Solidity automatically throws when dividing by 0
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
assert(b <= a);
return a - b;
}
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
assert(c >= a);
return c;
}
}
```
Simplement, Safemath va effectuer les opérations mathématiques de bases mais renvoyer une erreur si un overflow ou underflow est effectué.
Safemath fonctionne uniquement avec les 256bits de données, pour moins que ça il faut utiliser SafeMath16 and SafeMath32.
# Tokens Ethereum
Un token Ethereum est basiquement un smart contract qui implement un certain nombre de fonctions. Ils ont hanituellemnt un mappage interne,mapping(address => uint256) balances afin de connaitre le solde de chaque personne.
## ERC20
Les tokens ERC20 ont donc fonctions comme transfer(address to, uint256 value) et balanceOf(address owner) permettent l'echange inter tokens car ils partagent ses fonctions.
Cela veux dire que si une application peux interagir avec les tokens ERC20, elle sera capable d'avoir des transfer et des echanges avec tous les tokens juste en ajoutant la nouvelle adresse de contrat de ce dernier.
Les tokens ERC20 sont très pratique et peuvent servir de monaie et sont divisible. Les échanges de ses dernier se font au de prix du Gas lié à la Blockchain Ethereum et comme pour l'Ethereum, lors d'une saturation de réseau les transfert seront plus lents
```solidity=
contract ERC20{
function name() public view returns (string)
function symbol() public view returns (string)
function decimals() public view returns (uint8)
function totalSupply() public view returns (uint256)
function balanceOf(address _owner) public view returns (uint256 balance)
function transfer(address _to, uint256 _value) public returns (bool success)
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success)
function approve(address _spender, uint256 _value) public returns (bool success)
function allowance(address _owner, address _spender) public view returns (uint256 remaining)
event Transfer(address indexed _from, address indexed _to, uint256 _value)
event Approval(address indexed _owner, address indexed _spender, uint256 _value)
}
```
## ERC721 (NFT)
Aussi appelé jeton non fongible (NFT anglais), servent a définir ou identifier quelque chose d'unique. Grace et a cause de cette dimension uniques, ils ne sont donc pas interchangeable et ne peuvent pas etre divisbles. Il faut les échanger en entier si l'on souhaite le faire.
```solidity=
contract ERC721 {
function balanceOf(address _owner) external view returns (uint256);
function ownerOf(uint256 _tokenId) external view returns (address);
function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) external payable;
function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;
function transferFrom(address _from, address _to, uint256 _tokenId) external payable;
function approve(address _approved, uint256 _tokenId) external payable;
function setApprovalForAll(address _operator, bool _approved) external;
function getApproved(uint256 _tokenId) external view returns (address);
function isApprovedForAll(address _owner, address _operator) external view returns (bool);
event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);
event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId);
event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
}
```
### Heritage multiple
Avec les standars, les differents contrat doivent hérité de ses derniers afin d'accèder à leurs avantages et pour régler a ce besoin il est possible en solidity d'herité de plusieurs contrat. Il suffit de placer une virgule entre chaque parent.
```solidity=
contract fils is mere, pere {
}
```
### balanceOf & ownerOf
Bien, allons creuser l'implémentation d'un ERC721!
Nous allons voir comment afficher le montant présent dans un portefeuille, et à qui appartient un NFT.
`function balanceOf(address _owner) external view returns (uint256 _balance);`
Cette fonction prend une adresse et renvoie le nombre de token que possède l'adresse.
Vous pouvez l'utiliser dans vos smarts contrats pour afficher la balance d'un utilisateur.
Vous avez aussi la fonction ownerOf
`function ownerOf(uint256 _tokenId) external view returns (address _owner);`
Cette fonction pend un numéro d'identification de token et renvoie l'adresse de la personne qui le possède.
### Transfert
Super, maintenant on sait combien de token et nft possède une adresse, mais comment peux t'on transferer un NFT d'une adresse à une autre adresse ?
Deux manière de procéder, mais qui contiennent la même logique. Dans un cas le transfert sera initié par un appel à la fonction "transferFrom" par l'envoyeur du NFT (opensea par exemple), dans l'autre cas, le propriétaire ou le receveur approuvé par le propriétaire du NFT appelle une fonction qui concluera la transaction.
Première façon c'est appeler cette fonction avec l'adresse \_from égal à l'adresse du propriétaie et \_to l'adresse du receveur:
`function transferFrom(address _from, address _to, uint256 _tokenId) external payable;`
Deuxième façon c'est appeler en premier approve avec l'adresse ou le NFT sera envoyé, et le tokenId en tant qu'id du NFT:
`function approve(address _approved, uint256 _tokenId) external payable;`
puis appeler transferFrom avec les adresses autorisé précedemment en arguments:
`function transferFrom(address _from, address _to, uint256 _tokenId) external payable;`
# Web3.js
## Intro
Maintenant qu'on a créé notre DApp, et pour qu'on puisse communiquer avec notre DApp il nous faut une interface utilisateur, nous allons utiliser une bibliothèque Javascript spécialement conçu pour cela nommé **Web3.js**.
## C'est quoi exactement web3.js ?
Pour faire appel à une fonction d'un smart contract, il faut savoir qu'elle se trouve dans un contrat sur l'un des noeuds de la blockchain "une copie globale de la blockchain". A ce stade on fait une requête à l'un de ces noeuds en lui transmettant :
1. L'adresse du smart contract
2. Le nom de la fonction que l'on souhaite appeler ainsi que les paramètres demandés dans la signature de cette fonction.
## JSON-RPC
<span style="color:#00ACFF">*JSON-RPC*</span> est un protocole d'appel de procédure à distance (RPC) réputé d'être léger. La communication entre les noeuds Ethereum et à l'extérieur se fait via ce format.
#### Exemples :
un exemple de requête ressemble à ça :
```json=
{
"jsonrpc":"2.0",
"method":"buySoftwareLicense",
"params":[
{
"from":"0xb60e8dd61c5d32be8azert8eb970870f07233155",
"to":"0xd46e8dd67c5d32be8058bb8qwaze870f07244567",
"gas":"0x7880",
"gasPrice":"0x918ae72a000",
"value":2,
"id_software":"0x14qe8dd61c5d32b987aze789b970870f07233258"
}],
"id":5
}
```
La réponse qu'on obtiendra de la part du noeud après recherche du contract puis execution de la fonction, et si tout va bien ressemblera à ceci :
```json=
{
"jsonrpc":"2.0",
"id":"5",
"result":"0xfa0e8dd61c5d32be805abb8eb97aze0f07233155"
}
```
Le rôle du web3.js est de traduire nos requêtes et les résultats en ce langage et vice-versa.
Et donc au lieu d'envoyer du JSON-RPC brut on pourra coder :
```
ContractExample.methods.buySoftwareLicense(154752)
.send({ from: "0xb60e8dd61c5d32be8azert8eb970870f07233155", gas: "3000000" })
```
## Environnement
quelque soit l'environnement de travail que vous avez, on installer <span style="color:#00ACFF">*web3.js*</span> avec l'un des gestionnaires de paquets connus via les commandes :
```shell=
// Choisir une des commandes selon vos préférences :
// NPM
npm install web3
// Yarn
yarn add web3
// bower
Bower install web3
```
Ou tout simplement pour éviter les configurations différentes entre chaque environnement de travail, nous allons télécharger le package web3.js à partir de ce [lien](https://github.com/ChainSafe/web3.js/blob/1.x/dist/web3.min.js) puis inclure dans la balise `script` dans votre projet "dans la page html correspondante ou par les autres moyens de création de files séparés" :
```htmlembedded=
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Example front-end</title>
<script language="javascript" type="text/javascript" src="web3.min.js"></script>
</head>
<body>
</body>
</html>
```
Avec ceci on est sûr d'avoir un projet qui fonctionnera pour tout le monde, mais n'hésitez pas à installer web3 dans votre environnement si vous vous sentez à l'aise.
## Fournisseurs Web3
Pour faire un appel API dans une configuration classique, on a besoin de configurer l'URL dans le serveur web, ainsi on connait le serveur web distant qui traîtera nos requêtes. Dans la même optique notre interface a besoin d'un *provider*-*fournisseur*- qu'on doit configurer. On va prendre -attention spoiler- **Web3** qui indiquera à notre code vers quel noeud Ethereum il devra envoyer nos requêtes et nos appels d'écriture.
On peut naturellement hébérger notre propre noeud Ethereum et le définir comme fournisseur mais cela consommera beaucoup de ressource et de temps. Heureusement il existe des fournisseurs tiers qui offre une infrastructure qui nous facilitera cette configuration de Dapp, le plus connu et le plus fiable parmi tous est <span style="color:#00ACFF">**[Infura](https://infura.io)**</span>.
## Infura
Un développeur Solidity-Etheureum fait principalement face à ces difficultés :
- Le stockage des données sur Ethereum est coûteux.
- Il est compliqué de se connecter à la blockchain Ethereum.
- La synchronisation de la blockchain est lente.
- La blockchain Ethereum occupe beaucoup d'espace.
L'idée d'<span style="color:#00ACFF">**[Infura](https://infura.io)**</span> est de répondre à ces préoccupations et de s'occuper de cette partie en étant :
- Rapide - L'accès à la blockchain Ethereum devient beaucoup plus rapide.
- Évolutif - En gérant les nœuds, nous les développeurs nous n'avons pas à nous soucier des limites de l'infrastructure.
- Stockage des données - Au lieu de tout stocker sur la chaîne, les données peuvent être stockées séparément, avec juste un hachage stocké sur la blockchain.
Pour configurer Web3 dans notre code il suffit de déclarer une `var web3` ou tout autre nom de variable pour pouvoir l'utiliser plus tard :
```javascript=
var web3 = new Web3(new Web3.providers.WebsocketProvider("wss://mainnet.infura.io/ws"));
```
Puisqu'un utilisateur de notre DApp va devoir exécuter des fonctions de lecture mais aussi d'écriture sur la blockchain, il nous faudra un mécanisme qui permettra à cet utilisateur d'utiliser sa clé privée afin de signer la transaction.
>Le mécanisme de le signature sur une blockchain se repose sur le couple clé public/ clé privée. La clé publique nous identifie et la clé privée que sommes les seules à posséder prouve cette identité.
De manière générale la gestion des clés privées est un domaine ultra compliqué en sécurite et demande des connaissances importantes en cryptographie, c'est pour ça que la plupart des développeurs utilisent des services dédiés assez connus pour leur fiabilité et le plus réputé parmi eux est <span style="color:#00ACFF">**[MetaMask](https://metamask.io)**</span>.
## Metamask
<span style="color:#00ACFF">**[MetaMask](https://metamask.io)**</span> est une extension de browser qui fonctionne seulement sur Chrome et firefox. Il existe aussi une webApp pour les smartphones. Cette extension permet à l'utilisateur la gestion de ces clés privées ainsi que différents comptes Ethereum en même temps. Dans la section vidéo vous allez trouver un tuto qui explique comment installer MetaMask et comment intéragir avec les sites web qui tournent avec web3j.
Finalement pour que les utilisateurs puissent utiliser MetaMask sur notre DApp que ce soit une page web ou une webapp, on devra faire en sorte qu'elle soit compatible avec.
### L'utilisation de MetaMask avec Web3
L'extension MetaMask expose l'API web3 par un objet `web3` injecté auquel vous pouvez accéder via JavaScript et ne supporte pas la plupart des méthodes synchrones de l'API web3.
Dans la documentation de MetaMask on est invité à utiliser le code qui vérifit si MetaMask est installé et qui nous invite à le faire si ce n'est pas le cas:
```solidity=
window.addEventListener('load', function() {
// Checking if Web3 has been injected by the browser (Mist/MetaMask)
if (typeof web3 !== 'undefined') {
// Use Mist/MetaMask's provider
web3js = new Web3(web3.currentProvider);
} else {
// Handle the case where the user doesn't have web3. Probably
// show them a message telling them to install MetaMask in
// order to use our app.
}
// Now you can start your app & access web3js freely:
startApp()
})
```
#### Gestion d'erreurs
On vient de voir que MetaMask peut ne pas être installé chez l'utilisateur mais ceci n'est pas la seule erreur qu'on devrait gérer. D'ailleurs on peut créer plusieurs fonctions qui vérifient chacune de son coté la validité d'un élément avant de poursuivre vers `startApp()`.
Pour éviter les erreurs récurrentes auxquelles on peut faire face, on doit vérifier si :
- MetaMask est installé "ce point a été précédemment vu"
- MetaMask est installé mais verouillé
- MetaMask est installé, déverouillé mais le compte ne possède pas de solde suffisant pour effectuer une transaction.
- Une transaction MetaMask a été effeectuée mais rejetée.
Pour gérer ces cas d'erreus on peut implémenter des méthodes pour chaque cas:
###### Cas 1 et 2 : Vérifier que Metamask est installé et déverouillé
```solidity=
function est_installe() {
if (typeof web3 !== 'undefined'){
console.log("Tout va bien, Metamask est installé")
}
else{
console.log("On dirait que MetaMask n\'est pas installé")
}
}
function est_verrouille() {
var w3 = web3.eth;
w3.getAccounts(function(e, comptes){
if (e != null) {
console.log(e)
}else if (comptes.length === 0) {
console.log("On dirait qu'il MetaMask est verrouillé")
}else {
console.log("Tout va bien, MataMask est déverrouillé")
}
});
}
```
###### Cas 3 : Vérifier que le solde est suffisant pour effectuer une transaction:
```solidity=
function verifier_balance() {
instance.balanceOf(
web3.eth.accounts[0],
function (e, resultat) {
if (!e && resultat) {
var b = resultat.c[0];
if (b < montantDemande * (100000000)) {
console.log("Le montant demandé est supérieur à la valeur trouvée dans MetaMask")
return false;
}
console.log("Tout va bien, il y a assez d'argent dans la balance")
// Implémenter ici le code de transaction
} else {
console.error(e);
}
return false;
}
);
}
```
###### Cas 4 : Gérer le rejet d'une transaction après qu'elle soit effectuée

```solidity=
function demande_approbation() {
instance.approve(
adresse,
truePlanCost,
{ gasPrice: web3.toWei('100', 'gwei') },
function (e, resultat) {
if (!e && resultat) {
// Code de la transaction ici
} else {
console.error(e);
console.log('Transcation rejetée');// Ou code de gestion de rejet
}
}
);
}
```
###### Tous les cas en un seul morceau :
```solidity=
var approuver_transaction = true;
function validate() {
if (typeof web3 !== 'undefined'){
if (typeof web3 !== 'undefined'){
console.log("Tout va bien, Metamask est installé")
}
web3.eth.getAccounts(function(err, accounts){
if (e != null) {
console.log(e)
}else if (comptes.length === 0) {
console.log("On dirait qu'il MetaMask est verrouillé")
}else {
console.log("Tout va bien, MataMask est déverrouillé")
instance.balanceOf(
web3.eth.accounts[0],
function (e, resultat) {
if (!e && resultat) {
var b = resultat.c[0];
if (b < montantDemande * (100000000)) {
console.log("Le montant demandé est supérieur à la valeur trouvée dans MetaMask")
return false;
}
console.log("Tout va bien, il y a assez d'argent dans la balance")
if (approuver_transaction == true ){
demande_approbation();
approuver_transaction = false;
}
}
else {
console.error(e);
}
return false;
});
}
});
}
else{
console.log("On dirait que MetaMask n\'est pas installé")
}
}
// request approval from MetaMask user
function demande_approbation() {
instance.approve(
adresse,
truePlanCost,
{ gasPrice: web3.toWei('100', 'gwei') },
function (e, resultat) {
if (!e && resultat) {
// Code de la transaction ici
} else {
console.error(e);
console.log('Transcation rejetée');// Ou code de gestion de rejet
}
}
);
}
```
#### Race competition
Avant d'aller plus loin on parlera de la situation de compétition ou accès concurrent *Race competition* qui apparait quand une application dépend de l'ordre des processus, de leur synchronisation ou des threads. N'importe quel changement ou anomalie risuqe d'altérer le bon fonctionnement de l'application.
Pour éviter cette situation, on a le choix entre la définition d'un intervalle *laps de temps en boucle* qui execute la fonction de l'interrogation de l'interface **Polling** web3j en permanence ou d'utiliser un Listener sur un event "dans ce cas ce sera l'event update"
Le fournisseur de MetaMask recommande d'utiliser la méthode de l'intervalle de temps -polling- pour éviter de rafraîchir la page à chaque fois qu'on voudrait voir les nouveaux changements coté UI.
Avec la version 1.0.0 de web3, le fournisseur MetaMask expose un event listener "update".
```solidity=
web3.currentProvider.publicConfigStore.on('update', callback) ;
```
La callback reçoit un objet qui contient {selectedAddress, networkVersion}. Et on pourra modifier l'interface en fonction de ces attributs `change` ou `update`.
Choisir l'écoute d'évenement est une meilleure option que le polling car vous on améliore les performances, `.on('update')` est déclenché sans avoir besoin de boucler.
> Comme de plus en plus de personnes se familiarisent avec cette technologie, le potentiel créatif des DApps ne cesse de grandir.
>
> MetaMask est un outil qui rend le développement de cette technologie plus accessible, et il est constamment mis à jour avec de nouvelles fonctionnalités.
>
> MetaMask est un pont qui permet de visiter le web distribué de demain dans le navigateur d'aujourd'hui. Il nous permet d'exécuter des dApps Ethereum directement dans notre navigateur sans avoir à exécuter un nœud Ethereum complet.
>
> MetaMask développe de nouveaux moyens de prévenir les attaques éventuelles. Voici [un article qui parle des derniers outils de sécurité de Metamask](https://medium.com/metamask/how-metamasks-latest-security-tool-could-protect-smart-contract-developers-from-theft-e12da346aa53).
### La configuration de la communication
Pour pouvoir communiquer avec notre contrat, Web3.js a besoin de 2 objets : Son <span style="color:#00ACFF">**adresse**</span> et son <span style="color:#00ACFF">**ABI**</span>.
#### Adresse d'un contrat
Après la compilation du smart contrat on procère à son déploiement sur la blockchaine, cette action lui associe une adresse pérenne et identique sur la blockchaine d'Ethereum. Cette adresse est un sha-256 qu'on devrait utiliser pour pouvoir communiquer avec notre smart contract.
#### ABI
Pour Application Binary Interface se résume généralement à l'interface que l'on trouve entre deux modules de programme, dont l'un est souvent au niveau du code machine. L'interface est la méthode de facto pour coder/décoder les données dans/hors du code machine. Elle détermine des détails tels que la manière dont les fonctions sont appelées et le format binaire dans lequel les informations doivent être transmises d'un composant du programme à un autre...
Un contrat intelligent Ethereum est un bytecode déployé sur la blockchain Ethereum. Il peut y avoir plusieurs fonctions dans un contrat. Une ABI est nécessaire pour que vous puissiez spécifier quelle fonction du contrat invoquer, et obtenir la garantie que la fonction renverra des données dans le format attendu, c'est essentiellement la façon dont vous pouvez coder les appels de contrat Solidity pour l'EVM et, à l'inverse, la façon de lire les données des transactions.
Voivi un exemple tiré des specifications ABI d'Ethereum:
```solidity=
contract Foo {
function bar(real[2] xy) {}
function baz(uint32 x, bool y) returns (bool r) { r = x > 32 || y; }
function sam(bytes name, bool z, uint[] data) {}
}
```
Si nous voulions appeler baz avec les paramètres 69 et true, nous passerions 68 octets au total, qui peuvent être décomposés en :
> 0xcdcd77c0 : l'ID de la méthode. Il s'agit des 4 premiers octets du hachage Keccak-256 de la forme ASCII de la signature baz(uint32,bool).
> 0x00000000000000000000000000000000000000000045 : le premier paramètre, une valeur uint32 69 ajusté à 32 bytes
> 0x00000000000000000000000000000000000000000001 : le deuxième paramètre - booléen vrai, ajusté à 32 bytes
Et c'est là qu'on voit l'utilité du typage dit canonical, où on est obligé de l'utiliser pour éviter un piège courant lors de la dérivation de l'ID de la méthode, par exemple uint256 au lieu de uint.
Voici un exemple dans Solidity de calcul d'un ID de méthode pour sam ci-dessus :
`bytes4(keccak256("sam(bytes,bool,uint256[])")`
L'utilisation d'une bibliothèque de plus haut niveau telle que web3.js permet d'abstraire la plupart de ces détails, mais l'ABI au format JSON doit toujours être fournie à web3.js.
Finalement on peut résumer en difinissant l'ABI comme une représentation des fonctions d'un contrat au format JSON qui indique à Web3.js comment formater les appels aux fonctions pour les rendre compréhensible coté contrat.
c'est une représentation des fonctions de votre contrat au format JSON qui indique à Web3.js comment formater les appels aux fonctions pour que votre contrat les comprenne.
Pour instatier un instancier un contrat Web3.js, il est important d'avoir l'ABI `theABI` et l'adresse `theContractAddress` :
```solidity=
var myContract = new web3js.eth.Contract(theABI, theContractAddress);
```
# création de projet
## Installation de npm
### Windows 10
Télécharger https://nodejs.org/en/download/
Lancer le programme et suivre l'installation...
### Linux
Avec votre gestionnaire de paquet favoris
`sudo apt install npm nodejs`
### Verifier que l'installation fonctionne bien
`npm -v`

## Installation de truffle
2 - npm install -g truffle

3 - Créer un dossier ou sera installé truffle
`mkdir Truffle`
4 - installer truffle dans ce repertoire
`cd Truffle`
`truffle init`
Vous devriez voir ça


4 - Initialisation du projet
Dans votre projet à présent vous avez un dossier selon l'arborescence suivante
MonProjet/Truffle
Nous allons maintenant mettre en place les differents elements de notre application
Dans MonProjet, nous allons initialiser un projet nodejs

5 - Installation de Ganache
Allez sur https://www.trufflesuite.com/ganache
et téléchargez l'application pour votre OS.
Lancez Ganache, vous allez avoir un écran de ce type:

Cliquez sur "New Workspace", vous arriverez sur cette écran

Cliquez sur Add Project et ajouter le fichier truffle-config.js qui se trouve dans le dossier Truffle
de votre projet.

Cliquez ensuite sur save workspace en haut à droite.
Felicitation, vous avez une blockchain ethereum sur votre pc ! :palms_up_together:
# déploiement
1- truffle compile dans le terminal
2- créé un fichier dans le dossier migrations "2_deploy_contracts.js"
3- dans le fichier que l'on vient de créer :
```
const GestionProduit = artifacts.require("GestionProduit");
module.exports = function(deployer) {
deployer.deploy(GestionProduit);
};
```
4- truffle migrate dans le terminal, il faut s'assurer que ganache est sur le bon port.
# Les tests
Écrivons maintenant un test de base pour nous assurer qu'un contrat intelligent fonctionne correctement. Tout d'abord, laissez-moi vous expliquer pourquoi les tests sont si importants lorsque vous développez des contrats intelligents. Nous voulons nous assurer que les contrats sont exempts de bugs pour plusieurs raisons :
- Tout le code sur la blockchain Ethereum est immuable ; il ne peut pas être changé. Si le contrat contient des bugs, nous devons le désactiver et déployer une nouvelle copie. Cette nouvelle copie n'aura pas le même état que l'ancien contrat, et elle aura une adresse différente.
- Le déploiement des contrats coûte du gas car il crée une transaction et écrit des données sur la blockchain. Cela coûte de l'Ether, et nous voulons minimiser la quantité d'Ether que nous devons payer.
- Si l'une des fonctions de notre contrat qui écrit dans la blockchain contient des bugs, le compte qui appelle cette fonction pourrait potentiellement perdre de l'Ether, et le comportement peut être différent de l'attendu.
Dans l'arborescence du projet solidity, puis dans le dossier `test`, créons un fichier de test comme celui-ci :
```solidity=
test/NotreNomDeContrat.test.js
```
Nous allons écrire tous nos tests en Javascript à l'intérieur de ce fichier avec le framework de test Mocha et la bibliothèque d'assertions Chai. Ces éléments sont fournis avec le framework Truffle. Nous allons écrire tous ces tests en Javascript pour simuler une interaction côté client avec notre contrat intelligent, comme nous l'avons fait dans la console. Voici tout le code pour les tests :
```solidity=
const MyContract = artifacts.require('./MyContract.sol')
contract('MyContract', (accounts) => {
before(async () => {
this.myContract = await MyContract.deployed()
})
it('deploys successfully', async () => {
const address = await this.myContract.address
assert.notEqual(address, 0x0)
assert.notEqual(address, '')
assert.notEqual(address, null)
assert.notEqual(address, undefined)
})
it('lists methods', async () => {
const idInMap = await this.myContract.firstMethod()
const element = await this.myContract.mapInCode(idInMap)
assert.equal(element.completed, false)
assert.equal(idInMap.toNumber(), 1)
})
})
```
Laissez-moi expliquer ce code. Tout d'abord, nous exigeons le contrat et l'assignons à une variable, comme nous l'avons fait dans le fichier de migration. Ensuite, nous appelons la fonction "contract", et écrivons tous nos tests dans la fonction de rappel. Cette fonction de rappel fournit une variable "accounts" qui représente tous les comptes de notre blockchain, fournis par Ganache.
Le premier test vérifie que le contrat a été correctement déployé sur la blockchain en inspectant son adresse.
Le test suivant vérifie que le contrat intelligent liste correctement les tâches en contrôlant la tâche par défaut que nous avons créée dans la fonction d'initialisation.
Maintenant, exécutons les tests à partir de la ligne de commande comme ceci :
```javascript=
truffle test
```
# Sources "A garder toujours en bas de la page"
https://decrypt.co/resources/what-is-infura
https://www.crowdbotics.com/blog/a-complete-guide-to-building-ethereum-dapps-with-metamask
https://github.com/OpenZeppelin/openzeppelin-contracts
https://ethereum.github.io/yellowpaper/paper.pdf
https://docs.soliditylang.org/en/develop/index.html
https://consensys.github.io/smart-contract-best-practices/known_attacks/
https://docs.soliditylang.org/en/develop/types.html#fixed-point-numbers