Solidity(四)合约标准ERC-20

ERC-20协议:是以太坊网络上的一种代币标准,2015年提出;ERC-20协议定义了【同质化】标准代币基本的逻辑和组成;

同质化标准:同质化标准代币是指在一个特定的代币标准下,每个代币都是可以相互替换的,没有区别或者差异的代币;

同质化代币的特点:

  • 可互换性
  • 可分割性
  • 可传输性
  • 余额查询
  • 代币交易

1 IERC-20接口定义介绍

IERC-20接口规定了ERC-20协议标准中的代币需要实现的函数和事件;在具体应用时,所有的ERC-20代币均具有统一的函数名称、输入参数和输出结果;

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
// author:hqd8080
// contracts/IERC20.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.2< 0.9.0;

//IERC-20接口定义
interface IERC20 {
//可选函数
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);

//必须实现函数
function totalSupply() external view returns (uint256);
function balanceOf(address _owner) external view returns (uint256 balance);
function transfer(address _to, uint256 _value) external returns (bool success);
function transferFrom(address _from, address _to, uint256 _value) external returns (bool success);
function approve(address _spender, uint256 _value) external returns (bool success);
function allowance(address _owner, address _spender) external view returns (uint256 remaining);

//转账事件
event Transfer(address indexed _from, address indexed _to, uint256 _value);
//授权事件
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
}

2 实现ERC-20接口合约程序

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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
// author:hqd8080
// contracts/ERC20.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.2< 0.9.0;

import "./IERC20.sol";
//npm install @openzeppelin/contracts@v4.8.2
import "@openzeppelin/contracts/utils/math/SafeMath.sol";

//ERC-20合约,实现IERC-20接口
contract ERC20 is IERC20 {
using SafeMath for uint256;
string public ercName;
string public ercSymbol;
uint8 public ercDecimals = 18; //小数位数;以太币为18
uint256 public ercTotalSupply;
mapping (address => uint256) public ercBalances;
mapping (address => mapping (address => uint256)) public ercAllowances;
address private owner;

//构造函数
constructor(string memory _name, string memory _symbol, uint8 _decimals) {
ercName = _name;
ercSymbol = _symbol;
ercDecimals = _decimals;
owner = msg.sender;
}

//铸造代币,新定义的函数不属于IERC-20接口
function mint(address _to, uint256 amount) external {
require(msg.sender == owner, "caller is not owner");
require(_to != address(0), "to is a zero address");
require(amount > 0, "amount must > 0");

ercBalances[_to] = ercBalances[_to].add(amount);
ercTotalSupply = ercTotalSupply.add(amount);

emit Transfer(address(0), _to, amount);
}

//销毁代币,新定义的函数不属于IERC-20接口
function burn(address _to, uint256 amount) external {
require(msg.sender == owner, "caller is not owner");
require(_to != address(0), "to is a zero address");
require(amount > 0, "amount must > 0");

ercBalances[_to] = ercBalances[_to].sub(amount);
ercTotalSupply = ercTotalSupply.sub(amount);

emit Transfer(_to, address(0), amount);
}

function name() override external view returns (string memory) {
return ercName;
}

function symbol() override external view returns (string memory) {
return ercSymbol;
}

function decimals() override external view returns (uint8) {
return ercDecimals;
}

//发行总量
function totalSupply() override external view returns (uint256) {
return ercTotalSupply;
}

//账户余额
function balanceOf(address _owner) override external view returns (uint256 balance) {
return ercBalances[_owner];
}

//转账逻辑
function transfer(address _to, uint256 _value) override external returns (bool success) {
require(_value > 0, "_value must > 0");
require(_to != address(0), "to is zero address");
require(ercBalances[msg.sender] >= _value, "user balance not enough");

ercBalances[msg.sender] = ercBalances[msg.sender].sub(_value);
ercBalances[_to] = ercBalances[_to].add(_value);

emit Transfer(msg.sender, _to, _value);
return true;
}

//针对授权进行转账
function transferFrom(address _from, address _to, uint256 _value) override external returns (bool success) {
require(ercBalances[_from] >= _value, "user balance not enough");
require(ercAllowances[msg.sender][_from] >= _value, "approve balance not enough");
require(_value > 0, "_value must > 0");
require(_to != address(0), "_to is zero address");

ercBalances[_from] = ercBalances[_from].sub(_value);
ercBalances[_to] = ercBalances[_to].add(_value);
ercAllowances[_from][msg.sender] = ercAllowances[_from][msg.sender].sub(_value);

emit Transfer(_from, _to, _value);
return true;
}

//授权函数
function approve(address _spender, uint256 _value) override external returns (bool success) {
require(_value > 0, "_value must > 0");
require(_spender != address(0), "_spender is zero address");
require(ercBalances[msg.sender] >= _value, "user balance not enough");

ercAllowances[msg.sender][_spender] = _value;

emit Approval(msg.sender, _spender, _value);
return true;
}

//owner授权给spender余额
function allowance(address _owner, address _spender) override external view returns (uint256 remaining) {
return ercAllowances[_owner][_spender];
}
}

3 使用OpenZeppelin库实现代币合约

OpenZeppelin库根据IERC-20接口也实现了ERC-20合约逻辑,借助OpenZeppelin库合约程序开发自己的代币合约,只需继承该库合约即可;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// author:hqd8080
// contracts/MyToken.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.2< 0.9.0;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

//自定义MyToken合约,继承openzeppelin库的ERC20合约
contract MyToken is ERC20 {

//构造函数
constructor(string memory name, string memory symbol) ERC20(name, symbol) {
//铸造100枚自定义代币,** 为指定代币数量
_mint(msg.sender, 100 * 10 ** uint256(decimals()));
}
}

上面MyToken合约继承了openzeppelin库的ERC-20合约,并编写了初始化合约的构造函数,大大提高了代币的发布效率;

还可以根据实际的业务需求编写其他业务逻辑实现;