Zum Hauptinhalt springen

Übertragungen und Freigaben von ERC-20-Token aus einem Solidity-Smart-Contract

Smart Contracts
Token
Solidity
erc-20
Fortgeschritten
jdourlens
7. April 2020
7 Minuten Lesezeit

Im vorherigen Tutorial haben wir die Anatomie eines ERC-20-Tokens in Solidity auf der Ethereum-Blockchain untersucht. In diesem Artikel werden wir sehen, wie wir einen Smart Contract verwenden können, um mit einem Token unter Verwendung der Sprache Solidity zu interagieren.

Für diesen Smart Contract erstellen wir eine echte, fiktive dezentralisierte Börse, an der ein Benutzer Ether gegen unseren neu bereitgestellten ERC-20-Token tauschen kann.

Für dieses Tutorial verwenden wir den Code, den wir im vorherigen Tutorial geschrieben haben, als Basis. Unsere DEX wird in ihrem Konstruktor eine Instanz des Vertrags instanziieren und folgende Operationen ausführen:

  • Tauschen von Token gegen Ether
  • Tauschen von Ether gegen Token

Wir beginnen unseren Code für die dezentralisierte Börse, indem wir unsere einfache ERC-20-Codebasis hinzufügen:

1pragma solidity ^0.8.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 mapping(address => uint256) balances;
27
28 mapping(address => mapping (address => uint256)) allowed;
29
30 uint256 totalSupply_ = 10 ether;
31
32
33 constructor() {
34 balances[msg.sender] = totalSupply_;
35 }
36
37 function totalSupply() public override view returns (uint256) {
38 return totalSupply_;
39 }
40
41 function balanceOf(address tokenOwner) public override view returns (uint256) {
42 return balances[tokenOwner];
43 }
44
45 function transfer(address receiver, uint256 numTokens) public override returns (bool) {
46 require(numTokens <= balances[msg.sender]);
47 balances[msg.sender] = balances[msg.sender]-numTokens;
48 balances[receiver] = balances[receiver]+numTokens;
49 emit Transfer(msg.sender, receiver, numTokens);
50 return true;
51 }
52
53 function approve(address delegate, uint256 numTokens) public override returns (bool) {
54 allowed[msg.sender][delegate] = numTokens;
55 emit Approval(msg.sender, delegate, numTokens);
56 return true;
57 }
58
59 function allowance(address owner, address delegate) public override view returns (uint) {
60 return allowed[owner][delegate];
61 }
62
63 function transferFrom(address owner, address buyer, uint256 numTokens) public override returns (bool) {
64 require(numTokens <= balances[owner]);
65 require(numTokens <= allowed[owner][msg.sender]);
66
67 balances[owner] = balances[owner]-numTokens;
68 allowed[owner][msg.sender] = allowed[owner][msg.sender]-numTokens;
69 balances[buyer] = balances[buyer]+numTokens;
70 emit Transfer(owner, buyer, numTokens);
71 return true;
72 }
73}
74
75
Alle anzeigen

Unser neuer DEX-Smart-Contract wird den ERC-20 bereitstellen und das gesamte Angebot erhalten:

1contract DEX {
2
3 IERC20 public token;
4
5 event Bought(uint256 amount);
6 event Sold(uint256 amount);
7
8 constructor() {
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}
Alle anzeigen

Wir haben nun also unsere DEX und sie verfügt über die gesamte Token-Reserve. Der Vertrag hat zwei Funktionen:

  • buy: Der Benutzer kann Ether senden und im Austausch Token erhalten
  • sell: Der Benutzer kann sich entscheiden, Token zu senden, um Ether zurückzuerhalten

Die Buy-Funktion

Lassen Sie uns die Buy-Funktion programmieren. Wir müssen zuerst die Menge an Ether überprüfen, die die Nachricht enthält, und sicherstellen, dass der Vertrag genügend Token besitzt und dass die Nachricht etwas Ether enthält. Wenn der Vertrag genügend Token besitzt, sendet er die Anzahl der Token an den Benutzer und löst das Bought-Ereignis aus.

Beachten Sie, dass bei einem Aufruf der require-Funktion im Fehlerfall der gesendete Ether direkt zurückgesetzt (reverted) und dem Benutzer zurückgegeben wird.

Um es einfach zu halten, tauschen wir einfach 1 Token gegen 1 Wei.

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}

Für den Fall, dass der Kauf erfolgreich ist, sollten wir zwei Ereignisse in der Transaktion sehen: Das Token-Transfer- und das Bought-Ereignis.

Zwei Ereignisse in der Transaktion: Transfer und Bought

Die Sell-Funktion

Die für den Verkauf verantwortliche Funktion setzt zunächst voraus, dass der Benutzer den Betrag genehmigt hat, indem er zuvor die approve-Funktion aufruft. Die Genehmigung der Übertragung erfordert, dass der von der DEX instanziierte ERC20Basic-Token vom Benutzer aufgerufen wird. Dies kann erreicht werden, indem zuerst die token()-Funktion des DEX-Vertrags aufgerufen wird, um die Adresse abzurufen, an der die DEX den ERC20Basic-Vertrag namens token bereitgestellt hat. Dann erstellen wir eine Instanz dieses Vertrags in unserer Sitzung und rufen seine approve-Funktion auf. Danach können wir die sell-Funktion der DEX aufrufen und unsere Token wieder gegen Ether tauschen. So sieht das zum Beispiel in einer interaktiven Brownie-Sitzung aus:

1# ### Python in der interaktiven Brownie-Konsole...
2
3# die DEX bereitstellen
4dex = DEX.deploy({'from':account1})
5
6# die buy-Funktion aufrufen, um Ether gegen Token zu tauschen
7# 1e18 ist 1 Ether, angegeben in Wei
8dex.buy({'from': account2, 1e18})
9
10# die Bereitstellungsadresse für den ERC20-Token abrufen
11# der während der Erstellung des DEX-Vertrags bereitgestellt wurde
12# dex.token() gibt die bereitgestellte Adresse für den Token zurück
13token = ERC20Basic.at(dex.token())
14
15# die approve-Funktion des Tokens aufrufen
16# die DEX-Adresse als Ausgeber genehmigen
17# und wie viele deiner Token sie ausgeben darf
18token.approve(dex.address, 3e18, {'from':account2})
19
Alle anzeigen

Wenn dann die Sell-Funktion aufgerufen wird, überprüfen wir, ob die Übertragung von der Adresse des Aufrufers zur Vertragsadresse erfolgreich war, und senden dann die Ether an die Adresse des Aufrufers zurück.

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 payable(msg.sender).transfer(amount);
7 emit Sold(amount);
8}

Wenn alles funktioniert, sollten Sie 2 Ereignisse (ein Transfer und ein Sold) in der Transaktion sehen und Ihr Token-Guthaben sowie Ihr Ether-Guthaben sollten aktualisiert sein.

Zwei Ereignisse in der Transaktion: Transfer und Sold

In diesem Tutorial haben wir gesehen, wie man das Guthaben und die Freigabe (Allowance) eines ERC-20-Tokens überprüft und wie man Transfer und TransferFrom eines ERC-20-Smart-Contracts über die Schnittstelle aufruft.

Sobald Sie eine Transaktion durchführen, haben wir ein JavaScript-Tutorial, um zu warten und Details über die Transaktionen zu erhalten (opens in a new tab), die an Ihren Vertrag gesendet wurden, sowie ein Tutorial zum Decodieren von Ereignissen, die durch Token-Übertragungen oder andere Ereignisse generiert wurden (opens in a new tab), sofern Sie über die ABI verfügen.

Hier ist der vollständige Code für das Tutorial:

1pragma solidity ^0.8.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 mapping(address => uint256) balances;
27
28 mapping(address => mapping (address => uint256)) allowed;
29
30 uint256 totalSupply_ = 10 ether;
31
32
33 constructor() {
34 balances[msg.sender] = totalSupply_;
35 }
36
37 function totalSupply() public override view returns (uint256) {
38 return totalSupply_;
39 }
40
41 function balanceOf(address tokenOwner) public override view returns (uint256) {
42 return balances[tokenOwner];
43 }
44
45 function transfer(address receiver, uint256 numTokens) public override returns (bool) {
46 require(numTokens <= balances[msg.sender]);
47 balances[msg.sender] = balances[msg.sender]-numTokens;
48 balances[receiver] = balances[receiver]+numTokens;
49 emit Transfer(msg.sender, receiver, numTokens);
50 return true;
51 }
52
53 function approve(address delegate, uint256 numTokens) public override returns (bool) {
54 allowed[msg.sender][delegate] = numTokens;
55 emit Approval(msg.sender, delegate, numTokens);
56 return true;
57 }
58
59 function allowance(address owner, address delegate) public override view returns (uint) {
60 return allowed[owner][delegate];
61 }
62
63 function transferFrom(address owner, address buyer, uint256 numTokens) public override returns (bool) {
64 require(numTokens <= balances[owner]);
65 require(numTokens <= allowed[owner][msg.sender]);
66
67 balances[owner] = balances[owner]-numTokens;
68 allowed[owner][msg.sender] = allowed[owner][msg.sender]-numTokens;
69 balances[buyer] = balances[buyer]+numTokens;
70 emit Transfer(owner, buyer, numTokens);
71 return true;
72 }
73}
74
75
76contract DEX {
77
78 event Bought(uint256 amount);
79 event Sold(uint256 amount);
80
81
82 IERC20 public token;
83
84 constructor() {
85 token = new ERC20Basic();
86 }
87
88 function buy() payable public {
89 uint256 amountTobuy = msg.value;
90 uint256 dexBalance = token.balanceOf(address(this));
91 require(amountTobuy > 0, "You need to send some ether");
92 require(amountTobuy <= dexBalance, "Not enough tokens in the reserve");
93 token.transfer(msg.sender, amountTobuy);
94 emit Bought(amountTobuy);
95 }
96
97 function sell(uint256 amount) public {
98 require(amount > 0, "You need to sell at least some tokens");
99 uint256 allowance = token.allowance(msg.sender, address(this));
100 require(allowance >= amount, "Check the token allowance");
101 token.transferFrom(msg.sender, address(this), amount);
102 payable(msg.sender).transfer(amount);
103 emit Sold(amount);
104 }
105
106}
Alle anzeigen

Letzte Aktualisierung der Seite: 21. August 2025

War dieses Tutorial hilfreich?