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;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}7475Zobrazit všeNáš nový chytrý kontrakt DEX nasadí ERC-20 a získá všechny dodané:
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}Zobrazit všeTakž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...23# nasadit DEX4dex = DEX.deploy({'from':account1})56# zavolat funkci buy pro směnu etheru za token7# 1e18 je 1 ether vyjádřený ve wei8dex.buy({'from': account2, 1e18})910# 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())1415# 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})19Zobrazit všePoté, 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;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, "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 }9697 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 }105106}Zobrazit všeStránka naposledy aktualizována: 21. srpna 2025

