Przejdź do głównej zawartości

Transfery i zatwierdzenie tokenów ERC-20 z inteligentnego kontraktu Solidity

inteligentne kontraktytokenysoliditypierwsze krokierc-20
Średnie
jdourlens
EthereumDev(opens in a new tab)
7 kwietnia 2020
6 minuta czytania minute read
comp-tutorial-metadata-tip-author 0x19dE91Af973F404EDF5B4c093983a7c6E3EC8ccE

W poprzednim samouczku zbadaliśmy anatomię tokena ERC-20 w Solidity w blockchainie Ethereum. W tym artykule zobaczymy, jak możemy użyć inteligentnego kontraktu do interakcji z tokenem, używając języka Solidity.

Dla tego inteligentnego kontraktu stworzymy naprawdę fikcyjną zdecentralizowaną giełdę, na której użytkownik może wymieniać ether na nasze nowo wdrożone tokeny ERC-20.

W tym samouczku użyjemy kodu, który napisaliśmy w poprzednim samouczku jako podstawy. Nasz DEX utworzy instancjr kontraktu w konstruktorze i wykona operacje:

  • wymiana tokenów na ether
  • wymiana etheru na tokeny

Rozpoczniemy nasz zdecentralizowany kod wymiany poprzez dodanie naszej prostej bazy kodowej 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}
Pokaż wszystko
Kopiuj

Nasz nowy inteligentny kontrakt DEX wdroży ERC-20 i otrzyma wszystkie dostarczone:

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 // TODO
14 }
15
16 function sell(uint256 amount) public {
17 // TODO
18 }
19
20}
Pokaż wszystko
Kopiuj

Więc wiemy, że mamy nasz DEX i ma całą dostępną rezerwę tokenów. Kontrakt spełnia dwie funkcje:

  • buy: użytkownik może wysyłać ether i otrzymywać tokeny w zamian
  • sell: użytkownik może zdecydować się na wysłanie tokenów, aby odzyskać ether

Funkcja kupna

Zakodujmy funkcję kup. Najpierw będziemy musieli sprawdzić ilość etheru w wiadomości i sprawdzić, czy kontrakty posiadają wystarczającą ilość tokenów i czy wiadomość ma w niej jakiś eter. cJeśli kontrakt posiada wystarczającą ilość tokenów, wyśle liczbę tokenów do użytkownika i wyemituje zdarzenie Bought.

Zauważ, że jeśli wywołamy wymagającą funkcję w przypadku błędu, ether wysłany zostanie bezpośrednio przywrócony i odesłany do użytkownika.

Aby uprościć sprawę, po prostu wymieniamy 1 token na 1 eter.

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}
Kopiuj

Jeśli zakup zakończył się sukcesem, powinniśmy zobaczyć dwa zdarzenia w transakcji: Token Transfer i Bought wydarzenie.

Dwa zdarzenia w transakcji: Transfer i Bought

Funkcja kupna

Funkcja odpowiedzialna za sprzedaż będzie najpierw wymagała od użytkownika zatwierdzenia kwoty poprzez uprzednie wywołanie zatwierdzonej funkcji. Następnie, gdy funkcja sprzedaży jest uruchomiona, sprawdzimy, czy przelew z adresu dzwoniącego na adres umowy zakończył się sukcesem, a następnie wyślemy Ethers z powrotem na adres dzwoniącego.

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}
Kopiuj

Jeśli wszystko działa, powinieneś zobaczyć 2 zdarzenia w transakcji (a Transfer i Sold) i Twoje saldo tokenu i saldo Ethereum zaktualizowane.

Dwa zdarzenia w transakcji: Transfer i Sold

Z tego samouczka zobaczyliśmy, jak sprawdzić saldo i przydział tokena ERC-20, a także jak wywołać Transfer i TransferFrom inteligentnego kontraktu ERC20 przy użyciu interfejsu.

Po dokonaniu transakcji mamy samouczek JavaScript, aby poczekać i uzyskać szczegółowe informacje o transakcjach(opens in a new tab), które zostały wykonane w ramach Twojego kontraktu oraz samouczek dekodowania zdarzeń generowanych przez transfery tokenów lub inne zdarzenia(opens in a new tab), o ile masz ABI.

Oto kompletny kod do samouczka:

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, "You need to send some Ether");
109 require(amountTobuy <= dexBalance, "Not enough tokens in the reserve");
110 token.transfer(msg.sender, amountTobuy);
111 emit Bought(amountTobuy);
112 }
113
114 function sell(uint256 amount) public {
115 require(amount > 0, "You need to sell at least some tokens");
116 uint256 allowance = token.allowance(msg.sender, address(this));
117 require(allowance >= amount, "Check the token allowance");
118 token.transferFrom(msg.sender, address(this), amount);
119 msg.sender.transfer(amount);
120 emit Sold(amount);
121 }
122
123}
Pokaż wszystko
Kopiuj

Ostatnia edycja: @Beas(opens in a new tab), 31 października 2023

Czy ten samouczek był pomocny?