Transferuri și aprobarea de tokenuri ERC-20 dintr-un contract inteligent Solidity
În tutorialul anterior am studiat anatomia unui token ERC-20 în Solidity pe Ethereum blockchain. În acest articol vom vedea cum putem folosi un contract inteligent pentru a interacționa cu un token folosind limbajul Solidity.
Pentru acest contract inteligent, vom crea un schimb descentralizat fictiv unde un utilizator poate tranzacționa Ethereum cu tokenul nostru ERC-20 recent implementat.
Pentru acest tutorial vom folosi codul pe care l-am scris în tutorialul anterior ca bază. DEX-ul nostru va crea o instanță a contractului în constructorul său și va efectua operațiunile:
- schimbarea tokenurilor în eter
- schimbarea eterului în tokenuri
Vom începe codul nostru de schimb descentralizat prin adăugarea codului nostru de bază simplu ERC20:
1pragma solidity ^0.6.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 event Approval(address indexed tokenOwner, address indexed spender, uint tokens);27 event Transfer(address indexed from, address indexed to, uint tokens);282930 mapping(address => uint256) balances;3132 mapping(address => mapping (address => uint256)) allowed;3334 uint256 totalSupply_ = 100 ether;3536 using SafeMath for uint256;3738 constructor(uint256 total) public {39 balances[msg.sender] = totalSupply_;40 }4142 function totalSupply() public override view returns (uint256) {43 return totalSupply_;44 }4546 function balanceOf(address tokenOwner) public override view returns (uint256) {47 return balances[tokenOwner];48 }4950 function transfer(address receiver, uint256 numTokens) public override returns (bool) {51 require(numTokens <= balances[msg.sender]);52 balances[msg.sender] = balances[msg.sender].sub(numTokens);53 balances[receiver] = balances[receiver].add(numTokens);54 emit Transfer(msg.sender, receiver, numTokens);55 return true;56 }5758 function approve(address delegate, uint256 numTokens) public override returns (bool) {59 allowed[msg.sender][delegate] = numTokens;60 emit Approval(msg.sender, delegate, numTokens);61 return true;62 }6364 function allowance(address owner, address delegate) public override view returns (uint) {65 return allowed[owner][delegate];66 }6768 function transferFrom(address owner, address buyer, uint256 numTokens) public override returns (bool) {69 require(numTokens <= balances[owner]);70 require(numTokens <= allowed[owner][msg.sender]);7172 balances[owner] = balances[owner].sub(numTokens);73 allowed[owner][msg.sender] = allowed[owner][msg.sender].sub(numTokens);74 balances[buyer] = balances[buyer].add(numTokens);75 emit Transfer(owner, buyer, numTokens);76 return true;77 }78}7980library SafeMath {81 function sub(uint256 a, uint256 b) internal pure returns (uint256) {82 assert(b <= a);83 return a - b;84 }8586 function add(uint256 a, uint256 b) internal pure returns (uint256) {87 uint256 c = a + b;88 assert(c >= a);89 return c;90 }91}92Afișează totCopiere
Noul nostru contract inteligent DEX va implementa ERC-20 și va obține toate informațiile furnizate:
1contract DEX {23 IERC20 public token;45 event Bought(uint256 amount);6 event Sold(uint256 amount);78 constructor() public {9 token = new ERC20Basic();10 }1112 function buy() payable public {13 // DE_FĂCUT14 }1516 function sell(uint256 amount) public {17 // DE_FĂCUT18 }1920}21Afișează totCopiere
Deci acum avem contractul nostru DEX și el are disponibile toate rezervele de tokenuri. Contractul are două funcții:
buy
: Utilizatorul poate trimite eter și obține tokenuri în schimbsell
: Utilizatorul poate decide să trimită tokenuri pentru a obține eter înapoi
Funcția de cumpărare
Să programăm funcția de cumpărare. Mai întâi va trebui să verificăm cantitatea de eter pe care o conține mesajul și să verificăm dacă contractele dețin suficiente tokenuri și dacă mesajul are eter. În cazul în care contractul deține suficiente tokenuri, acesta va trimite numărul de tokenuri utilizatorului și va emite evenimentul "Bought"
.
Reține că, dacă apelăm funcția „require” în cazul unei erori, eterul trimis va fi reîntors direct și retrimis utilizatorului.
Pentru a păstra lucrurile simple, schimbăm doar 1 token pentru 1 eter.
1function buy() payable public {2 uint256 amountTobuy = msg.value;3 uint256 dexBalance = token.balanceOf(address(this));4 require(amountTobuy > 0, "Trebuie să trimiți niște eter");5 require(amountTobuy <= dexBalance, "Nu există suficiente tokenuri în rezervă");6 token.transfer(msg.sender, amountTobuy);7 emit Bought(amountTobuy);8}9Copiere
În cazul în care cumpărarea are succes, ar trebui să vedem două evenimente în tranzacție: Transfer
-ul de token și evenimentul Bought
.
Funcția de vânzare
Funcția responsabilă pentru vânzare, „sell”, va solicita mai întâi utilizatorului să aprobe suma apelând în prealabil funcția „approve”. Atunci când funcția „sell” este apelată, vom verifica dacă „transferFrom" de la adresa apelantului la adresa contractului a avut succes și vom trimite eteri înapoi la adresa apelantului.
1function sell(uint256 amount) public {2 require(amount > 0, "Trebuie să vinzi cel puțin câteva tokenuri");3 uint256 allowance = token.allowance(msg.sender, address(this));4 require(allowance >= amount, "Verifică alocația de tokenuri");5 token.transferFrom(msg.sender, address(this), amount);6 msg.sender.transfer(amount);7 emit Sold(amount);8}9Copiere
Dacă totul merge bine ar trebui să ai 2 evenimente (un „Transfer”
și un „Sold”
) în tranzacție și soldul tokenului și al Ethereum actualizate.
În acest tutorial am văzut cum să verificăm soldul și alocația permisă de tokenuri ERC-20 și, de asemenea, cum să apelăm „Transfer”
și „TransferFrom”
ale unui contract inteligent ERC20 folosind interfața.
Odată ce ai făcut o tranzacție avem un tutorial JavaScript pentru a aștepta și a obține detalii despre tranzacțiile care au fost făcute contractului tău și un tutorial pentru a decoda evenimente generate de transferurile de token sau orice alte evenimente, atâta timp cât ai ABI-ul.
Aici este codul complet pentru acest tutorial:
1pragma solidity ^0.6.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 event Approval(address indexed tokenOwner, address indexed spender, uint tokens);27 event Transfer(address indexed from, address indexed to, uint tokens);282930 mapping(address => uint256) balances;3132 mapping(address => mapping (address => uint256)) allowed;3334 uint256 totalSupply_ = 10 ether;3536 using SafeMath for uint256;3738 constructor() public {39 balances[msg.sender] = totalSupply_;40 }4142 function totalSupply() public override view returns (uint256) {43 return totalSupply_;44 }4546 function balanceOf(address tokenOwner) public override view returns (uint256) {47 return balances[tokenOwner];48 }4950 function transfer(address receiver, uint256 numTokens) public override returns (bool) {51 require(numTokens <= balances[msg.sender]);52 balances[msg.sender] = balances[msg.sender].sub(numTokens);53 balances[receiver] = balances[receiver].add(numTokens);54 emit Transfer(msg.sender, receiver, numTokens);55 return true;56 }5758 function approve(address delegate, uint256 numTokens) public override returns (bool) {59 allowed[msg.sender][delegate] = numTokens;60 emit Approval(msg.sender, delegate, numTokens);61 return true;62 }6364 function allowance(address owner, address delegate) public override view returns (uint) {65 return allowed[owner][delegate];66 }6768 function transferFrom(address owner, address buyer, uint256 numTokens) public override returns (bool) {69 require(numTokens <= balances[owner]);70 require(numTokens <= allowed[owner][msg.sender]);7172 balances[owner] = balances[owner].sub(numTokens);73 allowed[owner][msg.sender] = allowed[owner][msg.sender].sub(numTokens);74 balances[buyer] = balances[buyer].add(numTokens);75 emit Transfer(owner, buyer, numTokens);76 return true;77 }78}7980library SafeMath {81 function sub(uint256 a, uint256 b) internal pure returns (uint256) {82 assert(b <= a);83 return a - b;84 }8586 function add(uint256 a, uint256 b) internal pure returns (uint256) {87 uint256 c = a + b;88 assert(c >= a);89 return c;90 }91}9293contract DEX {9495 event Bought(uint256 amount);96 event Sold(uint256 amount);979899 IERC20 public token;100101 constructor() public {102 token = new ERC20Basic();103 }104105 function buy() payable public {106 uint256 amountTobuy = msg.value;107 uint256 dexBalance = token.balanceOf(address(this));108 require(amountTobuy > 0, "Trebuie să trimiți niște eter");109 require(amountTobuy <= dexBalance, "Nu există suficiente tokenuri în rezervă");110 token.transfer(msg.sender, amountTobuy);111 emit Bought(amountTobuy);112 }113114 function sell(uint256 amount) public {115 require(amount > 0, "Trebuie să vinzi cel puțin câteva tokenuri");116 uint256 allowance = token.allowance(msg.sender, address(this));117 require(allowance >= amount, "Verifică alocația de tokenuri");118 token.transferFrom(msg.sender, address(this), amount);119 msg.sender.transfer(amount);120 emit Sold(amount);121 }122123}124Afișează totCopiere