Chuyển khoản và phê duyệt token ERC-20 từ một hợp đồng thông minh Solidity
Trong hướng dẫn trước, chúng ta đã nghiên cứu cấu trúc của một token ERC-20 trong Solidity trên chuỗi khối Ethereum. Trong bài viết này, chúng ta sẽ xem cách có thể sử dụng hợp đồng thông minh để tương tác với token bằng ngôn ngữ Solidity.
Đối với hợp đồng thông minh này, chúng tôi sẽ tạo một sàn giao dịch phi tập trung mẫu, nơi người dùng có thể giao dịch ether để lấy token ERC-20 mới được triển khai của chúng tôi.
Đối với hướng dẫn này, chúng tôi sẽ sử dụng mã mà chúng tôi đã viết trong hướng dẫn trước làm cơ sở. Sàn giao dịch phi tập trung (DEX) của chúng tôi sẽ khởi tạo một bản sao của hợp đồng trong hàm khởi tạo và thực hiện các hoạt động sau:
- đổi token lấy ether
- đổi ether lấy token
Chúng tôi sẽ bắt đầu mã sàn giao dịch phi tập trung của mình bằng cách thêm cơ sở mã ERC20 đơn giản của chúng tôi:
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}7475Hiện tất cảHợp đồng thông minh của sàn giao dịch phi tập trung (DEX) mới của chúng tôi sẽ triển khai ERC-20 và nhận tất cả nguồn cung:
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}Hiện tất cảVì vậy, bây giờ chúng tôi đã có sàn giao dịch phi tập trung (DEX) của mình và nó có sẵn tất cả lượng token dự trữ. Hợp đồng có hai chức năng:
buy: Người dùng có thể gửi ether và nhận lại tokensell: Người dùng có thể quyết định gửi token để nhận lại ether
Hàm mua
Hãy lập trình hàm mua. Trước tiên, chúng tôi sẽ cần kiểm tra lượng ether chứa trong thông điệp và xác minh rằng hợp đồng sở hữu đủ token và thông điệp có chứa một lượng ether. Nếu hợp đồng sở hữu đủ token, nó sẽ gửi số lượng token cho người dùng và phát ra sự kiện Bought.
Lưu ý rằng nếu chúng tôi gọi hàm require trong trường hợp có lỗi, ether đã gửi sẽ được hoàn lại ngay lập tức và trả lại cho người dùng.
Để đơn giản hóa, chúng tôi chỉ đổi 1 token lấy 1 Wei.
1function buy() payable public {2 uint256 amountTobuy = msg.value;3 uint256 dexBalance = token.balanceOf(address(this));4 require(amountTobuy > 0, "Bạn cần gửi một ít ether");5 require(amountTobuy <= dexBalance, "Không đủ token trong kho dự trữ");6 token.transfer(msg.sender, amountTobuy);7 emit Bought(amountTobuy);8}Trong trường hợp mua thành công, chúng ta sẽ thấy hai sự kiện trong giao dịch: Sự kiện Transfer token và sự kiện Bought.
Hàm bán
Hàm chịu trách nhiệm bán trước tiên sẽ yêu cầu người dùng phải phê duyệt số tiền bằng cách gọi hàm phê duyệt trước đó. Việc phê duyệt chuyển khoản yêu cầu người dùng phải gọi token ERC20Basic được khởi tạo bởi sàn giao dịch phi tập trung (DEX). Điều này có thể được thực hiện bằng cách trước tiên gọi hàm token() của hợp đồng DEX để truy xuất địa chỉ nơi DEX đã triển khai hợp đồng ERC20Basic có tên là token. Sau đó, chúng tôi tạo một bản sao của hợp đồng đó trong phiên của mình và gọi hàm approve của nó. Sau đó, chúng ta có thể gọi hàm sell của DEX và hoán đổi token của mình để lấy lại ether. Ví dụ, đây là giao diện của nó trong một phiên brownie tương tác:
1#### Python trong bảng điều khiển brownie tương tác...23# triển khai DEX4dex = DEX.deploy({'from':account1})56# gọi hàm mua để hoán đổi ether lấy token7# 1e18 là 1 ether được tính bằng wei8dex.buy({'from': account2, 1e18})910# lấy địa chỉ triển khai cho token ERC2011# đã được triển khai trong quá trình tạo hợp đồng DEX12# dex.token() trả về địa chỉ đã triển khai cho token13token = ERC20Basic.at(dex.token())1415# gọi hàm phê duyệt của token16# phê duyệt địa chỉ dex là người chi tiêu17# và số lượng token của bạn được phép chi tiêu18token.approve(dex.address, 3e18, {'from':account2})19Hiện tất cảSau đó, khi hàm bán được gọi, chúng tôi sẽ kiểm tra xem việc chuyển từ địa chỉ của người gọi đến địa chỉ hợp đồng có thành công không và sau đó gửi Ethers trở lại địa chỉ của người gọi.
1function sell(uint256 amount) public {2 require(amount > 0, "Bạn cần bán ít nhất một số token");3 uint256 allowance = token.allowance(msg.sender, address(this));4 require(allowance >= amount, "Kiểm tra khoản phụ cấp token");5 token.transferFrom(msg.sender, address(this), amount);6 payable(msg.sender).transfer(amount);7 emit Sold(amount);8}Nếu mọi thứ hoạt động, bạn sẽ thấy 2 sự kiện (một Transfer và một Sold) trong giao dịch và số dư token cũng như số dư ether của bạn được cập nhật.
Từ hướng dẫn này, chúng ta đã thấy cách kiểm tra số dư và khoản phụ cấp của một token ERC-20, cũng như cách gọi Transfer và TransferFrom của một hợp đồng thông minh ERC20 bằng giao diện.
Khi bạn thực hiện một giao dịch, chúng tôi có một hướng dẫn JavaScript để chờ và lấy chi tiết về các giao dịch (opens in a new tab) đã được thực hiện đối với hợp đồng của bạn và một hướng dẫn để giải mã các sự kiện được tạo ra bởi việc chuyển khoản token hoặc bất kỳ sự kiện nào khác (opens in a new tab) miễn là bạn có Giao diện nhị phân ứng dụng (ABI).
Đây là mã hoàn chỉnh cho hướng dẫn:
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, "Bạn cần gửi một ít ether");92 require(amountTobuy <= dexBalance, "Không đủ token trong kho dự trữ");93 token.transfer(msg.sender, amountTobuy);94 emit Bought(amountTobuy);95 }9697 function sell(uint256 amount) public {98 require(amount > 0, "Bạn cần bán ít nhất một số token");99 uint256 allowance = token.allowance(msg.sender, address(this));100 require(allowance >= amount, "Kiểm tra khoản phụ cấp token");101 token.transferFrom(msg.sender, address(this), amount);102 payable(msg.sender).transfer(amount);103 emit Sold(amount);104 }105106}Hiện tất cảLần cập nhật trang lần cuối: 21 tháng 8, 2025

