Comment créer un jeton ERC20 de manière simple

Publié: 2022-03-11

Le but de cet article est de montrer comment créer un jeton ERC20 en un minimum de temps.

Commençons par les bases : qu'est-ce qu'un jeton ERC20 ?

Ces dernières années, la spécification de jeton ERC20 est devenue la norme de facto pour les jetons Ethereum. En d'autres termes, la plupart des contrats Ethereum disponibles aujourd'hui sont conformes à ERC20. Cet article détaillera comment vous pouvez créer votre propre jeton Ethereum, mais avant de commencer, examinons de plus près la norme ERC20.

Illustration du jeton ERC20

Qu'est-ce qui rend les jetons ERC20 si attrayants et réussis ? Plusieurs facteurs entrent en jeu :

  1. Les jetons ERC20 sont simples et faciles à déployer, comme vous le verrez dans ce tutoriel.
  2. La norme ERC20 résout un problème important, car les marchés et les crypto-portefeuilles basés sur la blockchain ont besoin d'un ensemble unique et standardisé de commandes pour communiquer avec la gamme de jetons qu'ils gèrent. Cela inclut les règles d'interaction entre différents jetons, ainsi que les règles d'achat de jetons.
  3. Il s'agissait de la première spécification populaire à proposer la standardisation des jetons Ethereum. Ce n'était en aucun cas le premier , mais grâce à sa popularité, il est rapidement devenu la norme de l'industrie.

Tout comme les autres jetons Ethereum, les jetons ERC20 sont mis en œuvre sous forme de contrats intelligents et exécutés sur la machine virtuelle Ethereum (EVM) de manière décentralisée.

Solidity : le langage de programmation des contrats intelligents

Les contrats intelligents Ethereum sont écrits en Solidity. Bien qu'il existe des langues alternatives, presque personne ne les utilise à cette fin. Solidity est similaire à JavaScript, donc si vous avez une certaine connaissance de JavaScript, ou même de Java et d'autres langages de type C, vous ne devriez avoir aucun mal à comprendre qu'un morceau de code dans Solidity le fait, avant même que vous ne maîtrisiez suffisamment Solidity pour l'utiliser. ce.

C'est là que le plaisir commence, car vous devriez pouvoir commencer à créer un simple contrat ERC20 en un rien de temps. Il s'agit d'une tâche simple, suffisamment simple pour que cet article montre comment vous pouvez écrire et déployer un jeton ERC20 en moins d'une heure.

Le jeton que nous allons créer dans cette démonstration sera une implémentation ERC20 simple, sans trop de cloches et de sifflets. Cependant, j'ai vu de nombreux jetons tout aussi simples dans le monde réel, et ils ont tendance à très bien fonctionner.

Présentation de la norme de jeton ERC20

Qu'est-ce que l'ERC20 ?

En termes simples, la norme ERC20 définit un ensemble de fonctions à mettre en œuvre par tous les jetons ERC20 afin de permettre l'intégration avec d'autres contrats, portefeuilles ou places de marché. Cet ensemble de fonctions est plutôt court et basique.

 function totalSupply() public view returns (uint256); function balanceOf(address tokenOwner) public view returns (uint); function allowance(address tokenOwner, address spender) public view returns (uint); function transfer(address to, uint tokens) public returns (bool); function approve(address spender, uint tokens) public returns (bool); function transferFrom(address from, address to, uint tokens) public returns (bool);

Les fonctions ERC20 permettent à un utilisateur externe, par exemple une application de portefeuille cryptographique, de connaître le solde d'un utilisateur et de transférer des fonds d'un utilisateur à un autre avec l'autorisation appropriée.

Le contrat intelligent définit deux événements spécifiquement définis :

 event Approval(address indexed tokenOwner, address indexed spender, uint tokens); event Transfer(address indexed from, address indexed to, uint tokens);

Ces événements seront appelés ou émis lorsqu'un utilisateur se verra accorder le droit de retirer des jetons d'un compte, et après le transfert effectif des jetons.

En plus des fonctions ERC20 standard, de nombreux jetons ERC20 comportent également des champs supplémentaires et certains sont devenus de facto une partie de la norme ERC20, sinon par écrit, du moins dans la pratique. Voici quelques exemples de tels champs.

 string public constant name; string public constant symbol; uint8 public constant decimals;

Voici quelques points concernant la nomenclature ERC20 et Solidity :

  • Une fonction public est accessible en dehors du contrat lui-même
  • view signifie fondamentalement constant, c'est-à-dire que l'état interne du contrat ne sera pas modifié par la fonction
  • Un event est le moyen utilisé par Solidity pour permettre aux clients, par exemple l'interface de votre application, d'être informés d'événements spécifiques dans le cadre du contrat.

La plupart des constructions du langage Solidity doivent être claires si vous possédez déjà des compétences Java/JavaScript essentielles.

Ecrire un Token ERC20 en Solidity

Les jetons ERC20 dans la solidité

Maintenant que nous avons décrit les bases et expliqué ce qu'il faut pour créer un jeton ERC20, il est temps de commencer à écrire une logique.

Tout d'abord, nous devons définir deux objets de mappage. C'est la notion de solidité pour un tableau associatif ou clé/valeur :

 mapping(address => uint256) balances; mapping(address => mapping (address => uint256)) allowed;

L'expression mapping(address => uint256) définit un tableau associatif dont les clés sont de type address - un nombre utilisé pour désigner les adresses de compte, et dont les valeurs sont de type uint256 - un entier de 256 bits généralement utilisé pour stocker les soldes de jetons.

Le premier objet de mappage, balances , contiendra le solde du jeton de chaque compte propriétaire.

Le deuxième objet de mappage, allowed , inclura tous les comptes approuvés pour le retrait d'un compte donné ainsi que la somme de retrait autorisée pour chacun.

Comme vous pouvez le voir, le champ de valeur du mappage autorisé est en soi un mappage de l'adresse du compte à sa somme de retrait approuvée.

Ces mappages ainsi que tous les autres champs de contrat seront stockés dans la blockchain et seront exploités , ce qui entraînera la propagation des modifications à tous les nœuds d'utilisateurs du réseau.

Le stockage Blockchain est coûteux et les utilisateurs de votre contrat devront payer, d'une manière ou d'une autre. Par conséquent, vous devez toujours essayer de minimiser la taille de stockage et les écritures dans la blockchain.

Maintenant que nous avons les structures de données requises en place, nous pouvons commencer à écrire la logique ERC20 dans les fonctions appropriées.

Définition du nombre de jetons ICO

Comment définissons-nous le nombre de jetons ICO ? Eh bien, il existe plusieurs façons de définir le nombre maximal de jetons ICO et cette question mériterait à elle seule une longue discussion.

Pour les besoins de notre tutoriel ECR20, nous utiliserons l'approche la plus simple : définissez le nombre total de jetons au moment de la création du contrat et attribuez-les tous initialement au « propriétaire du contrat », c'est-à-dire le compte qui a déployé le contrat intelligent :

 uint256 totalSupply_; constructor(uint256 total) public { totalSupply_ = total; balances[msg.sender] = _totalSupply; }

Un constructeur est une fonction spéciale appelée automatiquement par Ethereum juste après le déploiement du contrat. Il est généralement utilisé pour initialiser l'état du jeton à l'aide de paramètres transmis par le compte de déploiement du contrat.

msg est une variable globale déclarée et remplie par Ethereum lui-même. Il contient des données importantes pour l'exécution du contrat. Le champ que nous utilisons ici : msg.sender contient le compte Ethereum exécutant la fonction de contrat en cours.

Seul le compte de déploiement peut saisir le constructeur d'un contrat. Lors du démarrage du contrat, cette fonction alloue les jetons disponibles au compte 'propriétaire du contrat'.

Obtenir l'approvisionnement total en jetons

 function totalSupply() public view returns (uint256) { return totalSupply_; }

Cette fonction renverra le nombre de tous les jetons alloués par ce contrat, quel que soit le propriétaire.

Obtenir le solde du jeton du propriétaire

 function balanceOf(address tokenOwner) public view returns (uint) { return balances[tokenOwner]; }

balanceOf renverra le solde actuel du jeton d'un compte, identifié par l'adresse de son propriétaire.

Transférer des jetons vers un autre compte

 function transfer(address receiver, uint numTokens) public returns (bool) { require(numTokens <= balances[msg.sender]); balances[msg.sender] = balances[msg.sender] — numTokens; balances[receiver] = balances[receiver] + numTokens; emit Transfer(msg.sender, receiver, numTokens); return true; }

Comme son nom l'indique, la fonction de transfer est utilisée pour déplacer numTokens quantité de jetons du solde du propriétaire vers celui d'un autre utilisateur, ou receiver . Le propriétaire transférant est msg.sender c'est-à-dire celui qui exécute la fonction, ce qui implique que seul le propriétaire des jetons peut les transférer à d'autres.

La façon dont Solidity affirme un prédicat est require . Dans ce cas, que le compte transférant dispose d'un solde suffisant pour exécuter le transfert. Si une instruction require échoue, la transaction est immédiatement annulée sans aucune modification écrite dans la blockchain.

Juste avant de quitter, la fonction déclenche l'événement ERC20 Transfer permettant aux auditeurs enregistrés de réagir à son achèvement.

Approuver le délégué pour retirer des jetons

Cette fonction est le plus souvent utilisée dans un scénario de marché de jetons.

 function approve(address delegate, uint numTokens) public returns (bool) { allowed[msg.sender][delegate] = numTokens; emit Approval(msg.sender, delegate, numTokens); return true; }

Ce que fait approve est de permettre à un propriétaire, c'est-à-dire msg.sender d'approuver un compte délégué - éventuellement le marché lui-même - pour retirer des jetons de son compte et les transférer vers d'autres comptes.

Comme vous pouvez le voir, cette fonction est utilisée pour les scénarios où les propriétaires proposent des jetons sur un marché. Il permet à la place de marché de finaliser la transaction sans attendre l'approbation préalable.

A la fin de son exécution, cette fonction déclenche un événement Approval .

Obtenir le nombre de jetons approuvés pour le retrait

 function allowance(address owner, address delegate) public view returns (uint) { return allowed[owner][delegate]; }

Cette fonction renvoie le nombre actuel de jetons approuvés par un propriétaire à un délégué spécifique, comme défini dans la fonction d' approve .

Transférer des jetons par délégué

La fonction transferFrom est l'homologue de la fonction d' approve , dont nous avons parlé précédemment. Il permet à un délégué approuvé pour le retrait de transférer les fonds du propriétaire sur un compte tiers.

 function transferFrom(address owner, address buyer, uint numTokens) public returns (bool) { require(numTokens <= balances[owner]); require(numTokens <= allowed[owner][msg.sender]); balances[owner] = balances[owner] — numTokens; allowed[owner][msg.sender] = allowed[from][msg.sender] — numTokens; balances[buyer] = balances[buyer] + numTokens; Transfer(owner, buyer, numTokens); return true; }

Les deux instructions require au démarrage de la fonction visent à vérifier que la transaction est légitime, c'est-à-dire que le propriétaire a suffisamment de jetons à transférer et que le délégué a l'approbation pour (au moins) numTokens à retirer.

En plus de transférer le montant de numTokens du propriétaire à l'acheteur, cette fonction soustrait également numTokens de l'allocation du délégué. Cela permet essentiellement à un délégué disposant d'une allocation donnée de la diviser en plusieurs retraits distincts, ce qui est un comportement typique du marché.

Nous pourrions nous arrêter ici et avoir une implémentation ERC20 valide. Cependant, nous voulons aller plus loin, car nous voulons un jeton de force industrielle. Cela nous oblige à rendre notre code un peu plus sécurisé, même si nous pourrons toujours garder le jeton relativement simple, voire basique.

Bibliothèque de solidité SafeMath

SafeMath est une bibliothèque Solidity destinée à faire face à une façon dont les pirates sont connus pour rompre les contrats : l'attaque par débordement d'entier. Dans une telle attaque, le pirate force le contrat à utiliser des valeurs numériques incorrectes en transmettant des paramètres qui feront passer les entiers pertinents au- delà de leurs valeurs maximales.

Bibliothèque Safemath dans Solidity : illustration

SafeMath protège contre cela en testant le débordement avant d'effectuer l'action arithmétique, éliminant ainsi le danger d'attaque par débordement. La bibliothèque est si petite que l'impact sur la taille du contrat est minime, n'entraînant aucune performance et peu de pénalités sur les coûts de stockage.

Ajoutons SafeMath à notre code :

 library SafeMath { // Only relevant functions 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; } }

SafeMath utilise des instructions assert pour vérifier l'exactitude des paramètres passés. Si l' assert échoue, l'exécution de la fonction sera immédiatement arrêtée et toutes les modifications de la blockchain seront annulées.

Ensuite, ajoutons la déclaration suivante présentant la bibliothèque au compilateur Solidity :

using SafeMath for uint256;

Ensuite, nous remplaçons l'arithmétique naïve que nous utilisions au début par des fonctions SafeMath :

 balances[msg.sender] = balances[msg.sender].sub(numTokens); balances[receiver] = balances[receiver].add(numTokens); balances[buyer] = balances[buyer].add(numTokens); balances[owner] = balances[owner].sub(numTokens);

Tout emballer ensemble

Dans Solidity, les fonctions et les événements d'un contrat intelligent sont regroupés dans une entité appelée contrat que vous pouvez traduire silencieusement en une "classe blockchain". Vous trouverez ci-dessous le contrat compatible ERC20 que nous avons créé, y compris l'essentiel de notre code. Les champs de nom et de symbole peuvent être modifiés à volonté. La plupart des jetons conservent la valeur décimale à 18, nous ferons donc de même.

Déploiement du contrat Ethereum

Le temps est venu de déployer notre contrat sur la blockchain. Après le déploiement, notre contrat sera transféré à tous les nœuds participant au réseau. Toutes les modifications apportées au contrat seront propagées à tous les nœuds participants.

Les développeurs Ethereum utilisent généralement des outils de déploiement tels que Truffle. Même Truffle est exagéré pour les besoins limités de cet article, et un simple outil en ligne appelé Remix suffira.

Pour l'utiliser, vous devrez installer le plugin MetaMask sur votre navigateur et un compte Rinkeby (réseau de test Ethereum) avec au moins un Rinkeby Ether dedans. Ce sont des étapes relativement simples, nous n'entrerons donc pas dans les détails.

Si vous n'en avez pas non plus, rendez-vous sur MetaMask et Rinkeby pour les liens de téléchargement et pour obtenir des instructions d'installation et d'utilisation claires.

Maintenant que nous avons tous les éléments de base en place, nous allons nous diriger vers Remix et coller le code ci-dessus, y compris la ligne pragma et la bibliothèque SafeMath, dans l'éditeur en ligne.

Ensuite, nous allons passer au deuxième onglet à droite appelé " Exécuter " et cliquer sur " Déployer ". Une fenêtre contextuelle MetaMask apparaîtra nous demandant de confirmer la transaction. Bien sûr, nous l'approuverons.

texte alternatif de l'image

  • Boîte verte : Assurez-vous d'être sur Rinkeby
  • Boîte bleue : Définissez votre approvisionnement total en jetons
  • Boîte rouge : Déployez-vous !

Essentiel : https://gist.github.com/giladHaimov/8e81dbde10c9aeff69a1d683ed6870be#file-basicerc20-sol

Félicitations! Vous venez de déployer votre premier token ERC20, tel un vrai professionnel d'Ethereum. Comme promis, le jeton est simple et léger, mais entièrement fonctionnel, conforme à la norme ERC20 et sécurisé avec MathSafe. Il est prêt à être acheté, payé et transféré à travers la Blockchain.

Est-ce tout ce qu'il y a dans les contrats intelligents ?

Non, même pas proche, car notre brève démonstration effleure à peine la surface et traite uniquement d'un aspect du développement de contrats intelligents.

Les contrats intelligents peuvent être beaucoup plus complexes en fonction de votre logique métier, de votre modélisation de l'interaction utilisateur, que vous autorisiez ou non la frappe et la gravure de jetons, les modifications du cycle de vie que vous introduisez dans le contrat, le besoin de capacités de niveau administrateur qui s'accompagnent généralement d'un ensemble de fonctions autorisées par l'administrateur, etc. Vous obtenez l'image.

Néanmoins, si vous pouvez reproduire ce que nous avons fait ici, c'est une base solide pour élargir vos connaissances et passer à des contrats plus complexes si nécessaire.