forked from lim960/Eth
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathERC20_USDT智能合约讲解.js
418 lines (346 loc) · 14.6 KB
/
ERC20_USDT智能合约讲解.js
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
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
智能合约是用solidity写的,和JS很像,下面用通俗易懂的话来讲一下这个合约
usdt智能合约的原代码
1.[区块链浏览器usdt智能合约(大陆被墙)](https://etherscan.io/address/0xdac17f958d2ee523a2206206994597c13d831ec7#code)
2.[usdt智能合约(看不了1的看这里)](https://github.com/lim960/Eth/blob/master/ERC20_USDT%E6%99%BA%E8%83%BD%E5%90%88%E7%BA%A6/solidity.js)
**开始讲解**
```javascript
pragma solidity ^0.4.17;
//首先写了个工具类,提供加减乘除方法,方法中加入断言,使得调用时可以大胆调用,例如减法a-b无需判断
// a >= b是否成立,如果不成立这里就会抛异常
library SafeMath {
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
assert(c / a == b);
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// assert(b > 0); // Solidity automatically throws when dividing by 0
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
assert(b <= a);
return a - b;
}
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
assert(c >= a);
return c;
}
}
//定义一个Ownable合约
contract Ownable {
//定义一个合约拥有者地址变量
address public owner;
//构造方法 只会在部署合约时执行 初始化合约拥有者地址
//构造方法有两种写法 等同于:
// constructor() public {
// owner = msg.sender;
// }
function Ownable() public {
owner = msg.sender;
}
//函数修改器 和断言有异曲同工之处 不过这个是用作权限验证
//人话:把它当做拦截器,只有这里成立了函数才能执行
modifier onlyOwner() {
//只有拥有者才回满足修改器的条件
require(msg.sender == owner);
_;
}
//一个转移合约拥有者的函数 后面public onlyOwner 表示使用onlyOwner这个修改器
//满足修改器的条件,才可以执行函数内的代码
function transferOwnership(address newOwner) public onlyOwner {
if (newOwner != address(0)) {
owner = newOwner;
}
}
}
//https://eips.ethereum.org/EIPS/eip-20这里可以先看一下
//erc20协议提供的基本核心函数
//event函数
//合约代币的交易不过就是合约上的变量变化而已,后面看了mapping就懂了
//合约变量变化后没人知道,那外界就没办法监听数据,所以处理完要公开告诉大家合约做了什么(广播)
//变量变化之后需要调用event函数,才会被记录到EVM中,也就可以被监听到了
contract ERC20Basic {
uint public _totalSupply;
//查询合约上代币总数
function totalSupply() public constant returns (uint);
//查询指定地址代币余额
function balanceOf(address who) public constant returns (uint);
//代币交易
function transfer(address to, uint value) public;
event Transfer(address indexed from, address indexed to, uint value);
}
//erc20协议的扩展函数(这样说可能不合适) //其实写在一个contract内也可以 这只是分开条理清晰一些
//is 继承 继承了上面ERC20Basic合约
contract ERC20 is ERC20Basic {
//查询owner授权给spender剩余的代币额度
function allowance(address owner, address spender) public constant returns (uint);
//代理交易 从from转代币给to
//这里不讲太详细,说多可能会觉得乱,对代理交易有兴趣看我另一篇实战的博客
function transferFrom(address from, address to, uint value) public;
//msg.sender授权给spender value的代币额度
function approve(address spender, uint value) public;
event Approval(address indexed owner, address indexed spender, uint value);
}
//继承Ownable和ERC20Basic
contract BasicToken is Ownable, ERC20Basic {
//使用之前的工具类
using SafeMath for uint;
//账户及余额的mapping 其实就像一个map key是地址 value是余额
mapping(address => uint) public balances;
//定义 basisPointsRate 是合约燃料费比例 万分之?,如果大于0
//那么 交易时除以太币燃料费外还要扣你的代币,后面的实现是减少实际到账
uint public basisPointsRate = 0;
uint public maximumFee = 0;//最大燃料费
//修改器 防止短地址攻击(无需理解)
modifier onlyPayloadSize(uint size) {
require(!(msg.data.length < size + 4));
_;
}
//重写了交易实现
function transfer(address _to, uint _value) public onlyPayloadSize(2 * 32) {
//计算燃料费 basisPointsRate为0 这里没什么意义
uint fee = (_value.mul(basisPointsRate)).div(10000);
if (fee > maximumFee) {
fee = maximumFee;
}
uint sendAmount = _value.sub(fee);//减去燃料费 同样没意义
//交易 实际就是操作mipping数据而已
balances[msg.sender] = balances[msg.sender].sub(_value);
balances[_to] = balances[_to].add(sendAmount);
if (fee > 0) {
balances[owner] = balances[owner].add(fee);
Transfer(msg.sender, owner, fee);
}
//调用 event广播
Transfer(msg.sender, _to, sendAmount);
}
//重写查询余额
function balanceOf(address _owner) public constant returns (uint balance) {
return balances[_owner];
}
}
//继承BasicToken,ERC20
contract StandardToken is BasicToken, ERC20 {
//授权数据mapping 相当于map包map结构
mapping (address => mapping (address => uint)) public allowed;
//2**256就是2的256次方
uint public constant MAX_UINT = 2**256 - 1;
//重写代理交易
function transferFrom(address _from, address _to, uint _value) public onlyPayloadSize(3 * 32) {
var _allowance = allowed[_from][msg.sender];
//授权额度不足抛异常 下面就是减授权额度 交易 广播
// if (_value > _allowance) throw;
uint fee = (_value.mul(basisPointsRate)).div(10000);
if (fee > maximumFee) {
fee = maximumFee;
}
if (_allowance < MAX_UINT) {
allowed[_from][msg.sender] = _allowance.sub(_value);
}
uint sendAmount = _value.sub(fee);
balances[_from] = balances[_from].sub(_value);
balances[_to] = balances[_to].add(sendAmount);
if (fee > 0) {
balances[owner] = balances[owner].add(fee);
Transfer(_from, owner, fee);
}
Transfer(_from, _to, sendAmount);
}
//重写授权
function approve(address _spender, uint _value) public onlyPayloadSize(2 * 32) {
//如果已经有授权额度了 不能重新授权 必须先授权为0 再重新授权
require(!((_value != 0) && (allowed[msg.sender][_spender] != 0)));
allowed[msg.sender][_spender] = _value;
Approval(msg.sender, _spender, _value);
}
//重写授权额度
function allowance(address _owner, address _spender) public constant returns (uint remaining) {
return allowed[_owner][_spender];
}
}
//这个合约是用于合约更新时停止 启用合约 只有拥有者可以操作
//因为合约更新需要同步数据,所以更新时要停止数据变动,同步完在启用
//如果停止 将拒绝所有函数调用
contract Pausable is Ownable {
event Pause();
event Unpause();
bool public paused = false;//停止启用的变量
//修改器 验证当前是没有停止的
modifier whenNotPaused() {
require(!paused);
_;
}
//修改器 验证当前是停止的
modifier whenPaused() {
require(paused);
_;
}
//停止合约 onlyOwner whenNotPaused 修改器验证操作者是拥有者并且合约当前是启用的
function pause() onlyOwner whenNotPaused public {
paused = true;
Pause();
}
//启用合约 onlyOwner whenNotPaused 修改器验证操作者是拥有者并且合约当前是停止的
function unpause() onlyOwner whenPaused public {
paused = false;
Unpause();
}
}
//黑名单合约
contract BlackList is Ownable, BasicToken {
//查地址有没有被加入黑名单
function getBlackListStatus(address _maker) external constant returns (bool) {
return isBlackListed[_maker];
}
//获取合约拥有者
function getOwner() external constant returns (address) {
return owner;
}
//黑名单mapping
mapping (address => bool) public isBlackListed;
//加入黑名单
function addBlackList (address _evilUser) public onlyOwner {
isBlackListed[_evilUser] = true;
AddedBlackList(_evilUser);
}
//取消黑名单
function removeBlackList (address _clearedUser) public onlyOwner {
isBlackListed[_clearedUser] = false;
RemovedBlackList(_clearedUser);
}
//销毁黑名单账户 把它的代币清空
function destroyBlackFunds (address _blackListedUser) public onlyOwner {
require(isBlackListed[_blackListedUser]);
uint dirtyFunds = balanceOf(_blackListedUser);
balances[_blackListedUser] = 0;
_totalSupply -= dirtyFunds;
DestroyedBlackFunds(_blackListedUser, dirtyFunds);
}
event DestroyedBlackFunds(address _blackListedUser, uint _balance);
event AddedBlackList(address _user);
event RemovedBlackList(address _user);
}
//兼容老版本的合约函数 欢迎大佬补充 没太懂 没看到实现
contract UpgradedStandardToken is StandardToken{
function transferByLegacy(address from, address to, uint value) public;
function transferFromByLegacy(address sender, address from, address spender, uint value) public;
function approveByLegacy(address from, address spender, uint value) public;
}
//前边都是准备工作 这里才是最终的
contract TetherToken is Pausable, StandardToken, BlackList {
string public name;//代币名称
string public symbol;//代币符号
uint public decimals;// 代币小数点位数
address public upgradedAddress;//停用后要使用哪个版本??不确定
bool public deprecated;//版本控制变量 为true表示弃用这个版本了
//构造方法 发布合约时初始化代币信息 版本信息 拥有者信息
function TetherToken(uint _initialSupply, string _name, string _symbol, uint _decimals) public {
_totalSupply = _initialSupply;
name = _name;
symbol = _symbol;
decimals = _decimals;
balances[owner] = _initialSupply;
deprecated = false;
}
// 交易 如果是这个版本就用这个版本的函数
//whenNotPaused 验证当前是没有停止的
function transfer(address _to, uint _value) public whenNotPaused {
//过滤黑名单用户
require(!isBlackListed[msg.sender]);
if (deprecated) {
//!!!!!!这里我也没看懂 感觉像是没有实现还是怎样 欢迎大佬补充
return UpgradedStandardToken(upgradedAddress).transferByLegacy(msg.sender, _to, _value);
} else {
return super.transfer(_to, _value);
}
}
//代理交易
function transferFrom(address _from, address _to, uint _value) public whenNotPaused {
require(!isBlackListed[_from]);
if (deprecated) {
return UpgradedStandardToken(upgradedAddress).transferFromByLegacy(msg.sender, _from, _to, _value);
} else {
return super.transferFrom(_from, _to, _value);
}
}
//查代币余额
function balanceOf(address who) public constant returns (uint) {
if (deprecated) {
return UpgradedStandardToken(upgradedAddress).balanceOf(who);
} else {
return super.balanceOf(who);
}
}
//授权
function approve(address _spender, uint _value) public onlyPayloadSize(2 * 32) {
if (deprecated) {
return UpgradedStandardToken(upgradedAddress).approveByLegacy(msg.sender, _spender, _value);
} else {
return super.approve(_spender, _value);
}
}
//查询授权
function allowance(address _owner, address _spender) public constant returns (uint remaining) {
if (deprecated) {
return StandardToken(upgradedAddress).allowance(_owner, _spender);
} else {
return super.allowance(_owner, _spender);
}
}
//弃用当前版本
function deprecate(address _upgradedAddress) public onlyOwner {
deprecated = true;
upgradedAddress = _upgradedAddress;
Deprecate(_upgradedAddress);
}
//查代币总数
function totalSupply() public constant returns (uint) {
if (deprecated) {
return StandardToken(upgradedAddress).totalSupply();
} else {
return _totalSupply;
}
}
//发行代币 新发行的代币加到拥有者账户 onlyOwner 拥有者权限
function issue(uint amount) public onlyOwner {
require(_totalSupply + amount > _totalSupply);
require(balances[owner] + amount > balances[owner]);
balances[owner] += amount;
_totalSupply += amount;
Issue(amount);
}
//销毁一定的代币 从拥有者账户减 onlyOwner 拥有者权限
function redeem(uint amount) public onlyOwner {
require(_totalSupply >= amount);
require(balances[owner] >= amount);
_totalSupply -= amount;
balances[owner] -= amount;
Redeem(amount);
}
//修改燃料费 比例和最大燃料费参数 onlyOwner 拥有者权限
function setParams(uint newBasisPoints, uint newMaxFee) public onlyOwner {
// Ensure transparency by hardcoding limit beyond which fees can never be added
require(newBasisPoints < 20);
require(newMaxFee < 50);
basisPointsRate = newBasisPoints;
maximumFee = newMaxFee.mul(10**decimals);
Params(basisPointsRate, maximumFee);
}
// Called when new token are issued
event Issue(uint amount);
// Called when tokens are redeemed
event Redeem(uint amount);
// Called when contract is deprecated
event Deprecate(address newAddress);
// Called if contract ever adds fees
event Params(uint feeBasisPoints, uint maxFee);
}
```
附上一个我写的标准ERC20代币智能合约 (砍掉了白名单和版本更新模块)
[标准ERC20代币智能合约连接](https://github.com/lim960/Eth/blob/master/%E6%A0%87%E5%87%86ERC20%E4%BB%A3%E5%B8%81%E5%90%88%E7%BA%A6.js) ←←←