Salt la conținutul principal

Creați și implementați o aplicație DeFi

soliditydefiweb3.jstruffleganachecontracte inteligente
Intermediar
strykerin
github.com(opens in a new tab)
31 decembrie 2020
11 minute de citit minute read

În acest tutorial vom construi o aplicație DeFi cu Solidity în care utilizatorii pot depune un token ERC20 în contractul inteligent „FarmToken”, iar acesta va emite și va transfera tokenuri „Farm Token” către utilizatori. Utilizatorii își pot retrage ulterior tokenurile ERC20 prin arderea de tokenuri„Farm Token” de pe contractul inteligent „FarmToken”, iar tokenurile ERC20 le vor fi transferate înapoi.

Instalați Truffle și Ganache

Dacă aceasta este prima dată când scrieți un contract inteligent, va trebui să vă configurați mediul. Vom folosi două instrumente: Truffle(opens in a new tab) și Ganache(opens in a new tab).

Truffle este un mediu de dezvoltare și un framework de testare pentru dezvoltarea de contracte inteligente pentru Ethereum. Cu Truffle este ușor să construiți și să implementați contracte inteligente în blockchain. Ganache ne permite să creăm un blockchain Ethereum local pentru a testa contractele inteligente. Acesta simulează funcționalitățile rețelei reale, iar primele 10 conturi sunt finanțate cu 100 de ether de test, făcând astfel ca implementarea și testarea contractelor inteligente să fie gratuită și simplă. Ganache este disponibil ca aplicație pentru desktop și ca instrument de linie de comandă. Pentru acest articol vom folosi aplicația pentru desktop UI.

Aplicația pentru desktop Ganache UI(opens in a new tab)Aplicația pentru desktop Ganache UI

Pentru a crea proiectul, executați următoarele comenzi

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

Acestea vor crea un proiect gol pentru dezvoltarea și implementarea contractelor noastre inteligente. Structura proiectului creat este următoarea:

  • contracts: Dosar pentru contractele inteligente solidity

  • migrations: Dosar pentru scripturile de implementare

  • test: Dosar pentru testarea contractelor noastre inteligente

  • truffle-config.js: Fișier de configurare Truffle

Creați tokenul ERC20

Mai întâi trebuie să creăm tokenul ERC20 pe care îl vom folosi pentru a miza în contractul inteligent. Pentru a ne crea tokenul fungibil, va trebui mai întâi să instalăm biblioteca OpenZeppelin. Această bibliotecă include implementări ale unor standarde precum ERC20 și ERC721. Pentru a o instala, executați comanda:

npm install @openzeppelin/contracts

Utilizând biblioteca OpenZeppelin, ne putem crea tokenul ERC20 scriind în contracts/MyToken.sol cu următorul cod solidity:

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}
Afișează tot
Copiați

În codul de mai sus, pe:

  • Linia 3: Importăm contractul ERC20.sol din openzeppelin care conține implementarea acestui standard de token.

  • Linia 5: Moștenim din contractul ERC20.sol.

  • Linia 6: Apelăm constructorul ERC20.sol și trecem parametrii „nume” și „simbol” ca fiind ”MyToken” și, respectiv, ”MTKN”.

  • Linia 7: Vom emite și transfera 1 milion de tokenuri pentru contul care implementează contractul inteligent (folosim cele 18 zecimale implicite pentru tokenul ERC20, ceea ce înseamnă că, dacă vrem să emitem 1 token, îl vom reprezenta ca 10000000000000000000000, 1 cu 18 zerouri).

Putem vedea mai jos implementarea constructorului ERC20.sol, în care câmpul _decimals este setat la 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}
Afișează tot
Copiați

Compilați tokenul ERC20

Pentru a ne compila contractul inteligent, trebuie mai întâi să verificăm versiunea compilatorului Solidity. Puteți verifica aceasta executând comanda:

truffle version

Versiunea implicită este Solidity v0.5.16. Deoarece tokenul nostru este scris folosind versiunea solidity 0.6.2, dacă executăm comanda de compilare a contractelor noastre, vom primi o eroare de compilare. Pentru a specifica ce versiune de compilator solidity să folosiți, accesați fișierul truffle-config.js și setați versiunea de compilator dorită, așa cum se arată mai jos:

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}
Afișează tot

Acum ne putem compila contractul inteligent prin rularea următoarei comenzi:

truffle compile

Implementați tokenul ERC20

Abia acum, după compilare, ne putem implementa tokenul.

În folderul migrations, creați un fișier numit 2_deploy_Tokens.js. Acest fișier este locul în care ne vom implementa atât tokenul ERC20, cât și contractul inteligent „FarmToken”. Codul de mai jos este utilizat pentru a implementa contractul 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}

Deschideți Ganache și selectați opțiunea „Quickstart” („Pornire rapidă”) pentru a porni un blockchain Ethereum local. Pentru a ne implementa contractul, executați:

truffle migrate

Adresa utilizată pentru implementarea contractelor noastre este prima din lista de adrese pe care ne-o arată Ganache. Pentru a verifica aceasta, putem să deschidem aplicația Ganache pentru desktop și să verificăm că soldul de ether pentru primul cont s-a micșorat din cauza costului etherului necesar pentru implementarea contractelor noastre inteligente:

Aplicația Ganache pentru desktop(opens in a new tab)Aplicația Ganache pentru desktop

Pentru a verifica dacă 1 milion de tokenuri „MyToken” au fost trimise la adresa de implementare, putem folosi Consola Truffle pentru a interacționa cu contractul nostru inteligent implementat.

Truffle Console este o consolă interactivă de bază care se conectează la orice client Ethereum.(opens in a new tab)

Pentru a interacționa cu contractul nostru inteligent, rulați următoarea comandă:

truffle console

Acum putem scrie următoarele comenzi în terminal:

  • Obținerea contractului inteligent: myToken = await MyToken.deployed()

  • Obținerea matricii de conturi din Ganache: accounts = await web3.eth.getAccounts()

  • Obținerea soldului pentru primul cont: balance = await myToken.balanceOf(accounts[0])

  • Formatați soldul din 18 zecimale: web3.utils.fromWei(balance.toString())

Executând comenzile de mai sus, vom vedea că prima adresă are de fapt 1 milion de tokenuri „MyTokens”:

Prima adresă are 1000000 MyTokens(opens in a new tab)

Prima adresă are 1000000 de tokenuri „MyTokens”

Creați contractul inteligent „FarmToken”

Contractul inteligent „FarmToken” va avea 3 funcții:

  • balance(): Obțineţi soldul de tokenuri „MyToken” pe contractul inteligent „FarmToken”.

  • deposit(uint256 _amount): Transferaţi tokenurile „MyToken” în numele utilizatorului către contractul inteligent „FarmToken”, apoi emiteţi și transferaţi tokenurile „FarmToken” către utilizator.

  • withdraw(uint256 _amount): Ardeţi tokenurile „FarmToken” ale utilizatorului, apoi transferaţi tokenurile „MyToken” la adresa utilizatorului.

Să examinăm constructorul contractului inteligent "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 }
Afișează tot
Copiați
  • Liniile 3-6: Importăm următoarele contracte din openzeppelin: IERC20.sol, Address.sol, SafeERC20.sol și ERC20.sol.

  • Linia 8: „FarmToken” va moșteni din contractul ERC20.

  • Liniile 14-19: Constructorul „FarmToken” va primi ca parametru adresa contractului „MyToken” și vom atribui contractul său variabilei noastre publice numite token.

Haideți să implementăm funcția balance(). Acesta nu va primi niciun parametru și va returna soldul de tokenuri „MyToken” pe acest contract inteligent. Este implementat după cum se arată mai jos:

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

În ce privește funcția depozit(uint256 _amount), aceasta va primi ca parametru suma pe care utilizatorul dorește să o depună, apoi va emite și va transfera tokenuri „FarmTokens” către utilizator:

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}
Afișează tot
Copiați

În ce privește funcția withdraw(uint256 _amount), vom primi ca parametru suma de tokenuri „FarmTokens” pe care utilizatorul dorește să le ardă și apoi vom transfera aceeași sumă de tokenuri „MyTokens” înapoi către utilizator:

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}
Copiați

În continuare ne vom implementa contractul inteligent. Pentru aceasta, ne vom întoarce la fișierul 2_deploy_Tokens.js și vom adăuga noul contract care urmează să fie implementat:

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}
Afișează tot

De reținut că, la implementarea contractului inteligent „FarmToken”, trecem ca parametru adresa contractului MyToken implementat.

Iar acum rulați truffle compile și truffle migrate pentru a ne implementa contractele.

Acum să ne testăm contractul inteligent. Pentru aceasta, în loc să folosim truffle console ca să interacționăm cu contractul nostru inteligent, vom crea un script pentru a automatiza acest proces. Creați un dosar numit scripts și adăugați următorul fișier getMyTokenBalance.js. Acesta va verifica soldul de tokenuri „MyTokens” din contractul inteligent "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}
Afișează tot

Pentru a executa acest script, executați comanda „cli” de mai jos:

truffle exec .\scripts\getMyTokenBalance.js

Vom obține rezultatul preconizat, care este 0. Atunci când primiți o eroare care precizează că implementarea contractului dvs. inteligent „FarmToken” nu s-a efectuat încă, înseamnă că rețeaua Truffle nu a primit cea mai recentă versiune a codului contractului dvs. În acest acaz, închideți Ganache, reporniți-l și nu uitați să rulați truffle migrate.

Mai departe vom miza tokenurile „MyToken" pe contractul inteligent. Pentru că funcția depozit(uint256 _amount) apelează funcția safeTransferFrom din ERC20, utilizatorul trebuie întâi să autorizeze contractul inteligent să transfere tokenurile „MyToken” în numele său. De aceea, în scriptul de mai jos, vom autoriza întâi acest pas și apoi vom apela funcția:

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}
Afișează tot

Pentru a rula acest script, introduceţi comanda:truffle exec .\scripts\transferMyTokenToFarmToken.js. Veți vedea pe consolă:

rezultatul comenzii „transferMyTokenToFarmToken.js”(opens in a new tab)

rezultatul comenzii „transferMyTokenToFarmToken.js”

După cum se vede, am reuşit să depunem tokenuri "MyTokens" în contractul inteligent "FarmToken", deoarece acest cont are acum tokenuri "FarmTokens".

Pentru a retrage:

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}
Afișează tot

Pentru a rula scriptul, introduceţi comanda: truffle exec .\scripts\withdrawMyTokenFromTokenFarm.js. După cum vedem din rezultatul de mai jos, am recuperat cu succes tokenurile „MyTokens” și am ars tokenurile „FarmTokens”:

rezultatul comenzii „withdrawMyTokenFromTokenFarm.js”(opens in a new tab)

rezultatul comenzii „withdrawMyTokenFromTokenFarm.js”

Referințe

Contracte - Documente OpenZeppelin(opens in a new tab)

Instrumente simpatice pentru contractele inteligente | Suita Truffle(opens in a new tab)

Ganache | Suita Truffle(opens in a new tab)

Ce este DeFi? Un ghid pentru începători (actualizat în 2021) (99bitcoins.com)(opens in a new tab)

DeFi - Clasamentul finanțelor descentralizate la DeFi Llama(opens in a new tab)

Ultima modificare: @nicklcanada(opens in a new tab), 15 august 2023

A fost util acest tutorial?