Solidity 练习:ERC20

2022-12-29 Web3 Solidity

# 一、题目说明

ERC20 是一个常用的 token 标准,包含 3 个重要的函数:

  • transfer:将 token 从 msg.sender 转移到另一个帐户。
  • approve:批准另一个账户来花费你的 token。
  • transferFrom:批准的账户可以代表你来转移 token。

下面是使用 approve 和 transferFrom 的常见场景。

你 approve 了一份花费你一些 token 的合约;接下来,合约调用 transferFrom 将 token 从你的账户转移到合约中。

通过执行上述两个步骤,你可以避免将 token 意外发送到错误地址的风险。

下面是 ERC20 的接口内容。

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

interface IERC20 {
    function totalSupply() external view returns (uint);

    function balanceOf(address account) external view returns (uint);

    function transfer(address recipient, uint amount) external returns (bool);

    function allowance(
        address owner,
        address spender
    ) external view returns (uint);

    function approve(address spender, uint amount) external returns (bool);

    function transferFrom(
        address sender,
        address recipient,
        uint amount
    ) external returns (bool);

    event Transfer(address indexed from, address indexed to, uint amount);
    event Approval(address indexed owner, address indexed spender, uint amount);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# 二、任务列表

# 2.1 完成 transfer 函数

function transfer(address recipient, uint amount)
    external
    override
    returns (bool)
{
    return true;
}
1
2
3
4
5
6
7

该函数将从 msg.sender 向 recipient 发送 amount 数量的 token。

  • token 余额存储在 balanceOf 中。减少 msg.sender 的余额,增加 recipient 的余额。
  • 提交 Transfer 事件。
  • 遵循 ERC20 的标准返回 true。

# 2.2 完成 approve 函数

function approve(address spender, uint amount)
    external
    override
    returns (bool)
{
    return true;
}
1
2
3
4
5
6
7

该函数批准 spender 花费 msg.sender 拥有的 amount 数量的 token。

映射 allowance[owner][spender] 存储了 owner 允许 spender 花费的 token 数量。

  • 更新 allowance 对应项为 amount。
  • 提交 Approval 事件。
  • 遵循 ERC20 的标准返回 true。

# 2.3 完成 tranferFrom 函数

function transferFrom(
    address sender,
    address recipient,
    uint amount
) external override returns (bool) {
    return true;
}
1
2
3
4
5
6
7

该函数用于将 token 从 sender 发送到 recipient。msg.sender 被批准从 sender 那里花费至少 amount 数量的 token。

  • 适当更新 allowance 和 balanceOf。
  • 从 sender 向 recipient 转移 token。
  • 提交 Transfer 事件。
  • 遵循 ERC20 的标准返回 true。

# 2.4 完成 mint 函数

function mint(uint amount) external {
    // code
}
1
2
3

该函数为 msg.sender 创建额外 amount 数量的 token。

提交 Transfer 事件(从 address(0) 到 msg.sender 的 amount 数量)。

此函数不是 ERC20 标准的一部分,但它是许多 token 中常见的函数。

通常只有授权帐户才能铸造新的 token,但是对于此练习,我们将跳过访问控制。

# 2.5 完成 burn 函数

function burn(uint amount) external {
    // code
}
1
2
3

该函数从 msg.sender 中扣除 amount 数量个 token。

提交 Transfer 事件(从 msg.sender 到 address(0) 的 amount 数量)。

此函数不是 ERC20 标准的一部分,但它是许多 token 中常见的函数。

# 三、解答代码

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import "./IERC20.sol";

contract ERC20 is IERC20 {
    uint public totalSupply = 1000;
    mapping(address => uint) public balanceOf;
    mapping(address => mapping(address => uint)) public allowance;
    string public name = "TestToken";
    string public symbol = "TEST";
    uint8 public decimals = 18;

    constructor() {
        balanceOf[msg.sender] = totalSupply;
    }

    function transfer(address recipient, uint amount) external override returns (bool) {
        require(balanceOf[msg.sender] >= amount, "ERC20: transfer amount exceeds balance");
        
        balanceOf[msg.sender] -= amount;
        balanceOf[recipient] += amount;
        
        emit Transfer(msg.sender, recipient, amount);
        
        return true;
    }

    function approve(address spender, uint amount) external override returns (bool) {
        allowance[msg.sender][spender] = amount;
        
        emit Approval(msg.sender, spender, amount);
        
        return true;
    }

    function transferFrom(
        address sender,
        address recipient,
        uint amount
    ) external returns (bool) {
        require(allowance[sender][msg.sender] >= amount, "ERC20: insufficient allowance");
        require(balanceOf[sender] >= amount, "ERC20: transfer amount exceeds balance");
        
        allowance[sender][msg.sender] -= amount;
        
        balanceOf[sender] -= amount;
        balanceOf[recipient] += amount;
        
        emit Transfer(sender, recipient, amount);
        
        return true;
    }

    function mint(uint amount) external {
        balanceOf[msg.sender] += amount;
        totalSupply += amount;
        
        emit Transfer(address(0), msg.sender, amount);
    }

    function burn(uint amount) external {
        balanceOf[msg.sender] -= amount;
        totalSupply -= amount;
        
        emit Transfer(msg.sender, address(0), amount);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68

# 四、参考资料

Last Updated: 2023-01-28 4:31:25