-
Notifications
You must be signed in to change notification settings - Fork 37
/
C20.sol
350 lines (296 loc) · 13 KB
/
C20.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
346
347
348
349
350
pragma solidity 0.4.11;
import './StandardToken.sol';
contract C20 is StandardToken {
// FIELDS
string public name = "Crypto20";
string public symbol = "C20";
uint256 public decimals = 18;
string public version = "9.0";
uint256 public tokenCap = 86206896 * 10**18;
// crowdsale parameters
uint256 public fundingStartBlock;
uint256 public fundingEndBlock;
// vesting fields
address public vestingContract;
bool private vestingSet = false;
// root control
address public fundWallet;
// control of liquidity and limited control of updatePrice
address public controlWallet;
// time to wait between controlWallet price updates
uint256 public waitTime = 5 hours;
// fundWallet controlled state variables
// halted: halt buying due to emergency, tradeable: signal that assets have been acquired
bool public halted = false;
bool public tradeable = false;
// -- totalSupply defined in StandardToken
// -- mapping to token balances done in StandardToken
uint256 public previousUpdateTime = 0;
Price public currentPrice;
uint256 public minAmount = 0.04 ether;
// map participant address to a withdrawal request
mapping (address => Withdrawal) public withdrawals;
// maps previousUpdateTime to the next price
mapping (uint256 => Price) public prices;
// maps addresses
mapping (address => bool) public whitelist;
// TYPES
struct Price { // tokensPerEth
uint256 numerator;
uint256 denominator;
}
struct Withdrawal {
uint256 tokens;
uint256 time; // time for each withdrawal is set to the previousUpdateTime
}
// EVENTS
event Buy(address indexed participant, address indexed beneficiary, uint256 ethValue, uint256 amountTokens);
event AllocatePresale(address indexed participant, uint256 amountTokens);
event Whitelist(address indexed participant);
event PriceUpdate(uint256 numerator, uint256 denominator);
event AddLiquidity(uint256 ethAmount);
event RemoveLiquidity(uint256 ethAmount);
event WithdrawRequest(address indexed participant, uint256 amountTokens);
event Withdraw(address indexed participant, uint256 amountTokens, uint256 etherAmount);
// MODIFIERS
modifier isTradeable { // exempt vestingContract and fundWallet to allow dev allocations
require(tradeable || msg.sender == fundWallet || msg.sender == vestingContract);
_;
}
modifier onlyWhitelist {
require(whitelist[msg.sender]);
_;
}
modifier onlyFundWallet {
require(msg.sender == fundWallet);
_;
}
modifier onlyManagingWallets {
require(msg.sender == controlWallet || msg.sender == fundWallet);
_;
}
modifier only_if_controlWallet {
if (msg.sender == controlWallet) _;
}
modifier require_waited {
require(safeSub(now, waitTime) >= previousUpdateTime);
_;
}
modifier only_if_increase (uint256 newNumerator) {
if (newNumerator > currentPrice.numerator) _;
}
// CONSTRUCTOR
function C20(address controlWalletInput, uint256 priceNumeratorInput, uint256 startBlockInput, uint256 endBlockInput) {
require(controlWalletInput != address(0));
require(priceNumeratorInput > 0);
require(endBlockInput > startBlockInput);
fundWallet = msg.sender;
controlWallet = controlWalletInput;
whitelist[fundWallet] = true;
whitelist[controlWallet] = true;
currentPrice = Price(priceNumeratorInput, 1000); // 1 token = 1 usd at ICO start
fundingStartBlock = startBlockInput;
fundingEndBlock = endBlockInput;
previousUpdateTime = now;
}
// METHODS
function setVestingContract(address vestingContractInput) external onlyFundWallet {
require(vestingContractInput != address(0));
vestingContract = vestingContractInput;
whitelist[vestingContract] = true;
vestingSet = true;
}
// allows controlWallet to update the price within a time contstraint, allows fundWallet complete control
function updatePrice(uint256 newNumerator) external onlyManagingWallets {
require(newNumerator > 0);
require_limited_change(newNumerator);
// either controlWallet command is compliant or transaction came from fundWallet
currentPrice.numerator = newNumerator;
// maps time to new Price (if not during ICO)
prices[previousUpdateTime] = currentPrice;
previousUpdateTime = now;
PriceUpdate(newNumerator, currentPrice.denominator);
}
function require_limited_change (uint256 newNumerator)
private
only_if_controlWallet
require_waited
only_if_increase(newNumerator)
{
uint256 percentage_diff = 0;
percentage_diff = safeMul(newNumerator, 100) / currentPrice.numerator;
percentage_diff = safeSub(percentage_diff, 100);
// controlWallet can only increase price by max 20% and only every waitTime
require(percentage_diff <= 20);
}
function updatePriceDenominator(uint256 newDenominator) external onlyFundWallet {
require(block.number > fundingEndBlock);
require(newDenominator > 0);
currentPrice.denominator = newDenominator;
// maps time to new Price
prices[previousUpdateTime] = currentPrice;
previousUpdateTime = now;
PriceUpdate(currentPrice.numerator, newDenominator);
}
function allocateTokens(address participant, uint256 amountTokens) private {
require(vestingSet);
// 13% of total allocated for PR, Marketing, Team, Advisors
uint256 developmentAllocation = safeMul(amountTokens, 14942528735632185) / 100000000000000000;
// check that token cap is not exceeded
uint256 newTokens = safeAdd(amountTokens, developmentAllocation);
require(safeAdd(totalSupply, newTokens) <= tokenCap);
// increase token supply, assign tokens to participant
totalSupply = safeAdd(totalSupply, newTokens);
balances[participant] = safeAdd(balances[participant], amountTokens);
balances[vestingContract] = safeAdd(balances[vestingContract], developmentAllocation);
}
function allocatePresaleTokens(address participant, uint amountTokens) external onlyFundWallet {
require(block.number < fundingEndBlock);
require(participant != address(0));
whitelist[participant] = true; // automatically whitelist accepted presale
allocateTokens(participant, amountTokens);
Whitelist(participant);
AllocatePresale(participant, amountTokens);
}
function verifyParticipant(address participant) external onlyManagingWallets {
whitelist[participant] = true;
Whitelist(participant);
}
function buy() external payable {
buyTo(msg.sender);
}
function buyTo(address participant) public payable onlyWhitelist {
require(!halted);
require(participant != address(0));
require(msg.value >= minAmount);
require(block.number >= fundingStartBlock && block.number < fundingEndBlock);
uint256 icoDenominator = icoDenominatorPrice();
uint256 tokensToBuy = safeMul(msg.value, currentPrice.numerator) / icoDenominator;
allocateTokens(participant, tokensToBuy);
// send ether to fundWallet
fundWallet.transfer(msg.value);
Buy(msg.sender, participant, msg.value, tokensToBuy);
}
// time based on blocknumbers, assuming a blocktime of 30s
function icoDenominatorPrice() public constant returns (uint256) {
uint256 icoDuration = safeSub(block.number, fundingStartBlock);
uint256 denominator;
if (icoDuration < 2880) { // #blocks = 24*60*60/30 = 2880
return currentPrice.denominator;
} else if (icoDuration < 80640 ) { // #blocks = 4*7*24*60*60/30 = 80640
denominator = safeMul(currentPrice.denominator, 105) / 100;
return denominator;
} else {
denominator = safeMul(currentPrice.denominator, 110) / 100;
return denominator;
}
}
function requestWithdrawal(uint256 amountTokensToWithdraw) external isTradeable onlyWhitelist {
require(block.number > fundingEndBlock);
require(amountTokensToWithdraw > 0);
address participant = msg.sender;
require(balanceOf(participant) >= amountTokensToWithdraw);
require(withdrawals[participant].tokens == 0); // participant cannot have outstanding withdrawals
balances[participant] = safeSub(balances[participant], amountTokensToWithdraw);
withdrawals[participant] = Withdrawal({tokens: amountTokensToWithdraw, time: previousUpdateTime});
WithdrawRequest(participant, amountTokensToWithdraw);
}
function withdraw() external {
address participant = msg.sender;
uint256 tokens = withdrawals[participant].tokens;
require(tokens > 0); // participant must have requested a withdrawal
uint256 requestTime = withdrawals[participant].time;
// obtain the next price that was set after the request
Price price = prices[requestTime];
require(price.numerator > 0); // price must have been set
uint256 withdrawValue = safeMul(tokens, price.denominator) / price.numerator;
// if contract ethbal > then send + transfer tokens to fundWallet, otherwise give tokens back
withdrawals[participant].tokens = 0;
if (this.balance >= withdrawValue)
enact_withdrawal_greater_equal(participant, withdrawValue, tokens);
else
enact_withdrawal_less(participant, withdrawValue, tokens);
}
function enact_withdrawal_greater_equal(address participant, uint256 withdrawValue, uint256 tokens)
private
{
assert(this.balance >= withdrawValue);
balances[fundWallet] = safeAdd(balances[fundWallet], tokens);
participant.transfer(withdrawValue);
Withdraw(participant, tokens, withdrawValue);
}
function enact_withdrawal_less(address participant, uint256 withdrawValue, uint256 tokens)
private
{
assert(this.balance < withdrawValue);
balances[participant] = safeAdd(balances[participant], tokens);
Withdraw(participant, tokens, 0); // indicate a failed withdrawal
}
function checkWithdrawValue(uint256 amountTokensToWithdraw) constant returns (uint256 etherValue) {
require(amountTokensToWithdraw > 0);
require(balanceOf(msg.sender) >= amountTokensToWithdraw);
uint256 withdrawValue = safeMul(amountTokensToWithdraw, currentPrice.denominator) / currentPrice.numerator;
require(this.balance >= withdrawValue);
return withdrawValue;
}
// allow fundWallet or controlWallet to add ether to contract
function addLiquidity() external onlyManagingWallets payable {
require(msg.value > 0);
AddLiquidity(msg.value);
}
// allow fundWallet to remove ether from contract
function removeLiquidity(uint256 amount) external onlyManagingWallets {
require(amount <= this.balance);
fundWallet.transfer(amount);
RemoveLiquidity(amount);
}
function changeFundWallet(address newFundWallet) external onlyFundWallet {
require(newFundWallet != address(0));
fundWallet = newFundWallet;
}
function changeControlWallet(address newControlWallet) external onlyFundWallet {
require(newControlWallet != address(0));
controlWallet = newControlWallet;
}
function changeWaitTime(uint256 newWaitTime) external onlyFundWallet {
waitTime = newWaitTime;
}
function updateFundingStartBlock(uint256 newFundingStartBlock) external onlyFundWallet {
require(block.number < fundingStartBlock);
require(block.number < newFundingStartBlock);
fundingStartBlock = newFundingStartBlock;
}
function updateFundingEndBlock(uint256 newFundingEndBlock) external onlyFundWallet {
require(block.number < fundingEndBlock);
require(block.number < newFundingEndBlock);
fundingEndBlock = newFundingEndBlock;
}
function halt() external onlyFundWallet {
halted = true;
}
function unhalt() external onlyFundWallet {
halted = false;
}
function enableTrading() external onlyFundWallet {
require(block.number > fundingEndBlock);
tradeable = true;
}
// fallback function
function() payable {
require(tx.origin == msg.sender);
buyTo(msg.sender);
}
function claimTokens(address _token) external onlyFundWallet {
require(_token != address(0));
Token token = Token(_token);
uint256 balance = token.balanceOf(this);
token.transfer(fundWallet, balance);
}
// prevent transfers until trading allowed
function transfer(address _to, uint256 _value) isTradeable returns (bool success) {
return super.transfer(_to, _value);
}
function transferFrom(address _from, address _to, uint256 _value) isTradeable returns (bool success) {
return super.transferFrom(_from, _to, _value);
}
}