-
Notifications
You must be signed in to change notification settings - Fork 5
/
RIFToken.sol
336 lines (250 loc) · 11.9 KB
/
RIFToken.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
pragma solidity ^0.4.24;
import "../third-party/openzeppelin/token/ERC20/StandardToken.sol";
import "../third-party/openzeppelin/token/ERC20/DetailedERC20.sol";
import "../third-party/openzeppelin/ownership/Ownable.sol";
import "../ERC677/ERC677TransferReceiver.sol";
import "./AddressLinker.sol";
import "../util/AddressHelper.sol";
contract RIFToken is DetailedERC20, Ownable, StandardToken {
/**
* Transfer event as described in ERC-677
* See https://github.com/ethereum/EIPs/issues/677 for details
*/
event Transfer(address indexed from, address indexed to, uint256 value, bytes data);
mapping(address => uint) public minimumLeftFromSale;
// is the account of the original contributor
mapping(address => bool) public isInitialContributor;
// redeemed to same account or to another account
mapping(address => bool) public isRedeemed;
// original or redeemed contributor addresses
mapping (address => bool) public isOriginalOrRedeemedContributor;
// redirect:
// returns new address old address is now mapped
mapping(address => address) public redirect;
bool public enableManagerContract;
address public authorizedManagerContract;
uint public distributionTime;
uint256 constant REDEEM_DEADLINE = 365 days;
address constant ZERO_ADDRESS = address(0);
// The RIF token has minimum ownership permissions until ownership is manually withdrawn by
// releaseOwnership()
constructor() DetailedERC20("RIF","RIF",18) public {
// There will only ever be 1 billion tokens. Each tokens has 18 decimal digits.
// Therefore, 1 billion = 1,000,000,000 = 10**9 followed by 18 more zeroes = 10**18
// Total => 10**27 RIFIs.
totalSupply_ = 10**27;
balances[address(this)] = totalSupply_;
enableManagerContract = false;
authorizedManagerContract = ZERO_ADDRESS;
distributionTime = 0;
}
function getMinimumLeftFromSale(address a) public view returns(uint) {
address dest = getRedirectedAddress(a);
return minimumLeftFromSale[dest];
}
function disableManagerContract() public onlyAuthorizedManagerContract {
enableManagerContract = false;
}
function closeTokenDistribution(uint _distributionTime) public onlyAuthorizedManagerContract {
require(distributionTime == 0);
distributionTime = _distributionTime;
}
function setAuthorizedManagerContract(address authorized) public onlyOwner {
require(authorizedManagerContract == ZERO_ADDRESS);
authorizedManagerContract = authorized;
enableManagerContract = true;
transferAll(this, authorized);
}
modifier onlyAuthorizedManagerContract() {
require(msg.sender==authorizedManagerContract);
require(enableManagerContract);
_;
}
modifier onlyWhileInDistribution() {
require(distributionTime == 0);
_;
}
modifier onlyAfterDistribution() {
require(distributionTime > 0 && now >= distributionTime);
_;
}
modifier onlyIfAddressUsable(address sender) {
require(!isInitialContributor[sender] || isRedeemed[sender]);
_;
}
// Important: this is an internal function. It doesn't verify transfer rights.
function transferAll(address _from, address _to) internal returns (bool) {
require(_to != ZERO_ADDRESS);
uint256 _value;
_value = balances[_from];
balances[_from] = 0;
balances[_to] = balances[_to].add(_value);
emit Transfer(_from, _to, _value);
return true;
}
function transferToShareholder(address wallet, uint amount) public onlyWhileInDistribution onlyAuthorizedManagerContract {
bool result = super.transfer(wallet, amount);
if (!result) revert();
}
// TokenManager is the owner of the tokens to the pre-sale contributors and will distribute them
// also TokenManager is the owner of the bonuses.
function transferToContributor(address contributor, uint256 amount) public onlyWhileInDistribution onlyAuthorizedManagerContract {
if (!validAddress(contributor)) return;
super.transfer(contributor, amount);
minimumLeftFromSale[contributor] += amount; //sets the contributor as an ITA special address
isInitialContributor[contributor] = true;
isOriginalOrRedeemedContributor[contributor] = true;
}
// If this transfer fails, there will be a problem because other bonus won't be able to be paid.
function transferBonus(address _to, uint256 _value) public onlyAuthorizedManagerContract returns (bool) {
if (!isInitialContributor[_to]) return false;
address finalAddress = getRedirectedAddress(_to);
return super.transfer(finalAddress, _value);
}
function delegate(address from, address to) public onlyAuthorizedManagerContract returns (bool) {
if (!isInitialContributor[from] || isRedeemed[from]) {
return false;
}
if (!transferAll(from, to)) {
return false;
}
// mark as redirected and redeemed, for informational purposes
redirect[from] = to;
isRedeemed[from] = true;
return true;
}
function redeemIsAllowed() public view returns (bool) {
return distributionTime > 0 &&
now >= distributionTime &&
now <= distributionTime + REDEEM_DEADLINE;
}
function redeemToSameAddress() public returns (bool) {
require(redeemIsAllowed());
// Only an original contributor can be redeemed
require(isInitialContributor[msg.sender]);
isRedeemed[msg.sender] = true;
return true;
}
// Important: the user should not use the same contributorAddress for two different chains.
function redeem(
address contributorAddress, uint chainId,
string redeemAddressAsString, uint8 sig_v,
bytes32 sig_r, bytes32 sig_s) public returns (bool) {
require(redeemIsAllowed());
// Only an original contributor can be redeemed
require(isInitialContributor[contributorAddress]);
// Avoid redeeming an already redeemed address
require(!isRedeemed[contributorAddress]);
address redeemAddress = AddressHelper.fromAsciiString(redeemAddressAsString);
// Avoid reusing a contributor address
require(!isOriginalOrRedeemedContributor[redeemAddress]);
require(AddressLinker.acceptLinkedRskAddress(contributorAddress, chainId,
redeemAddressAsString, sig_v, sig_r, sig_s));
// Now we must move the funds from the old address to the new address
minimumLeftFromSale[redeemAddress] = minimumLeftFromSale[contributorAddress];
minimumLeftFromSale[contributorAddress] = 0;
// Mark as redirected and redeemed
redirect[contributorAddress] = redeemAddress;
isRedeemed[contributorAddress] = true;
isOriginalOrRedeemedContributor[redeemAddress] = true;
// Once the contributorAddress has moved the funds to the new RSK address, what to do with the old address?
// Users should not receive RIFs in the old address from other users. If they do, they may not be able to access
// those RIFs.
return transferAll(contributorAddress, redeemAddress);
}
function contingentRedeem(
address contributorAddress,
uint chainId,
address redeemAddress, uint8 sig_v,
bytes32 sig_r, bytes32 sig_s) public onlyOwner returns (bool) {
require(redeemIsAllowed());
// Only an original contributor can be redeemed
require(isInitialContributor[contributorAddress]);
// Avoid redeeming an already redeemed address
require(!isRedeemed[contributorAddress]);
// Avoid reusing a contributor address
require(!isOriginalOrRedeemedContributor[redeemAddress]);
if (!AddressLinker.acceptDelegate(contributorAddress, chainId, sig_v, sig_r, sig_s)) revert();
// Now we must move the funds from the old address to the new address
minimumLeftFromSale[redeemAddress] = minimumLeftFromSale[contributorAddress];
minimumLeftFromSale[contributorAddress] = 0;
// Mark as redirected and redeemed
redirect[contributorAddress] = redeemAddress;
isRedeemed[contributorAddress] = true;
isOriginalOrRedeemedContributor[redeemAddress] = true;
// Once the contributorAddress has moved the funds to the new RSK address, what to do with the old address?
// Users should not receive RIFs in the old address from other users. If they do, they may not be able to access
// those RIFs.
return transferAll(contributorAddress, redeemAddress);
}
function getRedirectedAddress(address a) public view returns(address) {
address r = redirect[a];
if (r != ZERO_ADDRESS) {
return r;
}
return a;
}
function validAddress(address a) public pure returns(bool) {
return (a != ZERO_ADDRESS);
}
function wasRedirected(address a) public view returns(bool) {
return (redirect[a] != ZERO_ADDRESS);
}
function transfer(address _to, uint256 _value) public onlyAfterDistribution onlyIfAddressUsable(msg.sender) returns (bool) {
// cannot transfer to a redirected account
if (wasRedirected(_to)) return false;
bool result = super.transfer(_to, _value);
if (!result) return false;
doTrackMinimums(msg.sender);
return true;
}
/**
* ERC-677's only method implementation
* See https://github.com/ethereum/EIPs/issues/677 for details
*/
function transferAndCall(address _to, uint _value, bytes _data) public returns (bool) {
bool result = transfer(_to, _value);
if (!result) return false;
emit Transfer(msg.sender, _to, _value, _data);
ERC677TransferReceiver receiver = ERC677TransferReceiver(_to);
receiver.tokenFallback(msg.sender, _value, _data);
// IMPORTANT: the ERC-677 specification does not say
// anything about the use of the receiver contract's
// tokenFallback method return value. Given
// its return type matches with this method's return
// type, returning it could be a possibility.
// We here take the more conservative approach and
// ignore the return value, returning true
// to signal a succesful transfer despite tokenFallback's
// return value -- fact being tokens are transferred
// in any case.
return true;
}
function transferFrom(address _from, address _to, uint256 _value) public onlyAfterDistribution onlyIfAddressUsable(_from) returns (bool) {
// cannot transfer to a redirected account
if (wasRedirected(_to)) return false;
bool result = super.transferFrom(_from, _to, _value);
if (!result) return false;
doTrackMinimums(_from);
return true;
}
function approve(address _spender, uint256 _value) public onlyAfterDistribution onlyIfAddressUsable(msg.sender) returns (bool) {
return super.approve(_spender, _value);
}
function increaseApproval(address _spender, uint256 _addedValue) public onlyAfterDistribution onlyIfAddressUsable(msg.sender) returns (bool) {
return super.increaseApproval(_spender, _addedValue);
}
function decreaseApproval(address _spender, uint256 _subtractedValue) public onlyAfterDistribution onlyIfAddressUsable(msg.sender) returns (bool) {
return super.decreaseApproval(_spender, _subtractedValue);
}
function doTrackMinimums(address addr) private {
// We only track minimums while there's a manager
// contract that can pay the bonuses for which
// these minimums are tracked for in the first place.
if (!enableManagerContract) return;
uint m = minimumLeftFromSale[addr];
if ((m>0) && (balances[addr] < m)) {
minimumLeftFromSale[addr] = balances[addr];
}
}
}