-
Notifications
You must be signed in to change notification settings - Fork 15
/
Trustee.sol
167 lines (135 loc) · 5.8 KB
/
Trustee.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
pragma solidity ^0.4.11;
import './SaferMath.sol';
import './Ownable.sol';
import './StoxSmartToken.sol';
/// @title Vesting trustee
contract Trustee is Ownable {
using SaferMath for uint256;
// The address of the STX ERC20 token.
StoxSmartToken public stox;
struct Grant {
uint256 value;
uint256 start;
uint256 cliff;
uint256 end;
uint256 transferred;
bool revokable;
}
// Grants holder.
mapping (address => Grant) public grants;
// Total tokens available for vesting.
uint256 public totalVesting;
event NewGrant(address indexed _from, address indexed _to, uint256 _value);
event UnlockGrant(address indexed _holder, uint256 _value);
event RevokeGrant(address indexed _holder, uint256 _refund);
/// @dev Constructor that initializes the address of the StoxSmartToken contract.
/// @param _stox StoxSmartToken The address of the previously deployed StoxSmartToken smart contract.
function Trustee(StoxSmartToken _stox) {
require(_stox != address(0));
stox = _stox;
}
/// @dev Grant tokens to a specified address.
/// @param _to address The address to grant tokens to.
/// @param _value uint256 The amount of tokens to be granted.
/// @param _start uint256 The beginning of the vesting period.
/// @param _cliff uint256 Duration of the cliff period.
/// @param _end uint256 The end of the vesting period.
/// @param _revokable bool Whether the grant is revokable or not.
function grant(address _to, uint256 _value, uint256 _start, uint256 _cliff, uint256 _end, bool _revokable)
public onlyOwner {
require(_to != address(0));
require(_value > 0);
// Make sure that a single address can be granted tokens only once.
require(grants[_to].value == 0);
// Check for date inconsistencies that may cause unexpected behavior.
require(_start <= _cliff && _cliff <= _end);
// Check that this grant doesn't exceed the total amount of tokens currently available for vesting.
require(totalVesting.add(_value) <= stox.balanceOf(address(this)));
// Assign a new grant.
grants[_to] = Grant({
value: _value,
start: _start,
cliff: _cliff,
end: _end,
transferred: 0,
revokable: _revokable
});
// Tokens granted, reduce the total amount available for vesting.
totalVesting = totalVesting.add(_value);
NewGrant(msg.sender, _to, _value);
}
/// @dev Revoke the grant of tokens of a specifed address.
/// @param _holder The address which will have its tokens revoked.
function revoke(address _holder) public onlyOwner {
Grant grant = grants[_holder];
require(grant.revokable);
// Send the remaining STX back to the owner.
uint256 refund = grant.value.sub(grant.transferred);
// Remove the grant.
delete grants[_holder];
totalVesting = totalVesting.sub(refund);
stox.transfer(msg.sender, refund);
RevokeGrant(_holder, refund);
}
/// @dev Calculate the total amount of vested tokens of a holder at a given time.
/// @param _holder address The address of the holder.
/// @param _time uint256 The specific time.
/// @return a uint256 representing a holder's total amount of vested tokens.
function vestedTokens(address _holder, uint256 _time) public constant returns (uint256) {
Grant grant = grants[_holder];
if (grant.value == 0) {
return 0;
}
return calculateVestedTokens(grant, _time);
}
/// @dev Calculate amount of vested tokens at a specifc time.
/// @param _grant Grant The vesting grant.
/// @param _time uint256 The time to be checked
/// @return An uint256 representing the amount of vested tokens of a specific grant.
/// | _/-------- vestedTokens rect
/// | _/
/// | _/
/// | _/
/// | _/
/// | /
/// | .|
/// | . |
/// | . |
/// | . |
/// | . |
/// | . |
/// +===+===========+---------+----------> time
/// Start Cliff End
function calculateVestedTokens(Grant _grant, uint256 _time) private constant returns (uint256) {
// If we're before the cliff, then nothing is vested.
if (_time < _grant.cliff) {
return 0;
}
// If we're after the end of the vesting period - everything is vested;
if (_time >= _grant.end) {
return _grant.value;
}
// Interpolate all vested tokens: vestedTokens = tokens/// (time - start) / (end - start)
return _grant.value.mul(_time.sub(_grant.start)).div(_grant.end.sub(_grant.start));
}
/// @dev Unlock vested tokens and transfer them to their holder.
/// @return a uint256 representing the amount of vested tokens transferred to their holder.
function unlockVestedTokens() public {
Grant grant = grants[msg.sender];
require(grant.value != 0);
// Get the total amount of vested tokens, acccording to grant.
uint256 vested = calculateVestedTokens(grant, now);
if (vested == 0) {
return;
}
// Make sure the holder doesn't transfer more than what he already has.
uint256 transferable = vested.sub(grant.transferred);
if (transferable == 0) {
return;
}
grant.transferred = grant.transferred.add(transferable);
totalVesting = totalVesting.sub(transferable);
stox.transfer(msg.sender, transferable);
UnlockGrant(msg.sender, transferable);
}
}