Creați și implementați o aplicație DeFi
Î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.
(opens in a new tab)Aplicația pentru desktop Ganache UI
Pentru a crea proiectul, executați următoarele comenzi
mkdir your-project-namecd your-project-nametruffle 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 soliditymigrations
: Dosar pentru scripturile de implementaretest
: Dosar pentru testarea contractelor noastre inteligentetruffle-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;23import "@openzeppelin/contracts/token/ERC20/ERC20.sol";45contract MyToken is ERC20 {6 constructor() public ERC20("MyToken", "MTKN"){7 _mint(msg.sender, 1000000000000000000000000);8 }9}Afișează totCopiaț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;45constructor (string memory name_, string memory symbol_) public {6 _name = name_;7 _symbol = symbol_;8 _decimals = 18;9}Afișează totCopiaț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 compilers2compilers: {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 evmVersion7 // optimizer: {8 // enabled: false,9 // runs: 20010 // },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 migration
s, 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")23module.exports = async function (deployer, network, accounts) {4 // Deploy MyToken5 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:
(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.
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 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;23import "@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";78contract 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 SafeMath11 using SafeERC20 for IERC20;1213 IERC20 public token;1415 constructor(address _token)16 public17 ERC20("FarmToken", "FRM")18 {19 token = IERC20(_token);20 }Afișează totCopiaț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 zero3 require(_amount > 0, "amount cannot be 0");45 // Transfer MyToken to smart contract6 token.safeTransferFrom(msg.sender, address(this), _amount);78 // Mint FarmToken to msg sender9 _mint(msg.sender, _amount);10}Afișează totCopiaț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 sender3 _burn(msg.sender, _amount);45 // Transfer MyTokens from this smart contract to msg sender6 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")34module.exports = async function (deployer, network, accounts) {5 // Deploy MyToken6 await deployer.deploy(MyToken)7 const myToken = await MyToken.deployed()89 // Deploy Farm Token10 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")34module.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")34module.exports = async function (callback) {5 const accounts = await new web3.eth.getAccounts()6 const myToken = await MyToken.deployed()7 const farmToken = await FarmToken.deployed()89 // 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.address14 )15 console.log(16 "Amount of MyToken FarmToken is allowed to transfer on our behalf Before: " +17 allowanceBefore.toString()18 )1920 // 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 behalf23 await myToken.approve(farmToken.address, web3.utils.toWei("100", "ether"))2425 // Validate that the farmToken can now move x amount of MyToken on our behalf26 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 )3132 // Verify accounts[0] and farmToken balance of MyToken before and after the transfer33 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 )4445 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 FarmToken57 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 )7071 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 )8283 // End function84 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”
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")34module.exports = async function (callback) {5 const accounts = await new web3.eth.getAccounts()6 const myToken = await MyToken.deployed()7 const farmToken = await FarmToken.deployed()89 // Verify accounts[0] and farmToken balance of MyToken before and after the transfer10 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 )2122 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 )3334 // Call Deposit function from FarmToken35 console.log("Call Withdraw Function")36 await farmToken.withdraw(web3.utils.toWei("100", "ether"))3738 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 )4950 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 )6162 // End function63 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”
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), Invalid DateTime