Transferts et approbation de jetons ERC-20 à partir d'un contrat intelligent Solidity
Dans le tutoriel précédent, nous avons étudié l'anatomie d'un jeton ERC-20 dans Solidity sur la blockchain Ethereum. Dans cet article, nous verrons comment nous pouvons utiliser un contrat intelligent pour interagir avec un jeton en utilisant le langage Solidity.
Pour ce contrat intelligent, nous allons créer un véritable échange décentralisé factice où un utilisateur pourra échanger de l'éther contre notre jeton ERC-20 nouvellement déployé.
Pour ce tutoriel, nous utiliserons le code que nous avons écrit dans le tutoriel précédent comme base. Notre DEX instanciera une instance du contrat dans son constructeur et effectuera les opérations suivantes :
- échanger des jetons contre de l'éther
- échanger de l'éther contre des jetons
Nous allons commencer le code de notre échange décentralisé en ajoutant notre base de code ERC20 simple :
1pragma solidity ^0.8.0;23interface IERC20 {45 function totalSupply() external view returns (uint256);6 function balanceOf(address account) external view returns (uint256);7 function allowance(address owner, address spender) external view returns (uint256);89 function transfer(address recipient, uint256 amount) external returns (bool);10 function approve(address spender, uint256 amount) external returns (bool);11 function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);121314 event Transfer(address indexed from, address indexed to, uint256 value);15 event Approval(address indexed owner, address indexed spender, uint256 value);16}171819contract ERC20Basic is IERC20 {2021 string public constant name = "ERC20Basic";22 string public constant symbol = "ERC";23 uint8 public constant decimals = 18;242526 mapping(address => uint256) balances;2728 mapping(address => mapping (address => uint256)) allowed;2930 uint256 totalSupply_ = 10 ether;313233 constructor() {34 balances[msg.sender] = totalSupply_;35 }3637 function totalSupply() public override view returns (uint256) {38 return totalSupply_;39 }4041 function balanceOf(address tokenOwner) public override view returns (uint256) {42 return balances[tokenOwner];43 }4445 function transfer(address receiver, uint256 numTokens) public override returns (bool) {46 require(numTokens <= balances[msg.sender]);47 balances[msg.sender] = balances[msg.sender]-numTokens;48 balances[receiver] = balances[receiver]+numTokens;49 emit Transfer(msg.sender, receiver, numTokens);50 return true;51 }5253 function approve(address delegate, uint256 numTokens) public override returns (bool) {54 allowed[msg.sender][delegate] = numTokens;55 emit Approval(msg.sender, delegate, numTokens);56 return true;57 }5859 function allowance(address owner, address delegate) public override view returns (uint) {60 return allowed[owner][delegate];61 }6263 function transferFrom(address owner, address buyer, uint256 numTokens) public override returns (bool) {64 require(numTokens <= balances[owner]);65 require(numTokens <= allowed[owner][msg.sender]);6667 balances[owner] = balances[owner]-numTokens;68 allowed[owner][msg.sender] = allowed[owner][msg.sender]-numTokens;69 balances[buyer] = balances[buyer]+numTokens;70 emit Transfer(owner, buyer, numTokens);71 return true;72 }73}7475Afficher toutNotre nouveau contrat intelligent DEX déploiera l'ERC-20 et obtiendra toute la réserve de jetons :
1contract DEX {23 IERC20 public token;45 event Bought(uint256 amount);6 event Sold(uint256 amount);78 constructor() {9 token = new ERC20Basic();10 }1112 function buy() payable public {13 // TODO14 }1516 function sell(uint256 amount) public {17 // TODO18 }1920}Afficher toutNous avons donc maintenant notre DEX et il dispose de toute la réserve de jetons. Le contrat a deux fonctions :
buy: L'utilisateur peut envoyer de l'éther et obtenir des jetons en échangesell: L'utilisateur peut envoyer des jetons pour récupérer de l'éther
La fonction buy
Codons la fonction buy. Nous devrons d'abord vérifier la quantité d'éther que le message contient et vérifier que le contrat possède suffisamment de jetons et que le message contient bien de l'éther. Si le contrat possède suffisamment de jetons, il enverra le nombre de jetons à l'utilisateur et émettra l'événement Bought.
Notez que si nous appelons la fonction require, en cas d'erreur, l'éther envoyé sera directement annulé et rendu à l'utilisateur.
Pour simplifier, nous échangeons simplement 1 jeton contre 1 wei.
1function buy() payable public {2 uint256 amountTobuy = msg.value;3 uint256 dexBalance = token.balanceOf(address(this));4 require(amountTobuy > 0, "Vous devez envoyer de l'éther");5 require(amountTobuy <= dexBalance, "Pas assez de jetons dans la réserve");6 token.transfer(msg.sender, amountTobuy);7 emit Bought(amountTobuy);8}Si l'achat réussit, nous devrions voir deux événements dans la transaction : l'événement de jeton Transfer et l'événement Bought.
La fonction sell
La fonction responsable de la vente exigera d'abord que l'utilisateur ait approuvé le montant en appelant au préalable la fonction approve. L'approbation du transfert exige que le jeton ERC20Basic instancié par le DEX soit appelé par l'utilisateur. Cela peut être réalisé en appelant d'abord la fonction token() du contrat DEX pour récupérer l'adresse où le DEX a déployé le contrat ERC20Basic appelé token. Ensuite, nous créons une instance de ce contrat dans notre session et appelons sa fonction approve. Ensuite, nous pouvons appeler la fonction sell du DEX et échanger nos jetons contre de l'éther. Par exemple, voici à quoi cela ressemble dans une session interactive de Brownie :
1#### Python in interactive brownie console...23# déployer le DEX4dex = DEX.deploy({'from':account1})56# appeler la fonction buy pour échanger de l'éther contre un jeton7# 1e18 correspond à 1 éther exprimé en wei8dex.buy({'from': account2, 1e18})910# obtenir l'adresse de déploiement du jeton ERC2011# qui a été déployé lors de la création du contrat DEX12# dex.token() renvoie l'adresse déployée pour le jeton13token = ERC20Basic.at(dex.token())1415# appeler la fonction approve du jeton16# approuver l'adresse du dex en tant que dépensier17# et le nombre de vos jetons qu'il est autorisé à dépenser18token.approve(dex.address, 3e18, {'from':account2})19Afficher toutEnsuite, lorsque la fonction sell est appelée, nous vérifierons si le transfert depuis l'adresse de l'appelant vers l'adresse du contrat a réussi, puis nous renverrons l'éther à l'adresse de l'appelant.
1function sell(uint256 amount) public {2 require(amount > 0, "Vous devez vendre au moins quelques jetons");3 uint256 allowance = token.allowance(msg.sender, address(this));4 require(allowance >= amount, "Vérifiez l'allocation de jetons");5 token.transferFrom(msg.sender, address(this), amount);6 payable(msg.sender).transfer(amount);7 emit Sold(amount);8}Si tout fonctionne, vous devriez voir 2 événements (un Transfer et un Sold) dans la transaction, et votre solde de jetons ainsi que votre solde d'éther mis à jour.
Dans ce tutoriel, nous avons vu comment vérifier le solde et l'allocation d'un jeton ERC-20, ainsi que la manière d'appeler Transfer et TransferFrom d'un contrat intelligent ERC20 en utilisant l'interface.
Une fois que vous avez effectué une transaction, nous avons un tutoriel JavaScript pour attendre et obtenir les détails sur les transactions (opens in a new tab) qui ont été effectuées sur votre contrat et un tutoriel pour décoder les événements générés par les transferts de jetons ou tout autre événement (opens in a new tab) tant que vous avez l'ABI.
Voici le code complet du tutoriel :
1pragma solidity ^0.8.0;23interface IERC20 {45 function totalSupply() external view returns (uint256);6 function balanceOf(address account) external view returns (uint256);7 function allowance(address owner, address spender) external view returns (uint256);89 function transfer(address recipient, uint256 amount) external returns (bool);10 function approve(address spender, uint256 amount) external returns (bool);11 function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);121314 event Transfer(address indexed from, address indexed to, uint256 value);15 event Approval(address indexed owner, address indexed spender, uint256 value);16}171819contract ERC20Basic is IERC20 {2021 string public constant name = "ERC20Basic";22 string public constant symbol = "ERC";23 uint8 public constant decimals = 18;242526 mapping(address => uint256) balances;2728 mapping(address => mapping (address => uint256)) allowed;2930 uint256 totalSupply_ = 10 ether;313233 constructor() {34 balances[msg.sender] = totalSupply_;35 }3637 function totalSupply() public override view returns (uint256) {38 return totalSupply_;39 }4041 function balanceOf(address tokenOwner) public override view returns (uint256) {42 return balances[tokenOwner];43 }4445 function transfer(address receiver, uint256 numTokens) public override returns (bool) {46 require(numTokens <= balances[msg.sender]);47 balances[msg.sender] = balances[msg.sender]-numTokens;48 balances[receiver] = balances[receiver]+numTokens;49 emit Transfer(msg.sender, receiver, numTokens);50 return true;51 }5253 function approve(address delegate, uint256 numTokens) public override returns (bool) {54 allowed[msg.sender][delegate] = numTokens;55 emit Approval(msg.sender, delegate, numTokens);56 return true;57 }5859 function allowance(address owner, address delegate) public override view returns (uint) {60 return allowed[owner][delegate];61 }6263 function transferFrom(address owner, address buyer, uint256 numTokens) public override returns (bool) {64 require(numTokens <= balances[owner]);65 require(numTokens <= allowed[owner][msg.sender]);6667 balances[owner] = balances[owner]-numTokens;68 allowed[owner][msg.sender] = allowed[owner][msg.sender]-numTokens;69 balances[buyer] = balances[buyer]+numTokens;70 emit Transfer(owner, buyer, numTokens);71 return true;72 }73}747576contract DEX {7778 event Bought(uint256 amount);79 event Sold(uint256 amount);808182 IERC20 public token;8384 constructor() {85 token = new ERC20Basic();86 }8788 function buy() payable public {89 uint256 amountTobuy = msg.value;90 uint256 dexBalance = token.balanceOf(address(this));91 require(amountTobuy > 0, "Vous devez envoyer de l'éther");92 require(amountTobuy <= dexBalance, "Pas assez de jetons dans la réserve");93 token.transfer(msg.sender, amountTobuy);94 emit Bought(amountTobuy);95 }9697 function sell(uint256 amount) public {98 require(amount > 0, "Vous devez vendre au moins quelques jetons");99 uint256 allowance = token.allowance(msg.sender, address(this));100 require(allowance >= amount, "Vérifiez l'allocation de jetons");101 token.transferFrom(msg.sender, address(this), amount);102 payable(msg.sender).transfer(amount);103 emit Sold(amount);104 }105106}Afficher toutDernière mise à jour de la page : 21 août 2025

