Převody a schválení tokenů ERC-20 z chytrého kontraktu v Solidity
V předchozím tutoriálu jsme prostudovali anatomii tokenu ERC-20 v Solidity na blockchainu Etherea. V tomto článku si ukážeme, jak můžeme použít chytrý kontrakt k interakci s tokenem pomocí jazyka Solidity.
Pro tento chytrý kontrakt vytvoříme skutečnou zkušební decentralizovanou burzu, kde může uživatel směňovat ether za náš nově nasazený token ERC-20.
Pro tento tutoriál použijeme kód, který jsme napsali v předchozím tutoriálu, jako základ. Naše DEX vytvoří instanci kontraktu ve svém konstruktoru a provede následující operace:
- směna tokenů za ether
- směna etheru za tokeny
Začneme s kódem naší decentralizované burzy přidáním naší jednoduché kódové základny ERC20:
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
Náš nový chytrý kontrakt DEX nasadí ERC-20 a získá všechny dodané:
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 // TODO14 }15
16 function sell(uint256 amount) public {17 // TODO18 }19
20}Takže nyní máme naši DEX a ta má k dispozici celou rezervu tokenů. Kontrakt má dvě funkce:
buy: Uživatel může poslat ether a na oplátku dostat tokenysell: Uživatel se může rozhodnout poslat tokeny, aby dostal zpět ether
Funkce buy
Naprogramujme funkci buy. Nejprve budeme muset zkontrolovat množství etheru, které zpráva obsahuje, a ověřit, že kontrakt vlastní dostatek tokenů a že zpráva obsahuje nějaký ether. Pokud kontrakt vlastní dostatek tokenů, pošle počet tokenů uživateli a vyšle událost Bought.
Všimněte si, že pokud v případě chyby zavoláme funkci require, zaslaný ether bude přímo vrácen a navrácen uživateli.
Pro zjednodušení směníme 1 token za 1 wei.
1function buy() payable public {2 uint256 amountTobuy = msg.value;3 uint256 dexBalance = token.balanceOf(address(this));4 require(amountTobuy > 0, "Musíte poslat nějaký ether");5 require(amountTobuy <= dexBalance, "Nedostatek tokenů v rezervě");6 token.transfer(msg.sender, amountTobuy);7 emit Bought(amountTobuy);8}V případě, že je nákup úspěšný, měli bychom v transakci vidět dvě události: událost Transfer tokenu a událost Bought.
Funkce sell
Funkce zodpovědná za prodej bude nejprve vyžadovat, aby uživatel schválil částku tím, že předem zavolá funkci approve. Schválení převodu vyžaduje, aby uživatel zavolal token ERC20Basic, který byl vytvořen DEXem. Toho lze dosáhnout tím, že nejprve zavoláte funkci token() kontraktu DEX, abyste získali adresu, kam DEX nasadila kontrakt ERC20Basic nazvaný token. Poté v naší relaci vytvoříme instanci tohoto kontraktu a zavoláme jeho funkci approve. Poté jsme schopni zavolat funkci sell DEXu a směnit naše tokeny zpět za ether. Například takhle to vypadá v interaktivní relaci brownie:
1#### Python v interaktivní konzoli brownie...2
3# nasadit DEX4dex = DEX.deploy({'from':account1})5
6# zavolat funkci buy pro směnu etheru za token7# 1e18 je 1 ether vyjádřený ve wei8dex.buy({'from': account2, 1e18})9
10# získat adresu nasazení pro token ERC2011# který byl nasazen při vytváření kontraktu DEX12# dex.token() vrátí adresu nasazeného tokenu13token = ERC20Basic.at(dex.token())14
15# zavolat funkci approve tokenu16# schválit adresu dexu jako utrácejícího17# a kolik vašich tokenů smí utratit18token.approve(dex.address, 3e18, {'from':account2})19
Poté, když je zavolána funkce sell, zkontrolujeme, zda byl převod z adresy volajícího na adresu kontraktu úspěšný, a poté pošleme ether zpět na adresu volajícího.
1function sell(uint256 amount) public {2 require(amount > 0, "Musíte prodat alespoň nějaké tokeny");3 uint256 allowance = token.allowance(msg.sender, address(this));4 require(allowance >= amount, "Zkontrolujte povolenou částku tokenu");5 token.transferFrom(msg.sender, address(this), amount);6 payable(msg.sender).transfer(amount);7 emit Sold(amount);8}Pokud vše funguje, měli byste v transakci vidět 2 události (Transfer a Sold) a váš zůstatek tokenů a etheru by měl být aktualizován.
V tomto tutoriálu jsme viděli, jak zkontrolovat zůstatek a povolenou částku tokenu ERC-20 a také jak volat Transfer a TransferFrom chytrého kontraktu ERC20 pomocí rozhraní.
Jakmile provedete transakci, máme JavaScriptový tutoriál, jak počkat a získat podrobnosti o transakcích (opens in a new tab), které byly provedeny na vašem kontraktu, a tutoriál pro dekódování událostí generovaných převody tokenů nebo jinými událostmi (opens in a new tab), pokud máte ABI.
Zde je kompletní kód k tutoriálu:
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, "Musíte poslat nějaký ether");92 require(amountTobuy <= dexBalance, "Nedostatek tokenů v rezervě");93 token.transfer(msg.sender, amountTobuy);94 emit Bought(amountTobuy);95 }96
97 function sell(uint256 amount) public {98 require(amount > 0, "Musíte prodat alespoň nějaké tokeny");99 uint256 allowance = token.allowance(msg.sender, address(this));100 require(allowance >= amount, "Zkontrolujte povolenou částku tokenu");101 token.transferFrom(msg.sender, address(this), amount);102 payable(msg.sender).transfer(amount);103 emit Sold(amount);104 }105
106}Stránka naposledy aktualizována: 3. března 2026

