跳转至主要内容

通过Solidity智能合约转移和批准ERC-20通证

智能合约代币solidity入门指南erc-20 (一种以太坊代币)
中级
jdourlens
EthereumDev(opens in a new tab)
2020年4月7日
8 分钟阅读 minute read
comp-tutorial-metadata-tip-author 0x19dE91Af973F404EDF5B4c093983a7c6E3EC8ccE

在之前的教程中,我们学习了对以太坊区块链上基于 Solidity 的 ERC-20 通证的剖析。 在本文中,我们将看到如何借助 Solidity 语言使用智能合约来与通证进行交互。

对于此智能合约,我们将创建一个真正虚拟的去中心化交易所,用户可以在这里使用我们新部署的ERC-20 通证进行以太坊交易。

对于此教程,我们将使用我们在上一个教程中编写的代码作为基础。 我们的 DEX 将在它的构造函数中实例化一个合约中的实例,并进行以下操作:

  • 将通证换成 ETH
  • 把 ETH 换成通证

我们将通过添加简单的 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
显示全部
复制

我们新的 DEX 智能合约将部署 ERC-20 并生成总量供应:

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 // TODO
14 }
15
16 function sell(uint256 amount) public {
17 // TODO
18 }
19
20}
显示全部
复制

所以我们现在有了我们的 DEX,它所有的代币储备都已准备好。 合约有两个函数:

  • buy:用户可以发送 ETH 并在交易所中获得通证
  • sell:用户可以决定发送通证换回 ETH

buy 函数

让我们编写 buy 函数的代码。 我们首先需要检查消息中包含的 ETH 数量,并验证合约中是否拥有足够的通证,以及消息中是否有一些 ETH。 如果合约拥有足够的通证,它将会向用户发送通证数量,并发出Bought事件。

请注意,如果我们在出错的情况下调用所需的函数,则发送的 ETH 将会直接还原并退回给用户。

为简单起见,我们只需将 1 个代币换成 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}
9
10Text
11XPath: /pre[3]/code
12
显示全部
复制

在购买成功的情况下,我们应会在交易中看到两个事件:通证TransferBought事件。

交易中的两个事件:Transfer和Bought

sell 函数

负责卖出的函数将首先要求用户事先通过调用 approve 函数来批准金额。 要批准转账,用户需要调用由去中心化交易所 (DEX) 实例化的 ERC20Basic 代币。 为此,首先需要调用去中心化交易所 (DEX) 合约的 token() 函数来检索 DEX 部署名为 token 的 ERC20Basic 合约的地址。 然后,我们需要在会话中创建该合约的实例并调用它的 approve 函数。 接着,我们可以调用 DEX 的 sell 函数并将我们的代币换成以太币。 例如,该过程在交互式 Brownie 会话中显示如下:

1#### Python in interactive brownie console...
2
3# 部署 DEX
4dex = DEX.deploy({'from':account1})
5
6# 调用buy函数将ETH换成代币
7# 1e18 是 1 个以 wei 计价的ETH
8dex.buy({'from': account2, 1e18})
9
10# 获取 ERC20 代币的部署地址
11# 在 DEX 合约创建期间部署的
12# dex.token() 返回token的部署地址
13token = ERC20Basic.at(dex.token())
14
15# 调用token的approve函数
16# 批准dex地址为spender
17# 以及允许花费你的多少代币
18token.approve(dex.address, 3e18, {'from':account2})
19
显示全部
复制

然后当调用 sell 函数时,我们会检查从调用者地址到合约地址的转账是否成功,然后将以太币发送回调用者地址。

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}
复制

如果一切正常,您应该在交易中看到两个事件(TransferSold),并且代币余额和以太坊余额已更新。

交易中的两个事件:Transfer 和 Sold

在本教程中,我们了解到如何检查 ERC-20 代币的余额和余量,以及如何使用接口调用 ERC20 智能合约的 TransferTransferFrom 函数。

完成交易后,我们即会提供一个 JavaScript 教程来等待并获取在您的合约中进行的交易的详细信息(opens in a new tab),还会提供教程来解码由代币转账或任何其他事件生成的事件(opens in a new tab),但前提是您有应用程序二进制接口。

下面是本教程的完整代码:

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, "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 }
96
97 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 }
105
106}
显示全部
复制

上次修改时间: @cuijia(opens in a new tab), 2023年8月15日

本教程对你有帮助吗?