咸鱼

咸鱼是以盐腌渍后,晒干的鱼

0%

9、OpenZeppelin ERC20源码注释

ERC20:Ethereum Request for Comments 20,是一个基于以太坊代币的接口标准(协议)。所有符合 ERC-20 标准的代币都能立即兼容以太坊钱包,它能让用户和交易所,都能非常方便的管理多种代币,转账、存储、ICO 等等。

StandardToken.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
pragma solidity ^0.4.24;

import "./BasicToken.sol";
import "./ERC20.sol";

/**
* @title 标准 ERC20 token
*
* @dev 实现基础的标准token
* @dev https://github.com/ethereum/EIPs/issues/20
* @dev Based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
*/
contract StandardToken is ERC20, BasicToken {
mapping (address => mapping (address => uint256)) internal allowed;

/**
* @dev 从一个地址向另外一个地址转token
* @param _from 转账的from地址
* @param _to address 转账的to地址
* @param _value uint256 转账token数量
*/
function transferFrom(
address _from,
address _to,
uint256 _value
)
public
returns (bool)
{
// 做合法性检查
require(_to != address(0));
require(_value <= balances[_from]);
require(_value <= allowed[_from][msg.sender]);
//_from余额减去相应的金额
//_to余额加上相应的金额
//msg.sender可以从账户_from中转出的数量减少_value
balances[_from] = balances[_from].sub(_value);
balances[_to] = balances[_to].add(_value);
allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
// 触发Transfer事件
emit Transfer(_from, _to, _value);
return true;
}

/**
* @dev 批准传递的address以代表msg.sender花费指定数量的token
*
* Beware that changing an allowance with this method brings the risk that someone may use both the old
* and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
* race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
* @param _spender 花费资金的地址
* @param _value 可以被花费的token数量
*/
function approve(address _spender, uint256 _value) public returns (bool) {
//记录msg.sender允许_spender动用的token
allowed[msg.sender][_spender] = _value;
//触发Approval事件
emit Approval(msg.sender, _spender, _value);
return true;
}

/**
* @dev 函数检查所有者允许的_spender花费的token数量
* @param _owner address 资金所有者地址.
* @param _spender address 花费资金的spender的地址.
* @return A uint256 指定_spender仍可用token的数量。
*/
function allowance(
address _owner,
address _spender
)
public
view
returns (uint256)
{
//允许_spender从_owner中转出的token数
return allowed[_owner][_spender];
}

/**
* @dev 增加所有者允许_spender花费代币的数量。
*
* allowed[_spender] == 0时approve应该被调用. 增加allowed值最好使用此函数避免2此调用(等待知道第一笔交易被挖出)
* From MonolithDAO Token.sol
* @param _spender 花费资金的地址
* @param _addedValue 用于增加允许动用的token牌数量
*/
function increaseApproval(
address _spender,
uint _addedValue
)
public
returns (bool)
{
//在之前允许的数量上增加_addedValue
allowed[msg.sender][_spender] = (
allowed[msg.sender][_spender].add(_addedValue));
//触发Approval事件
emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
return true;
}

/**
* @dev 减少所有者允许_spender花费代币的数量
*
* allowed[_spender] == 0时approve应该被调用. 减少allowed值最好使用此函数避免2此调用(等待知道第一笔交易被挖出)
* From MonolithDAO Token.sol
* @param _spender 花费资金的地址
* @param _subtractedValue 用于减少允许动用的token牌数量
*/
function decreaseApproval(
address _spender,
uint _subtractedValue
)
public
returns (bool)
{
uint oldValue = allowed[msg.sender][_spender];
if (_subtractedValue > oldValue) {
//减少的数量少于之前允许的数量,则清零
allowed[msg.sender][_spender] = 0;
} else {
//减少对应的_subtractedValue数量
allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue);
}
//触发Approval事件
emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
return true;
}

}

BasicToken.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
pragma solidity ^0.4.24;

import "./ERC20Basic.sol";
import "../../math/SafeMath.sol";


/**
* @title 实现ERC20基本合约的接口
* @dev 基本的StandardToken,不包含allowances.
*/
contract BasicToken is ERC20Basic {
using SafeMath for uint256;

mapping(address => uint256) balances;

uint256 totalSupply_;

/**
* @dev 返回存在的token总数
*/
function totalSupply() public view returns (uint256) {
return totalSupply_;
}

/**
* @dev 给特定的address转token
* @param _to 要转账到的address
* @param _value 要转账的金额
*/
function transfer(address _to, uint256 _value) public returns (bool) {
//做相关的合法验证
require(_to != address(0));
require(_value <= balances[msg.sender]);
// msg.sender余额中减去额度,_to余额加上相应额度
balances[msg.sender] = balances[msg.sender].sub(_value);
balances[_to] = balances[_to].add(_value);
//触发Transfer事件
emit Transfer(msg.sender, _to, _value);
return true;
}

/**
* @dev 获取指定address的余额
* @param _owner 查询余额的address.
* @return An uint256 representing the amount owned by the passed address.
*/
function balanceOf(address _owner) public view returns (uint256) {
return balances[_owner];
}

}

SafeERC20.sol (一个 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
pragma solidity ^0.4.24;

import "./ERC20Basic.sol";
import "./ERC20.sol";

/**
* @title SafeERC20
* @dev 围绕ERC20操作发生故障的包装程序.
* 可以在合约中通过这样使用这个库 `using SafeERC20 for ERC20;` 来使用安全的操作`token.safeTransfer(...)`
*/
library SafeERC20 {
function safeTransfer(ERC20Basic token, address to, uint256 value) internal {
require(token.transfer(to, value));
}

function safeTransferFrom(
ERC20 token,
address from,
address to,
uint256 value
)
internal
{
require(token.transferFrom(from, to, value));
}

function safeApprove(ERC20 token, address spender, uint256 value) internal {
require(token.approve(spender, value));
}
}

时间锁仓

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
pragma solidity ^0.4.24;

import "zeppelin-solidity/contracts/token/ERC20/SafeERC20.sol";


/**
* @title TokenTimelock
* @dev TokenTimelock is a token holder contract that will allow a
* beneficiary to extract the tokens after a given release time
*/
contract TokenTimelock {
using SafeERC20 for ERC20Basic;

// 代币种类(合约地址)
ERC20Basic public token;

// 释放后的受益人address
address public beneficiary;

// token可以被释放的时间戳(秒)
uint256 public releaseTime;

/**
* 创建合约,定义Token,收益者,固定时间释放
* (Token要手动转入创建的合约地址)
*
* ERC20Basic _token : 锁仓的ERC20合约地址
* address _beneficiary: 受益者的合约地址
* uint256 _releaseTime: 锁仓时间(单位秒)
*/
constructor(
ERC20Basic _token,
address _beneficiary,
uint256 _releaseTime
)
public
{
// solium-disable-next-line security/no-block-members
require(_releaseTime > block.timestamp);
token = _token;
beneficiary = _beneficiary;
releaseTime = _releaseTime;
}

/**
* 将时间限制内的token转移给收益人.(合约中的余额 ->合约中的收益人)
*/
function release() public {
// solium-disable-next-line security/no-block-members
require(block.timestamp >= releaseTime);

uint256 amount = token.balanceOf(address(this));
require(amount > 0);

token.safeTransfer(beneficiary, 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
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
pragma solidity ^0.4.24;


import "zeppelin-solidity/contracts/token/ERC20/ERC20Basic.sol";
import "zeppelin-solidity/contracts/token/ERC20/SafeERC20.sol";
import "zeppelin-solidity/contracts/ownership/Ownable.sol";
import "zeppelin-solidity/contracts/math/SafeMath.sol";

contract TokenLockVesting is Ownable{

using SafeMath for uint256;
using SafeERC20 for ERC20Basic;

event Released(uint256 amount);
event Revoked();

// beneficiary of tokens after they are released
address public beneficiary;

uint256 public cliff;
uint256 public cliffTs;
uint256 public start;
uint256 public duration;

bool public revocable;

mapping (address => uint256) public released;
mapping (address => bool) public revoked;

/**
* @dev Creates a vesting contract that vests its balance of any ERC20 token to the
* _beneficiary, gradually in a linear fashion until _start + _duration. By then all
* of the balance will have vested.
* @param _beneficiary address of the beneficiary to whom vested tokens are transferred
* @param _cliff duration in seconds of the cliff in which tokens will begin to vest
* @param _start the time (as Unix time) at which point vesting starts
* @param _duration duration in seconds of the period in which the tokens will vest
* @param _revocable whether the vesting is revocable or not
*/
constructor(
address _beneficiary,
uint256 _start,
uint256 _cliff,
uint256 _duration,
bool _revocable
)
public
{
require(_beneficiary != address(0));
require(_cliff <= _duration);

beneficiary = _beneficiary;
revocable = _revocable;
duration = _duration;//duration是一个x秒时间段
start = _start;
cliff = _cliff;
cliffTs = _start.add(_cliff);//_cliff是x秒时间段,cliffTs是一个时间戳
}
/**
* @notice Transfers vested tokens to beneficiary.
* @param _token ERC20 token which is being vested
*/
function release(ERC20Basic _token) public {
uint256 unreleased = releasableAmount(_token);

require(unreleased > 0);

released[_token] = released[_token].add(unreleased);

_token.safeTransfer(beneficiary, unreleased);

emit Released(unreleased);
}

/**
* @notice Allows the owner to revoke the vesting. Tokens already vested
* remain in the contract, the rest are returned to the owner.
* @param _token ERC20 token which is being vested
*/
function revoke(ERC20Basic _token) public onlyOwner {
require(revocable);
require(!revoked[_token]);

uint256 balance = _token.balanceOf(address(this));

uint256 unreleased = releasableAmount(_token);
uint256 refund = balance.sub(unreleased);

revoked[_token] = true;

_token.safeTransfer(owner, refund);

emit Revoked();
}

/**
* @dev 计算已授予但尚未释放的金额。
* @dev Calculates the amount that has already vested but hasn't been released yet.
* @param _token ERC20 token which is being vested
*/
function releasableAmount(ERC20Basic _token) public view returns (uint256) {
return vestedAmount(_token).sub(released[_token]);
}

/**
* @dev 计算已授予的金额。
* @dev Calculates the amount that has already vested.
* @param _token ERC20 token which is being vested
*/
function vestedAmount(ERC20Basic _token) public view returns (uint256) {
uint256 currentBalance = _token.balanceOf(address(this));
uint256 totalBalance = currentBalance.add(released[_token]);

// block.timestamp 块校验unix时间戳
if (block.timestamp < cliffTs) {
//没到释放的时间,所以返回0
return 0;
} else if (block.timestamp >= start.add(duration) || revoked[_token]) {
//块时间已经超过了start + duration,所有的代币都可以释放
return totalBalance;
} else{
//持续释放时间之内
//mul: X * y
//div:x / y
// totalBalance * (block.timestamp - start) / duration
//return totalBalance.mul(block.timestamp.sub(start)).div(duration);

//改造
return totalBalance.mul(block.timestamp.sub(start).sub(cliff)).div(duration.sub(cliff));
}
}
}

了解更多