ERC-20-Token aus einem Solidity-Smart Contract übertragen und genehmigen
Im vorherigen Tutorial haben wir uns die Anatomy eines ERC-20-Tokens in Solidity auf der Ethereum-Blockchain angesehen. In diesem Beitrag sehen wir uns an, wie wir einen Smart Contract verwenden können, um mit einem Token unter Verwendung der Solidity-Sprache zu interagieren.
Für diesen Smart Contract erstellen wir eine echte Dummy-Börse, auf der ein Nutzer Ethereum mit unserem neu eingeführten ERC-20-Token handeln kann.
In diesem Tutorial werden wir den Code verwenden, den wir in den vorherigen Tutorials als Grundlage geschrieben haben. Unser DEX wird eine Instanz des Vertrags in seinem Konstruktor instanziieren und die folgenden Operationen durchführen:
- Umtausch von Token zu Ether
- Umtausch von Ether zu Token
Wir beginnen für den Code für den dezentralisierten Austausch damit, unsere einfache ERC20-Codebasis hinzufügen:
1pragma solidity ^0.8.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 mapping(address => uint256) balances;2728 mapping(address => mapping (address => uint256)) allowed;2930 uint256 totalSupply_ = 10 ether;313233 constructor() {34 balances[msg.sender] = totalSupply_;35 }3637 function totalSupply() public override view returns (uint256) {38 return totalSupply_;39 }4041 function balanceOf(address tokenOwner) public override view returns (uint256) {42 return balances[tokenOwner];43 }4445 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 }5253 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 }5859 function allowance(address owner, address delegate) public override view returns (uint) {60 return allowed[owner][delegate];61 }6263 function transferFrom(address owner, address buyer, uint256 numTokens) public override returns (bool) {64 require(numTokens <= balances[owner]);65 require(numTokens <= allowed[owner][msg.sender]);6667 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}7475Alles anzeigenKopieren
Unser neuer DEX-Smart Contract stellt den ERC-20 bereit und erhält folgende Komponenten:
1contract DEX {23 IERC20 public token;45 event Bought(uint256 amount);6 event Sold(uint256 amount);78 constructor() {9 token = new ERC20Basic();10 }1112 function buy() payable public {13 // TODO14 }1516 function sell(uint256 amount) public {17 // TODO18 }1920}Alles anzeigenKopieren
Wir haben jetzt also unseren DEX. Dieser verfügt über die gesamte Token-Reserve. Der Vertrag hat zwei Funktionen:
buy
: Der Benutzer kann Ether senden und erhält dafür Token.sell
: Der Benutzer kann entscheiden, Token zu senden, um Ether zurückzubekommen.
Die Kauffunktion
Programmieren wir nun die Kauffunktion. Als Erstes müssen wir die Menge an Ether in der Nachricht überprüfen und sicherstellen, dass die Verträge genügend Token besitzen. Enthält der Vertrag genügend Token, sendet er die Anzahl an Token an den Benutzer und gibt als Ereignis Gekauft
aus.
Beachten Sie, dass beim Aufruf der Require-Funktion im Falle eines Fehlers der Versand rückgängig gemacht und der Ether wieder zurück an den Benutzer gesendet wird.
Um die Dinge 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}Kopieren
Im Falle eines erfolgreichen Kaufs sollten zwei Ereignisse in der Transaktion angezeigt werden: das Tokenereignis Transfer
und Bought
.
Die Verkaufsfunktion
Die Funktion, die für den Verkauf zuständig ist, setzt voraus, dass der Benutzer den Betrag zuvor durch die Abfrage der Funktion genehmigt hat. Zur Genehmigung der Überweisung muss der vom DEX instanziierte ERC20Basic-Token vom Nutzer aufgerufen werden. Das lässt sich erreichen, indem zuerst die token()-
Funktion des DEX-Vertrags aufgerufen wird, um die Adresse abzurufen, an der DEX den ERC20Basic-Vertrag namens Token
bereitgestellt hat. Dann erstellen Sie eine Instanz dieses Vertrags in der Sitzung und rufen seine approve
-Funktion auf. Dann können wir die sell
-Funktion des DEX aufrufen und unsere Token gegen Ether zurücktauschen. So sieht das zum Beispiel bei einer interaktiven Brownie-Sitzung aus:
1#### Python in interactive brownie console...23# DEX einsetzen4dex = DEX.deploy({'from':account1})56# Aufruf der Kauffunktion zum Tausch von Ether gegen Token7# 1e18 ist 1 Ether, der auf Wei lautet8dex.buy({'from': account2, 1e18})910# Ermitteln der Bereitstellungsadresse für den ERC20-Token11# der während der DEX-Vertragserstellung bereitgestellt wurde12# dex.token() liefert die Deployment-Adresse für den Token13token = ERC20Basic.at(dex.token())1415# Aufruf der Approve-Funktion für den Token16# die dex-Adresse als Spender genehmigen17# und wie viele Ihrer Token sie ausgeben darf18token.approve(dex.address, 3e18, {'from':account2})19Alles anzeigenKopieren
Wenn dann die Verkaufsfunktion aufgerufen wird, prüfen wir, ob die Übertragung von der Adresse des Abfragenden an die Adresse des Vertragspartners erfolgreich war, und senden dann die Ether zurück an die Adresse des Abfragenden.
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}Kopieren
Wenn alles funktioniert, sollten Sie zwei Ereignisse (Transfer
und Sold
) in der Transaktion sehen und dass Ihr Tokenguthaben und Ethereum-Guthaben aktualisiert wurden.
In diesem Tutorial haben wir gesehen, wie Sie den Kontostand und das Guthaben eines ERC-20-Tokens überprüfen und wie Sie Transfer
und TransferFrom
eines ERC20-Smart-Contracts über die Schnittstelle abrufen.
Sobald Sie eine Transaktion durchführen, haben wir ein JavaScript-Tutorial, um zu warten und Details über die Transaktionen(opens in a new tab) zu erhalten, die mit Ihrem Vertrag durchgeführt wurden, und ein Tutorial, um Ereignisse zu dekodieren, die durch Tokenübertragungen oder andere Ereignisse(opens in a new tab) erzeugt werden, solange Sie die ABI haben.
Hier finden Sie den vollständigen Code für das Tutorial:
1pragma solidity ^0.8.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 mapping(address => uint256) balances;2728 mapping(address => mapping (address => uint256)) allowed;2930 uint256 totalSupply_ = 10 ether;313233 constructor() {34 balances[msg.sender] = totalSupply_;35 }3637 function totalSupply() public override view returns (uint256) {38 return totalSupply_;39 }4041 function balanceOf(address tokenOwner) public override view returns (uint256) {42 return balances[tokenOwner];43 }4445 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 }5253 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 }5859 function allowance(address owner, address delegate) public override view returns (uint) {60 return allowed[owner][delegate];61 }6263 function transferFrom(address owner, address buyer, uint256 numTokens) public override returns (bool) {64 require(numTokens <= balances[owner]);65 require(numTokens <= allowed[owner][msg.sender]);6667 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}747576contract DEX {7778 event Bought(uint256 amount);79 event Sold(uint256 amount);808182 IERC20 public token;8384 constructor() {85 token = new ERC20Basic();86 }8788 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 }9697 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 }105106}Alles anzeigenKopieren
Letzte Änderung: @nhsz(opens in a new tab), 15. August 2023