OpenZeppelin ERC777 源码解析

这篇文章是对ERC777 功能型代币(通证)最佳实践 的一个补充,如果你仅仅是要实现一个自己的 ERC777 代币, 那么阅读另一篇就够了, 如果想对ERC777进行一些自己的定制,那么就有需要对源码有理解。

ERC777 源码解析

本文源码来自 OpenZeppelin, GitHub 代码库地址 当前基于 OpenZeppelin 2.3.0 版本,源码文件路径为:contracts/token/ERC777/ERC777.sol

下面在源码进行注释进行解析:

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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
pragma solidity ^0.5.0;

import "./IERC777.sol";
import "./IERC777Recipient.sol";
import "./IERC777Sender.sol";
import "../../token/ERC20/IERC20.sol";
import "../../math/SafeMath.sol";
import "../../utils/Address.sol";
import "../../introspection/IERC1820Registry.sol";

// 合约实现兼容了 ERC20
contract ERC777 is IERC777, IERC20 {
using SafeMath for uint256;
using Address for address;

// ERC1820 注册表合约地址
IERC1820Registry private _erc1820 = IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24);

mapping(address => uint256) private _balances;

uint256 private _totalSupply;

string private _name;
string private _symbol;


// 发送者接口hash 硬编码 keccak256("ERC777TokensSender") 为了减少 gas, 用户查询是否实现了对应的接口。
bytes32 constant private TOKENS_SENDER_INTERFACE_HASH =
0x29ddb589b1fb5fc7cf394961c1adf5f8c6454761adf795e67fe149f658abe895;

// keccak256("ERC777TokensRecipient")
bytes32 constant private TOKENS_RECIPIENT_INTERFACE_HASH =
0xb281fc8c12954d22544db45de3159a39272895b169a852b314f9cc762e44c53b;

// 保存默认操作者列表
address[] private _defaultOperatorsArray;

// 为了索引默认操作者状态 使用的mapping
mapping(address => bool) private _defaultOperators;

// 保存授权的操作者
mapping(address => mapping(address => bool)) private _operators;
// 保存取消授权的默认操作者
mapping(address => mapping(address => bool)) private _revokedDefaultOperators;

// 为了兼容 ERC20 (授权信息)
mapping (address => mapping (address => uint256)) private _allowances;

/**
* @dev `defaultOperators` 可以为空.
*/
constructor(
string memory name,
string memory symbol,
address[] memory defaultOperators
) public {
_name = name;
_symbol = symbol;

_defaultOperatorsArray = defaultOperators;
for (uint256 i = 0; i < _defaultOperatorsArray.length; i++) {
_defaultOperators[_defaultOperatorsArray[i]] = true;
}

// 注册接口
_erc1820.setInterfaceImplementer(address(this), keccak256("ERC777Token"), address(this));
_erc1820.setInterfaceImplementer(address(this), keccak256("ERC20Token"), address(this));
}

function name() public view returns (string memory) {
return _name;
}

function symbol() public view returns (string memory) {
return _symbol;
}

// 为了兼容 ERC20
function decimals() public pure returns (uint8) {
return 18;
}

// 默认粒度为1,粒度表示代币最小的分隔单位
function granularity() public view returns (uint256) {
return 1;
}

function totalSupply() public view returns (uint256) {
return _totalSupply;
}

function balanceOf(address tokenHolder) public view returns (uint256) {
return _balances[tokenHolder];
}

// ERC777 定义的转账函数, 同时触发 ERC20的 `Transfer` 事件
function send(address recipient, uint256 amount, bytes calldata data) external {
_send(msg.sender, msg.sender, recipient, amount, data, "", true);
}

// 为兼容 ERC20,同时触发 `Sent` 事件
function transfer(address recipient, uint256 amount) external returns (bool) {
require(recipient != address(0), "ERC777: transfer to the zero address");

address from = msg.sender;

_callTokensToSend(from, from, recipient, amount, "", "");

_move(from, from, recipient, amount, "", "");

//最后一个参数表示不要求接收者实现钩子函数 `tokensReceived`
_callTokensReceived(from, from, recipient, amount, "", "", false);

return true;
}

// 为了兼容 ERC20, 触发 `Transfer` 事件
function burn(uint256 amount, bytes calldata data) external {
_burn(msg.sender, msg.sender, amount, data, "");
}

// 是否是操作员
function isOperatorFor(
address operator,
address tokenHolder
) public view returns (bool) {
return operator == tokenHolder ||
(_defaultOperators[operator] && !_revokedDefaultOperators[tokenHolder][operator]) ||
_operators[tokenHolder][operator];
}

// 授权操作员
function authorizeOperator(address operator) external {
require(msg.sender != operator, "ERC777: authorizing self as operator");

if (_defaultOperators[operator]) {
delete _revokedDefaultOperators[msg.sender][operator];
} else {
_operators[msg.sender][operator] = true;
}

emit AuthorizedOperator(operator, msg.sender);
}

// 撤销操作员
function revokeOperator(address operator) external {
require(operator != msg.sender, "ERC777: revoking self as operator");

if (_defaultOperators[operator]) {
_revokedDefaultOperators[msg.sender][operator] = true;
} else {
delete _operators[msg.sender][operator];
}

emit RevokedOperator(operator, msg.sender);
}

// 默认操作者
function defaultOperators() public view returns (address[] memory) {
return _defaultOperatorsArray;
}

// 转移代币,需要有操作者权限,触发 `Sent` 和 `Transfer` 事件
function operatorSend(
address sender,
address recipient,
uint256 amount,
bytes calldata data,
bytes calldata operatorData
)
external
{
require(isOperatorFor(msg.sender, sender), "ERC777: caller is not an operator for holder");
_send(msg.sender, sender, recipient, amount, data, operatorData, true);
}

// 销毁
function operatorBurn(address account, uint256 amount, bytes calldata data, bytes calldata operatorData) external {
require(isOperatorFor(msg.sender, account), "ERC777: caller is not an operator for holder");
_burn(msg.sender, account, amount, data, operatorData);
}

// 为了兼容 ERC20
function allowance(address holder, address spender) public view returns (uint256) {
return _allowances[holder][spender];
}

// 为了兼容 ERC20
function approve(address spender, uint256 value) external returns (bool) {
address holder = msg.sender;
_approve(holder, spender, value);
return true;
}

// 注意, 操作员没有权限调用(除非经过approve)
// 触发 `Sent` and `Transfer`事件

function transferFrom(address holder, address recipient, uint256 amount) external returns (bool) {
require(recipient != address(0), "ERC777: transfer to the zero address");
require(holder != address(0), "ERC777: transfer from the zero address");

address spender = msg.sender;

_callTokensToSend(spender, holder, recipient, amount, "", "");

_move(spender, holder, recipient, amount, "", "");
_approve(holder, spender, _allowances[holder][spender].sub(amount));

_callTokensReceived(spender, holder, recipient, amount, "", "", false);

return true;
}

// 铸币函数(即常说的挖矿)
function _mint(
address operator,
address account,
uint256 amount,
bytes memory userData,
bytes memory operatorData
)
internal
{
require(account != address(0), "ERC777: mint to the zero address");

// Update state variables
_totalSupply = _totalSupply.add(amount);
_balances[account] = _balances[account].add(amount);

_callTokensReceived(operator, address(0), account, amount, userData, operatorData, true);

emit Minted(operator, account, amount, userData, operatorData);
emit Transfer(address(0), account, amount);
}

// 转移 token
// 最后一个参数 requireReceptionAck 表示是否必须实现 ERC777TokensRecipient
function _send(
address operator,
address from,
address to,
uint256 amount,
bytes memory userData,
bytes memory operatorData,
bool requireReceptionAck
)
private
{
require(from != address(0), "ERC777: send from the zero address");
require(to != address(0), "ERC777: send to the zero address");

_callTokensToSend(operator, from, to, amount, userData, operatorData);

_move(operator, from, to, amount, userData, operatorData);

_callTokensReceived(operator, from, to, amount, userData, operatorData, requireReceptionAck);
}

// 销毁代币实现
function _burn(
address operator,
address from,
uint256 amount,
bytes memory data,
bytes memory operatorData
)
private
{
require(from != address(0), "ERC777: burn from the zero address");

_callTokensToSend(operator, from, address(0), amount, data, operatorData);

// Update state variables
_totalSupply = _totalSupply.sub(amount);
_balances[from] = _balances[from].sub(amount);

emit Burned(operator, from, amount, data, operatorData);
emit Transfer(from, address(0), amount);
}

// 转移所有权
function _move(
address operator,
address from,
address to,
uint256 amount,
bytes memory userData,
bytes memory operatorData
)
private
{
_balances[from] = _balances[from].sub(amount);
_balances[to] = _balances[to].add(amount);

emit Sent(operator, from, to, amount, userData, operatorData);
emit Transfer(from, to, amount);
}

function _approve(address holder, address spender, uint256 value) private {
// TODO: restore this require statement if this function becomes internal, or is called at a new callsite. It is
// currently unnecessary.
//require(holder != address(0), "ERC777: approve from the zero address");
require(spender != address(0), "ERC777: approve to the zero address");

_allowances[holder][spender] = value;
emit Approval(holder, spender, value);
}

// 尝试调用持有者的 tokensToSend() 函数
function _callTokensToSend(
address operator,
address from,
address to,
uint256 amount,
bytes memory userData,
bytes memory operatorData
)
private
{
address implementer = _erc1820.getInterfaceImplementer(from, TOKENS_SENDER_INTERFACE_HASH);
if (implementer != address(0)) {
IERC777Sender(implementer).tokensToSend(operator, from, to, amount, userData, operatorData);
}
}

// 尝试调用接收者的 tokensReceived()
function _callTokensReceived(
address operator,
address from,
address to,
uint256 amount,
bytes memory userData,
bytes memory operatorData,
bool requireReceptionAck
)
private
{
address implementer = _erc1820.getInterfaceImplementer(to, TOKENS_RECIPIENT_INTERFACE_HASH);
if (implementer != address(0)) {
IERC777Recipient(implementer).tokensReceived(operator, from, to, amount, userData, operatorData);
} else if (requireReceptionAck) {
require(!to.isContract(), "ERC777: token recipient contract has no implementer for ERC777TokensRecipient");
}
}
}

深入浅出区块链 - 打造高质量区块链技术博客,学区块链都来这里,关注知乎微博 掌握区块链技术动态。

LBC-Team wechat
欢迎订阅公众号:深入浅出区块链技术
0%