Transfery i zatwierdzenie tokenów ERC-20 z inteligentnego kontraktu Solidity
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;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 event Approval(address indexed tokenOwner, address indexed spender, uint tokens);27 event Transfer(address indexed from, address indexed to, uint tokens);282930 mapping(address => uint256) balances;3132 mapping(address => mapping (address => uint256)) allowed;3334 uint256 totalSupply_ = 100 ether;3536 using SafeMath for uint256;3738 constructor(uint256 total) public {39 balances[msg.sender] = totalSupply_;40 }4142 function totalSupply() public override view returns (uint256) {43 return totalSupply_;44 }4546 function balanceOf(address tokenOwner) public override view returns (uint256) {47 return balances[tokenOwner];48 }4950 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 }5758 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 }6364 function allowance(address owner, address delegate) public override view returns (uint) {65 return allowed[owner][delegate];66 }6768 function transferFrom(address owner, address buyer, uint256 numTokens) public override returns (bool) {69 require(numTokens <= balances[owner]);70 require(numTokens <= allowed[owner][msg.sender]);7172 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}7980library SafeMath {81 function sub(uint256 a, uint256 b) internal pure returns (uint256) {82 assert(b <= a);83 return a - b;84 }8586 function add(uint256 a, uint256 b) internal pure returns (uint256) {87 uint256 c = a + b;88 assert(c >= a);89 return c;90 }91}92Pokaż wszystkoKopiuj
Nasz nowy inteligentny kontrakt DEX wdroży ERC-20 i otrzyma wszystkie dostarczone:
1contract DEX {23 IERC20 public token;45 event Bought(uint256 amount);6 event Sold(uint256 amount);78 constructor() public {9 token = new ERC20Basic();10 }1112 function buy() payable public {13 // TODO14 }1516 function sell(uint256 amount) public {17 // TODO18 }1920}21Pokaż wszystkoKopiuj
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 zamiansell
: 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}9Kopiuj
Jeśli zakup zakończył się sukcesem, powinniśmy zobaczyć dwa zdarzenia w transakcji: Token Transfer
i Bought
wydarzenie.
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}9Kopiuj
Jeśli wszystko działa, powinieneś zobaczyć 2 zdarzenia w transakcji (a Transfer
i Sold
) i Twoje saldo tokenu i saldo Ethereum zaktualizowane.
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, które zostały wykonane w ramach Twojego kontraktu oraz samouczek dekodowania zdarzeń generowanych przez transfery tokenów lub inne zdarzenia, o ile masz ABI.
Oto kompletny kod do samouczka:
1pragma solidity ^0.6.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 event Approval(address indexed tokenOwner, address indexed spender, uint tokens);27 event Transfer(address indexed from, address indexed to, uint tokens);282930 mapping(address => uint256) balances;3132 mapping(address => mapping (address => uint256)) allowed;3334 uint256 totalSupply_ = 10 ether;3536 using SafeMath for uint256;3738 constructor() public {39 balances[msg.sender] = totalSupply_;40 }4142 function totalSupply() public override view returns (uint256) {43 return totalSupply_;44 }4546 function balanceOf(address tokenOwner) public override view returns (uint256) {47 return balances[tokenOwner];48 }4950 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 }5758 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 }6364 function allowance(address owner, address delegate) public override view returns (uint) {65 return allowed[owner][delegate];66 }6768 function transferFrom(address owner, address buyer, uint256 numTokens) public override returns (bool) {69 require(numTokens <= balances[owner]);70 require(numTokens <= allowed[owner][msg.sender]);7172 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}7980library SafeMath {81 function sub(uint256 a, uint256 b) internal pure returns (uint256) {82 assert(b <= a);83 return a - b;84 }8586 function add(uint256 a, uint256 b) internal pure returns (uint256) {87 uint256 c = a + b;88 assert(c >= a);89 return c;90 }91}9293contract DEX {9495 event Bought(uint256 amount);96 event Sold(uint256 amount);979899 IERC20 public token;100101 constructor() public {102 token = new ERC20Basic();103 }104105 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 }113114 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 }122123}124Pokaż wszystkoKopiuj