Passer au contenu principal

Créer et déployer une application DeFi

solidityDeFiweb3.jstruffleganachecontrats intelligents
Intermédiaire
strykerin
github.com(opens in a new tab)
31 décembre 2020
11 minutes de lecture minute read

Dans ce tutoriel, nous allons créer une application DeFi avec Solidity dans laquelle les utilisateurs peuvent déposer un jeton ERC20 dans le contrat intelligent et ce jeton va générer et leur transférer des jetons Farm. Par la suite, les utilisateurs pourront retirer leurs jetons ERC20 en brûlant leur jeton Farm sur un contrat intelligent et les jetons ERC20 leur seront transférés en retour.

Installer Truffle et Ganache

Si c'est votre première création de contrat intelligent, il vous faudra configurer votre environnement. Nous allons utiliser deux outils : Truffle(opens in a new tab) et Ganache(opens in a new tab).

Truffle est un environnement de développement et un framework de test pour l'établissement de contrats intelligents pour Ethereum. Avec Truffle, il est facile de créer et de déployer des contrats intelligents sur la blockchain. Ganache permet de créer une blockchain Ethereum locale afin de tester les contrats intelligents. Il simule les fonctionnalités du réseau réel et les 10 premiers comptes sont financés avec 100 éther de test, rendant ainsi le déploiement et le test du contrat intelligent gratuit et facile. Ganache est disponible en tant qu'application de bureau et outil de ligne de commande. Pour cet article nous allons utiliser l'application UI Desktop.

Application de bureau pour utilisateur Ganache (Ganache UI)(opens in a new tab)Application de bureau pour utilisateur Ganache

Pour créer notre projet, exécutez les commandes suivantes :

mkdir your-project-name
cd your-project-name
truffle init

Cela créera un projet vide pour l'établissement et le déploiement de nos contrats intelligents. La structure du projet créée est la suivante :

  • test : Dossier des contrats intelligents Solidity

  • migrations : Dossier des scripts de déploiement

  • test : Dossier pour tester nos contrats intelligents

  • truffle-config.js : Fichier de configuration Truffle

Créer un jeton ERC20

Nous devons premièrement créer notre jeton ERC20 que nous miserons dans le contrat intelligent. Pour créer notre jeton fongible, nous devrons commencer par installer la bibliothèque OpenZeppelin. Cette bibliothèque contient les implémentations de normes telles qu'ERC20 et ERC721. Pour l'installer, exécutez la commande :

npm install @openzeppelin/contracts

En utilisant la bibliothèque OpenZeppelin nous pouvons créer notre jeton ERC20 en écrivant sur contracts/MyToken.sol avec le code de Solidity suivant :

1pragma solidity ^0.8.0;
2
3import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
4
5contract MyToken is ERC20 {
6 constructor() public ERC20("MyToken", "MTKN"){
7 _mint(msg.sender, 1000000000000000000000000);
8 }
9}
Afficher tout
Copier

Dans le code ci-dessus à la :

  • Ligne 3 : Nous importons le contrat ERC20.sol depuis openzeppelin qui contient l'implémentation de la norme de ce jeton.

  • Ligne 5 : Nous héritons du contrat ERC20.sol.

  • Ligne 6 : Nous appelons le constructeur ERC20.sol et passons le nom et les paramètres du symbole respectivement en tant que "MyToken" et "MTKN".

  • Ligne 7 : Nous générons et transférons 1 million de jetons pour le compte qui déploie le contrat intelligent (nous utilisons les 18 décimales par défaut pour le jeton ERC20, ce qui signifie que si nous voulons générer 1 jeton, il se présentera sous la forme 1000000000000000000, 1 avec 18 zéros).

Nous pouvons voir ci-dessous l'implémentation du constructeur ERC20.sol où le champ _decimals est défini à 18 :

1string private _name;
2string private _symbol;
3uint8 private _decimals;
4
5constructor (string memory name_, string memory symbol_) public {
6 _name = name_;
7 _symbol = symbol_;
8 _decimals = 18;
9}
Afficher tout
Copier

Compiler le jeton ERC20

Pour compiler notre contrat intelligent, nous devons d'abord vérifier notre version du compilateur de Solidity. Vous pouvez la vérifier en exécutant la commande :

truffle version

La version par défaut est la version Solidity v0.5.16. Puisque notre jeton est écrit en utilisant la version Solidity 0.6.2, si nous exécutons la commande pour compiler nos contrats, nous allons générer une erreur de compilation. Afin de spécifier quelle version de compilateur Solidity utiliser, allez dans le fichier truffle-config.js et définissez la version désirée du compilateur comme ci-dessous :

1// Configure your compilers
2compilers: {
3 solc: {
4 version: "^0.8.0", // Fetch exact version from solc-bin (default: truffle's version)
5 // docker: true, // Use "0.5.1" you've installed locally with docker (default: false)
6 // settings: { // See the solidity docs for advice about optimization and evmVersion
7 // optimizer: {
8 // enabled: false,
9 // runs: 200
10 // },
11 // evmVersion: "byzantium"
12 // }
13 }
14}
Afficher tout

Maintenant, nous pouvons compiler notre contrat intelligent en exécutant la commande suivante :

truffle compile

Déployer le jeton ERC20

Après la compilation, nous pouvons maintenant déployer notre jeton.

Dans le dossier migrations, créez un fichier appelé 2_deploy_Tokens.js. Ce fichier est l'endroit où nous allons déployer à la fois notre jeton ERC20 et notre contrat intelligent FarmToken. Le code ci-dessous est utilisé pour déployer notre contrat MyToken.sol :

1const MyToken = artifacts.require("MyToken")
2
3module.exports = async function (deployer, network, accounts) {
4 // Deploy MyToken
5 await deployer.deploy(MyToken)
6 const myToken = await MyToken.deployed()
7}

Ouvrez Ganache et sélectionnez l'option "Quickstart" pour démarrer une blockchain Ethereum en local. Pour déployer notre contrat, lancez :

truffle migrate

L'adresse utilisée pour déployer nos contrats est la première de la liste d'adresses que Ganache nous indique. Pour vérifier, nous pouvons ouvrir l'application de bureau Ganache et ainsi vérifier que le solde d'ether pour le premier compte a été réduit en raison du coût d'ether pour déployer nos contrats intelligents :

Application de bureau Ganache(opens in a new tab)Application de bureau Ganache

Pour vérifier que 1 million de jetons MyToken ont été envoyés à l'adresse de déploiement, nous pouvons utiliser la console Truffle pour interagir avec notre contrat intelligent déployé.

Truffle Console est une console interactive de base qui se connecte à n'importe quel client Ethereum.(opens in a new tab)

Afin d'interagir avec notre contrat intelligent, exécutez la commande suivante :

truffle console

Nous pouvons maintenant écrire les commandes suivantes dans le terminal :

  • Récupère le contrat intelligent : myToken = await MyToken.deployed()

  • Récupère le tableau de comptes depuis Ganache : accounts = await web3.eth.getAccounts()

  • Récupère le solde pour le premier compte : balance = await myToken.balanceOf(accounts[0])

  • Formate le solde à partir de 18 décimales : web3.utils.fromWei(balance.toString())

En exécutant les commandes ci-dessus, nous constatons que la première adresse a en fait 1 million de MyTokens :

Première adresse à 1 000 000 MyTokens(opens in a new tab)

La première adresse dispose de 1 000 000 de MyTokens

Créer un Contrat Intelligent FarmToken

Le contrat intelligent FarmToken aura 3 fonctions :

  • balance() : Transférer le solde de MyToken sur le contrat intelligent FarmToken.

  • deposit(uint256 _amount) : Transférer MyToken pour le compte de l'utilisateur sur le Contrat Intelligent FarmToken puis frapper et transférer FarmToken à l'utilisateur.

  • withdraw(uint256 _amount) : Brûler les FarmTokens de l'utilisateur et transférer MyTokens à l'adresse de l'utilisateur.

Jetons un œil au constructeur FarmToken :

1pragma solidity ^0.6.2;
2
3import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
4import "@openzeppelin/contracts/utils/Address.sol";
5import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
6import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
7
8contract FarmToken is ERC20 {
9 using Address for address;
10 using SafeMath for uint256; // As of Solidity v0.8.0, mathematical operations can be done safely without the need for SafeMath
11 using SafeERC20 for IERC20;
12
13 IERC20 public token;
14
15 constructor(address _token)
16 public
17 ERC20("FarmToken", "FRM")
18 {
19 token = IERC20(_token);
20 }
Afficher tout
Copier
  • Lignes 3-6 : Nous importons depuis openzeppelin les contrats suivants : IERC20.sol, Address.sol, SafeERC20.sol et ERC20.sol.

  • Ligne 8 : Le FarmToken héritera du contrat ERC20.

  • Lignes 14-19 : Le constructeur FarmToken recevra comme paramètre l'adresse du contrat MyToken et nous attribuerons son contrat à notre variable publique appelée token.

Implémentons la fonction balance(). Elle ne recevra aucun paramètre et retournera le solde de MyToken sur ce contrat intelligent. Il s'implémente comme indiqué ci-dessous :

1function balance() public view returns (uint256) {
2 return token.balanceOf(address(this));
3}
Copier

Pour la fonction deposit(uint256 _amount), il recevra comme paramètre le montant que l'utilisateur veut déposer et il frappera et transférera FarmTokens à l'utilisateur :

1function deposit(uint256 _amount) public {
2 // Amount must be greater than zero
3 require(_amount > 0, "amount cannot be 0");
4
5 // Transfer MyToken to smart contract
6 token.safeTransferFrom(msg.sender, address(this), _amount);
7
8 // Mint FarmToken to msg sender
9 _mint(msg.sender, _amount);
10}
Afficher tout
Copier

Pour la fonction withdraw(uint256 _amount), nous recevrons comme paramètre la quantité de FarmTokens que l'utilisateur veut brûler, puis transférerons la même quantité de MyTokens à l'utilisateur :

1function withdraw(uint256 _amount) public {
2 // Burn FarmTokens from msg sender
3 _burn(msg.sender, _amount);
4
5 // Transfer MyTokens from this smart contract to msg sender
6 token.safeTransfer(msg.sender, _amount);
7}
Copier

Nous allons maintenant déployer notre contrat intelligent. Pour ce faire, nous retournerons au fichier 2_deploy_Tokens.js et ajouterons le nouveau contrat à déployer :

1const MyToken = artifacts.require("MyToken")
2const FarmToken = artifacts.require("FarmToken")
3
4module.exports = async function (deployer, network, accounts) {
5 // Deploy MyToken
6 await deployer.deploy(MyToken)
7 const myToken = await MyToken.deployed()
8
9 // Deploy Farm Token
10 await deployer.deploy(FarmToken, myToken.address)
11 const farmToken = await FarmToken.deployed()
12}
Afficher tout

Notez que lors du déploiement du FarmToken, nous passons comme paramètre l'adresse du contrat MyToken déployé.

Exécutez maintenant truffle compile et truffle migrate pour déployer nos contrats.

Testons notre contrat intelligent. Au lieu d'utiliser la console truffle pour interagir avec notre contrat intelligent, nous allons créer un script pour automatiser ce processus. Créez un dossier appelé scripts et ajoutez le fichier suivant getMyTokenBalance.js. Il vérifiera le solde de MyTokens sur le contrat intelligent FarmToken :

1const MyToken = artifacts.require("MyToken")
2const FarmToken = artifacts.require("FarmToken")
3
4module.exports = async function (callback) {
5 myToken = await MyToken.deployed()
6 farmToken = await FarmToken.deployed()
7 balance = await myToken.balanceOf(farmToken.address)
8 console.log(web3.utils.fromWei(balance.toString()))
9 callback()
10}
Afficher tout

Pour exécuter ce script, exécutez la commande suivante :

truffle exec .\scripts\getMyTokenBalance.js

Nous obtiendrons le résultat attendu qui est 0. Si vous avez comme message d'erreur que FarmToken n'est pas encore déployé, cela signifie que le réseau truffle n'a pas reçu la dernière version du code du contrat. Fermez tout simplement ganache, démarrez-le à nouveau et veillez à exécuter truffle migrate.

Maintenant, misons MyToken sur le contrat intelligent. Dès lors que la fonction deposit(uint256 _amount) appelle la fonction safeTransferFrom de l'ERC20, l'utilisateur doit d'abord approuver le contrat intelligent pour transférer MyToken en nom et place de l'utilisateur. Donc, sur le script ci-dessous, nous allons d'abord approuver cette étape puis nous appellerons la fonction :

1const MyToken = artifacts.require("MyToken")
2const FarmToken = artifacts.require("FarmToken")
3
4module.exports = async function (callback) {
5 const accounts = await new web3.eth.getAccounts()
6 const myToken = await MyToken.deployed()
7 const farmToken = await FarmToken.deployed()
8
9 // Returns the remaining number of tokens that spender will be allowed to spend on behalf of owner through transferFrom.
10 // This is zero by default.
11 const allowanceBefore = await myToken.allowance(
12 accounts[0],
13 farmToken.address
14 )
15 console.log(
16 "Amount of MyToken FarmToken is allowed to transfer on our behalf Before: " +
17 allowanceBefore.toString()
18 )
19
20 // In order to allow the Smart Contract to transfer to MyToken (ERC-20) on the accounts[0] behalf,
21 // we must explicitly allow it.
22 // We allow farmToken to transfer x amount of MyToken on our behalf
23 await myToken.approve(farmToken.address, web3.utils.toWei("100", "ether"))
24
25 // Validate that the farmToken can now move x amount of MyToken on our behalf
26 const allowanceAfter = await myToken.allowance(accounts[0], farmToken.address)
27 console.log(
28 "Amount of MyToken FarmToken is allowed to transfer on our behalf After: " +
29 allowanceAfter.toString()
30 )
31
32 // Verify accounts[0] and farmToken balance of MyToken before and after the transfer
33 balanceMyTokenBeforeAccounts0 = await myToken.balanceOf(accounts[0])
34 balanceMyTokenBeforeFarmToken = await myToken.balanceOf(farmToken.address)
35 console.log("*** My Token ***")
36 console.log(
37 "Balance MyToken Before accounts[0] " +
38 web3.utils.fromWei(balanceMyTokenBeforeAccounts0.toString())
39 )
40 console.log(
41 "Balance MyToken Before TokenFarm " +
42 web3.utils.fromWei(balanceMyTokenBeforeFarmToken.toString())
43 )
44
45 console.log("*** Farm Token ***")
46 balanceFarmTokenBeforeAccounts0 = await farmToken.balanceOf(accounts[0])
47 balanceFarmTokenBeforeFarmToken = await farmToken.balanceOf(farmToken.address)
48 console.log(
49 "Balance FarmToken Before accounts[0] " +
50 web3.utils.fromWei(balanceFarmTokenBeforeAccounts0.toString())
51 )
52 console.log(
53 "Balance FarmToken Before TokenFarm " +
54 web3.utils.fromWei(balanceFarmTokenBeforeFarmToken.toString())
55 )
56 // Call Deposit function from FarmToken
57 console.log("Call Deposit Function")
58 await farmToken.deposit(web3.utils.toWei("100", "ether"))
59 console.log("*** My Token ***")
60 balanceMyTokenAfterAccounts0 = await myToken.balanceOf(accounts[0])
61 balanceMyTokenAfterFarmToken = await myToken.balanceOf(farmToken.address)
62 console.log(
63 "Balance MyToken After accounts[0] " +
64 web3.utils.fromWei(balanceMyTokenAfterAccounts0.toString())
65 )
66 console.log(
67 "Balance MyToken After TokenFarm " +
68 web3.utils.fromWei(balanceMyTokenAfterFarmToken.toString())
69 )
70
71 console.log("*** Farm Token ***")
72 balanceFarmTokenAfterAccounts0 = await farmToken.balanceOf(accounts[0])
73 balanceFarmTokenAfterFarmToken = await farmToken.balanceOf(farmToken.address)
74 console.log(
75 "Balance FarmToken After accounts[0] " +
76 web3.utils.fromWei(balanceFarmTokenAfterAccounts0.toString())
77 )
78 console.log(
79 "Balance FarmToken After TokenFarm " +
80 web3.utils.fromWei(balanceFarmTokenAfterFarmToken.toString())
81 )
82
83 // End function
84 callback()
85}
Afficher tout

Pour exécuter ce script : truffle exec .\scripts\transferMyTokenToFarmToken.js. Vous devriez voir sur votre console :

sortie de transferMyTokenToFarmToken.js(opens in a new tab)

sortie de transferMyTokenToFarmToken.js

Comme nous pouvons le voir, nous avons déposé avec succès MyTokens sur le contrat intelligent car le premier compte est maintenant associé à FarmTokens.

Afin de retirer :

1const MyToken = artifacts.require("MyToken")
2const FarmToken = artifacts.require("FarmToken")
3
4module.exports = async function (callback) {
5 const accounts = await new web3.eth.getAccounts()
6 const myToken = await MyToken.deployed()
7 const farmToken = await FarmToken.deployed()
8
9 // Verify accounts[0] and farmToken balance of MyToken before and after the transfer
10 balanceMyTokenBeforeAccounts0 = await myToken.balanceOf(accounts[0])
11 balanceMyTokenBeforeFarmToken = await myToken.balanceOf(farmToken.address)
12 console.log("*** My Token ***")
13 console.log(
14 "Balance MyToken Before accounts[0] " +
15 web3.utils.fromWei(balanceMyTokenBeforeAccounts0.toString())
16 )
17 console.log(
18 "Balance MyToken Before TokenFarm " +
19 web3.utils.fromWei(balanceMyTokenBeforeFarmToken.toString())
20 )
21
22 console.log("*** Farm Token ***")
23 balanceFarmTokenBeforeAccounts0 = await farmToken.balanceOf(accounts[0])
24 balanceFarmTokenBeforeFarmToken = await farmToken.balanceOf(farmToken.address)
25 console.log(
26 "Balance FarmToken Before accounts[0] " +
27 web3.utils.fromWei(balanceFarmTokenBeforeAccounts0.toString())
28 )
29 console.log(
30 "Balance FarmToken Before TokenFarm " +
31 web3.utils.fromWei(balanceFarmTokenBeforeFarmToken.toString())
32 )
33
34 // Call Deposit function from FarmToken
35 console.log("Call Withdraw Function")
36 await farmToken.withdraw(web3.utils.toWei("100", "ether"))
37
38 console.log("*** My Token ***")
39 balanceMyTokenAfterAccounts0 = await myToken.balanceOf(accounts[0])
40 balanceMyTokenAfterFarmToken = await myToken.balanceOf(farmToken.address)
41 console.log(
42 "Balance MyToken After accounts[0] " +
43 web3.utils.fromWei(balanceMyTokenAfterAccounts0.toString())
44 )
45 console.log(
46 "Balance MyToken After TokenFarm " +
47 web3.utils.fromWei(balanceMyTokenAfterFarmToken.toString())
48 )
49
50 console.log("*** Farm Token ***")
51 balanceFarmTokenAfterAccounts0 = await farmToken.balanceOf(accounts[0])
52 balanceFarmTokenAfterFarmToken = await farmToken.balanceOf(farmToken.address)
53 console.log(
54 "Balance FarmToken After accounts[0] " +
55 web3.utils.fromWei(balanceFarmTokenAfterAccounts0.toString())
56 )
57 console.log(
58 "Balance FarmToken After TokenFarm " +
59 web3.utils.fromWei(balanceFarmTokenAfterFarmToken.toString())
60 )
61
62 // End function
63 callback()
64}
Afficher tout

Pour exécuter ce script : truffle exec .\scripts\transferMyTokenToFarmToken.js. Comme nous pouvons le constater avec l'exemple ci-dessous, nous avons réussi à récupérer les MyTokens et nous avons supprimé les FarmTokens :

sortie de withdrawMyTokenFromTokenFarm.js(opens in a new tab)

sortie de withdrawMyTokenFromTokenFarm.js

Références

Contrats - Documentation OpenZeppelin(opens in a new tab)

Outils Sympas pour contrats intelligents | Suite Truffle(opens in a new tab)

Ganache | Suite Truffle(opens in a new tab)

Qu'est-ce que DeFi ? Guide du débutant (mise à jour 2021) (99bitcoins.com)(opens in a new tab)

DeFi - Le classement de la finance décentralisée sur DeFi Llama(opens in a new tab)

Dernière modification: @MATsxm(opens in a new tab), 15 août 2023

Ce tutoriel vous a été utile ?