以太坊标准代币系列之同质化代币ERC20

0x00 写在前面

众所周知,以太坊在现阶段最大的应用就是代币发行,而在以太坊中有很多类型的代币,最著名的当属ERC20了,但是对于其他几种代币类型,可能还有一些朋友不知道,所以最近规划了一个系列,就是以太坊标准代币系列。

目前市面上,凡是基于以太坊的代币,在交易所上线交易的均是ERC20代币,那么今天我们就来聊聊ERC20代币的标准方案吧。

0x01 ERC20标准制定动机

在基于以太坊发行代币时,如果各个代币发行方都使用自有标准去发行代币,那对于钱包开发者以及交易所对接成本是极其高昂了,因为他们需要为每一种代币去独立进行对接,为了降低钱包开发者以及交易所的对接成本,以太坊社区制定了一个关于代币的发行标准。

该标准中主要包含了,代币的转移,地址余额的获取等方法。

0x02 ERC20标准规范

一个代币的合约,一般需要代币的发行量,每个地址的余额,代币的转移等方法, 而ERC20标准就是将这些最常用又是必不可少的方法,对此进行标准化,方便开发者进行代币合约的开发,也方便钱包开发者以及交易所对接成本降到最低。

其中,定义了三类,方法、属性、事件。

下面介绍这些标准方法:

A. 方法(Method)

1、totalSupply:代币总量

函数原型
1
function totalSupply() constant returns (uint256 totalSupply)

方法 该方法用于获取代币总发行量

2、balanceOf:获取余额
函数原型
1
function balanceOf(address _owner) constant returns (uint256 balance)

方法 该方法用于获取地址 _owner 的代币余额

3、transfer:转账

函数原型
1
function transfer(address _to, uint256 _value) returns (bool success)

方法 该方法用于将调用代币合约的地址中的_value个代币转给_to 地址

4、transferFrom:转账

函数原型
1
function transferFrom(address _from, address _to, uint256 _value) returns (bool success)

方法 该方法用于从_from 地址中发送_value 个代币给_to地址。

当你希望能够使用其他智能合约控制你的代币转移,则可以使用transferFrom方法,而使用transferFrom方法有一个前提条件,那就是需要调用者需要得到 _from 地址的授权,才可以进行代币转移的操作。而如何进行授权,我们接下来会介绍。

5、approve:授权

函数原型
1
function approve(address _spender, uint256 _value) returns (bool success)

方法 允许 _spender 地址从你的账户中转移 _value 个代币到任何地方。

当你设置了一个 _value 之后,_spender 地址可以分任何多次将代币进行转移,直至_value为0.

6、allowance:获取被授权代币余额

函数原型
1
function allowance(address _owner, address _spender) constant returns (uint256 remaining)

方法 获取 _owner 地址授权给 _spender 地址可以转移的代币的余额。

B. 事件(Event)

事件是EVM内置的日志功能,而且在DAPP中,我们可以通过JS来监听事件的回调。在ERC-20代币中,定义了以下事件:

1、Transfer:转移代币
事件定义
1
event Transfer(address indexed _from, address indexed _to, uint256 _value)

当进行代币转移时,需要触发调用该事件。其中记录了代币发送者_from,代币接受者_to ,代币发送量_value.

2、Approval:授权事件
事件定义
1
event Approval(address indexed _owner, address indexed _spender, uint256 _value)

当进行授权时,需要触发调用该事件,其中记录了授权者_owner,被授权者_spender,授权代币量_value

0x03 ERC20代币接口

通过以上的标准,我们可以了解到一个遵守ERC20代币标准的代币合约需要实现的方法,以下是通过规范实现的一个ERC20代币的接口。

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
contract EIP20Interface {

///////////////////////////// 方法 ///////////////////////////////////

// 获取代币发行量
// 注意:constant的修饰符是提示该方法中不能进行变量的修改。
// 但编译器不会强制校验。
// @return uint256 totalSupply 总发行量
function totalSupply() constant returns (uint256 totalSupply)

// 获取指定地址
// @param address _owner 想要获取的地址
// @return uint256 balance 代币余额
function balanceOf(address _owner) public view returns (uint256 balance);

// 从`msg.sender`中发送`_value`个代币给`_to`
// @param address _to 接收代币的地址
// @param uint256 _value 发送的代币数量
// @return bool success 发送代币成功状态
function transfer(address _to, uint256 _value) public returns (bool success);

// 从`_from`地址发送`_value`代币到`_to`地址
// 需要满足条件,需要被`_from`地址授权给`msg.sender`
// @param address _from 发送者地址
// @param address _to 接收者地址
// @param uint256 _value 发送的代币数量
// @return bool success 发送代币成功状态
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success);

// `msg.sender`授权`_spender`地址可以任意转移`_value`数量的代币
// @param address _spender 被授权发送代币的地址
// @param uint256 _value 授权发送代币的数量
// @return bool success 授权成功状态
function approve(address _spender, uint256 _value) public returns (bool success);

/// @param _owner The address of the account owning tokens
/// @param _spender The address of the account able to transfer the tokens
/// @return Amount of remaining tokens allowed to spent
// 获取被授权限额
// @param address _owner 代币所有者的账户
// @param address _spender 代币被授权者账户
// @return uint256 remaining 剩余可发送的代币数量
function allowance(address _owner, address _spender) public view returns (uint256 remaining);

///////////////////////////// 事件 ///////////////////////////////////
// 代币转移事件 当发生代币的转移时,需要调用该事件
event Transfer(address indexed _from, address indexed _to, uint256 _value);
// 授权转移事件 当进行授权时,需要触发该事件
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
}

0x04 ERC20代码示例

在标准的基础上,有很多代码的实现方法,比如有的在代码中实现空投,有的在实现锁定,有的实现挖矿等等,但是最常规的实现已经有了官方的实现代码,也有很多组织实现了一些范例,如果没有定制化的地方,完全可以直接采用这些代码去创建ERC20代币。

示例代码:

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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
pragma solidity ^0.4.18;

contract ERC20 {

// 定义一个mapping 存储各个地址的代币
mapping (address => uint256) public balances;
// 定义一个mapping 存储授权详情
mapping (address => mapping (address => uint256)) public allowed;

// 以下参数非必须,但是尽量添加,大多数钱包会通过此获取相关信息

// 代币发行总量
uint256 public totalSupply;

// 代币名称,如 OmiseGO
string public name;
// 支持的小数位数
// 因为在EVM中对浮点数的支持很差,在代币的创建中直接采用整数
// 然后通过该字段进行小数的处理
uint8 public decimals;
// 代币简称,如 OMG
string public symbol;

// 代币合约的构造函数
// 在solidity中,和合约名称一致的方法为构造函数,在第一次创建合约时,仅执行一次。
function ERC20(
uint256 _totalSupply, // 代币创建总量
string _name, // 代币名称
uint8 _decimals, // 支持小数位数
string _symbol // 代币简称
) public {
// 给创建者账户初始化代币
balances[msg.sender] = _totalSupply;
// 设置代币发行量
totalSupply = _totalSupply;
// 设置代币名称
name = _name;
// 设置代币支持小数位数
decimals = _decimals;
// 设置代币简称
symbol = _symbol;
}

/**
* 获取代币总发行量
* @return uint256 totalSupply 发行总量
*/
function totalSupply() constant returns (uint256 totalSupply) {
return totalSupply;
}

/**
* 转移代币
* @param address _to 代币接收者地址
* @param uint256 _value 发送代币数量
* @return bool success 发送成功状态
*/
function transfer(address _to, uint256 _value) public returns (bool success) {
// 判断发送者余额是否充足
require(balances[msg.sender] >= _value);
// 从发送者余额中减去`_value`数量的代币
balances[msg.sender] -= _value;
// 给接收者账户中添加`_value`数量的代币
balances[_to] += _value;
// 记录代币转移的事件
Transfer(msg.sender, _to, _value);
return true;
}

/**
* 转移代币(授权)
* @param address _from 代币发送者地址
* @param address _to 代币接收者地址
* @param uint256 _value 代币发送数量
* @return bool success 方法执行状态
*/
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {

// 其中`msg.sender`是合约方法调用者的地址

// 获取`_from`地址授权给`msg.sender`地址的可转移代币余额
uint256 allowance = allowed[_from][msg.sender];

// 判断`_from`地址余额是否充足以及授权转移代币是否充足
require(balances[_from] >= _value && allowance >= _value);

// 从`_from`地址减去`_value`数量的代币
balances[_from] -= _value;

// 从授权余额中减去`_value`数量的代币
allowed[_from][msg.sender] -= _value;

// 给`_to`地址添加`_value`数量的代币
balances[_to] += _value;

// 记录转移代币的事件
Transfer(_from, _to, _value);
return true;
}

/**
* 获取`_owner`地址的代币余额
* @param address _owner 要获取余额的地址
* @return uint256 balance 返回`_owner`地址的余额
*/
function balanceOf(address _owner) public view returns (uint256 balance) {
return balances[_owner];
}

/**
* 授权代币转移方法
* @param address _spender 被授权地址
* @param uint256 _value 授权可转移的数量
* @return bool success 方法执行状态
*/
function approve(address _spender, uint256 _value) public returns (bool success) {
// `msg.sender`授权`_spender`地址转移`_value`数量的代币
allowed[msg.sender][_spender] = _value;

// 记录授权事件
Approval(msg.sender, _spender, _value);
return true;
}

/**
* 获取被授权的代币余额
* @param address _owner 授权地址
* @param address _spender 被授权地址
* @return uint256 remaining 可转移的代币余额
*/
function allowance(address _owner, address _spender) public view returns (uint256 remaining) {
return allowed[_owner][_spender];
}
}

0x05 写在后面

大多数在交易所交易的代币的合约代码就这么简单,其实每一个方法拆分开来都是最简单的编程代码,而核心的处理都被EVM进行了封装,以太坊在代币发行方面确实极大的解放了人类,简单几十行代码就可以发行一个代币。ERC20代币又被成为同质化代币,就是每个代币都是一致的,无法区分,而市场上现在冒出了很多以太猫,以太狗的游戏,而这里面也是使用以太坊的代币来实现的,但是他们选择的不是ERC20代币,而是被成为非同质化的代币,被称为ERC721代币。

下期,我们一起来聊非同质化代币ERC721。

如果喜欢,别说话,扫我~

qrcode_for_gh_0f12fe5ef5fd_258.jpg