Salt la conținutul principal

Transferurile și aprobarea de tokenuuri ERC-20 dintr-un contract inteligent Solidity

contracte inteligentetokenurisoliditynoțiuni de bazăerc-20
Intermediar
jdourlens
EthereumDev(opens in a new tab)
7 aprilie 2020
6 minute de citit minute read
comp-tutorial-metadata-tip-author 0x19dE91Af973F404EDF5B4c093983a7c6E3EC8ccE

În tutorialul anterior am studiat anatomia unui token ERC-20 în Solidity pe blochain-ul Ethereum. Î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 de:

  • schimbare a tokenurilor în ether
  • schimbul de ether pe tokenuri

Vom începe codul nostru de schimb descentralizat prin adăugarea codului nostru de bază simplu ERC20:

1pragma solidity ^0.6.0;
2
3interface IERC20 {
4
5 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);
8
9 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);
12
13
14 event Transfer(address indexed from, address indexed to, uint256 value);
15 event Approval(address indexed owner, address indexed spender, uint256 value);
16}
17
18
19contract ERC20Basic is IERC20 {
20
21 string public constant name = "ERC20Basic";
22 string public constant symbol = "ERC";
23 uint8 public constant decimals = 18;
24
25
26 event Approval(address indexed tokenOwner, address indexed spender, uint tokens);
27 event Transfer(address indexed from, address indexed to, uint tokens);
28
29
30 mapping(address => uint256) balances;
31
32 mapping(address => mapping (address => uint256)) allowed;
33
34 uint256 totalSupply_ = 100 ether;
35
36 using SafeMath for uint256;
37
38 constructor(uint256 total) public {
39 balances[msg.sender] = totalSupply_;
40 }
41
42 function totalSupply() public override view returns (uint256) {
43 return totalSupply_;
44 }
45
46 function balanceOf(address tokenOwner) public override view returns (uint256) {
47 return balances[tokenOwner];
48 }
49
50 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 }
57
58 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 }
63
64 function allowance(address owner, address delegate) public override view returns (uint) {
65 return allowed[owner][delegate];
66 }
67
68 function transferFrom(address owner, address buyer, uint256 numTokens) public override returns (bool) {
69 require(numTokens <= balances[owner]);
70 require(numTokens <= allowed[owner][msg.sender]);
71
72 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}
79
80library SafeMath {
81 function sub(uint256 a, uint256 b) internal pure returns (uint256) {
82 assert(b <= a);
83 return a - b;
84 }
85
86 function add(uint256 a, uint256 b) internal pure returns (uint256) {
87 uint256 c = a + b;
88 assert(c >= a);
89 return c;
90 }
91}
Afișează tot
Copiați

Noul nostru contract inteligent DEX va implementa ERC-20 și va obține toate informațiile furnizate:

1contract DEX {
2
3 IERC20 public token;
4
5 event Bought(uint256 amount);
6 event Sold(uint256 amount);
7
8 constructor() public {
9 token = new ERC20Basic();
10 }
11
12 function buy() payable public {
13 // DE_FĂCUT
14 }
15
16 function sell(uint256 amount) public {
17 // DE_FĂCUT
18 }
19
20}
Afișează tot
Copiați

Deci, acum avem DEX-ul nostru, care are toate rezervele de tokenuri disponibile. Contractul are două funcții:

  • buy: Utilizatorul poate trimite ether și obține tokenuri în schimb
  • sell: Utilizatorul poate decide să trimită tokenuri pentru a obține ether în schimb

Funcția de cumpărare

Hai să codificăm funcția de cumpărare. Mai întâi va trebui să verificăm cantitatea de ether pe care o conține mesajul și să verificăm dacă contractele dețin suficiente tokenuri și dacă mesajul are ether. În cazul în care contractul deține suficiente tokenuri, acesta va trimite numărul de tokenuri utilizatorului și va emite evenimentul "Bought"(cumpărat).

Rețineţi că, dacă apelăm funcția „require” în cazul unei erori, etherul trimis se va întoarce imediat și va fi retrimis utilizatorului.

Pentru simplificare, schimbăm doar 1 token pe 1 ether.

1function buy() payable public {
2 uint256 amountTobuy = msg.value;
3 uint256 dexBalance = token.balanceOf(address(this));
4 require(amountTobuy > 0, "You need to send some ether");
5 require(amountTobuy <= dexBalance, "Not enough tokens in the reserve");
6 token.transfer(msg.sender, amountTobuy);
7 emit Bought(amountTobuy);
8}
Copiați

În cazul în care cumpărarea are succes, ar trebui să vedem două evenimente în tranzacție: Transfer-ul tokenului și evenimentul Bought.

Două evenimente în tranzacție: „Transfer” și „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ă transferul de la adresa apelantului la adresa contractului a avut succes și vom trimite ether-ul înapoi la adresa apelantului.

1function sell(uint256 amount) public {
2 require(amount > 0, "You need to sell at least some tokens");
3 uint256 allowance = token.allowance(msg.sender, address(this));
4 require(allowance >= amount, "Check the token allowance");
5 token.transferFrom(msg.sender, address(this), amount);
6 msg.sender.transfer(amount);
7 emit Sold(amount);
8}
Copiați

Dacă totul merge bine, ar trebui să vedeţi 2 evenimente (un „Transfer” și un „Sold”) în tranzacție și soldul tokenului dvs. și soldul Ethereum actualizate.

Două evenimente în tranzacție: „Transfer” și „Sold”

În acest tutorial am văzut cum să verificăm soldul și alocația permisă de tokenurile ERC-20 și de asemenea cum să apelăm „Transfer” și „TransferFrom” ale unui contract inteligent ERC20 folosind interfața.

Odată ce aţi efectuat o tranzacție, avem un tutorial JavaScript pentru a aștepta și a obține detalii despre tranzacțiile(opens in a new tab) care au fost efectuate cu contractul dvs. și un tutorial pentru a decoda evenimentele generate de transferurile de token sau orice alte evenimente(opens in a new tab), atâta timp cât aveţi ABI-ul.

Iată codul complet pentru acest tutorial:

1pragma solidity ^0.6.0;
2
3interface IERC20 {
4
5 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);
8
9 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);
12
13
14 event Transfer(address indexed from, address indexed to, uint256 value);
15 event Approval(address indexed owner, address indexed spender, uint256 value);
16}
17
18
19contract ERC20Basic is IERC20 {
20
21 string public constant name = "ERC20Basic";
22 string public constant symbol = "ERC";
23 uint8 public constant decimals = 18;
24
25
26 event Approval(address indexed tokenOwner, address indexed spender, uint tokens);
27 event Transfer(address indexed from, address indexed to, uint tokens);
28
29
30 mapping(address => uint256) balances;
31
32 mapping(address => mapping (address => uint256)) allowed;
33
34 uint256 totalSupply_ = 10 ether;
35
36 using SafeMath for uint256;
37
38 constructor() public {
39 balances[msg.sender] = totalSupply_;
40 }
41
42 function totalSupply() public override view returns (uint256) {
43 return totalSupply_;
44 }
45
46 function balanceOf(address tokenOwner) public override view returns (uint256) {
47 return balances[tokenOwner];
48 }
49
50 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 }
57
58 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 }
63
64 function allowance(address owner, address delegate) public override view returns (uint) {
65 return allowed[owner][delegate];
66 }
67
68 function transferFrom(address owner, address buyer, uint256 numTokens) public override returns (bool) {
69 require(numTokens <= balances[owner]);
70 require(numTokens <= allowed[owner][msg.sender]);
71
72 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}
79
80library SafeMath {
81 function sub(uint256 a, uint256 b) internal pure returns (uint256) {
82 assert(b <= a);
83 return a - b;
84 }
85
86 function add(uint256 a, uint256 b) internal pure returns (uint256) {
87 uint256 c = a + b;
88 assert(c >= a);
89 return c;
90 }
91}
92
93contract DEX {
94
95 event Bought(uint256 amount);
96 event Sold(uint256 amount);
97
98
99 IERC20 public token;
100
101 constructor() public {
102 token = new ERC20Basic();
103 }
104
105 function buy() payable public {
106 uint256 amountTobuy = msg.value;
107 uint256 dexBalance = token.balanceOf(address(this));
108 require(amountTobuy > 0, "Trebuie să trimiteți ceva ether");
109 require(amountTobuy <= dexBalance, "Nu sunt suficiente token-uri în rezervă");
110 token.transfer(msg.sender, amountTobuy);
111 emit Bought(amountTobuy);
112 }
113
114 function sell(uint256 amount) public {
115 require(amount > 0, "Trebuie să vindeți cel puțin câteva token-uri"");
116 uint256 allowance = token.allowance(msg.sender, address(this));
117 require(allowance >= amount, "Verificați alocația de token-uri");
118 token.transferFrom(msg.sender, address(this), amount);
119 msg.sender.transfer(amount);
120 emit Sold(amount);
121 }
122
123}
Afișează tot
Copiați

A fost util acest tutorial?