-
Notifications
You must be signed in to change notification settings - Fork 97
/
Copy path0x2bdc0d42996017fce214b21607a515da41a9e0c5-SKIN-SkinCoin.sol
377 lines (366 loc) · 12.7 KB
/
0x2bdc0d42996017fce214b21607a515da41a9e0c5-SKIN-SkinCoin.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
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
pragma solidity ^0.4.11;
library SafeMath {
function mul(uint a, uint b) internal returns (uint) {
uint c = a * b;
assert(a == 0 || c / a == b);
return c;
}
function div(uint a, uint b) internal returns (uint) {
assert(b > 0);
uint c = a / b;
assert(a == b * c + a % b);
return c;
}
function sub(uint a, uint b) internal returns (uint) {
assert(b <= a);
return a - b;
}
function add(uint a, uint b) internal returns (uint) {
uint c = a + b;
assert(c >= a);
return c;
}
function max64(uint64 a, uint64 b) internal constant returns (uint64) {
return a >= b ? a : b;
}
function min64(uint64 a, uint64 b) internal constant returns (uint64) {
return a < b ? a : b;
}
function max256(uint256 a, uint256 b) internal constant returns (uint256) {
return a >= b ? a : b;
}
function min256(uint256 a, uint256 b) internal constant returns (uint256) {
return a < b ? a : b;
}
function assert(bool assertion) internal {
if (!assertion) {
throw;
}
}
}
contract Ownable {
address public owner;
function Ownable() {
owner = msg.sender;
}
modifier onlyOwner {
if (msg.sender != owner) throw;
_;
}
function transferOwnership(address newOwner) onlyOwner {
if (newOwner != address(0)) {
owner = newOwner;
}
}
}
/*
* Pausable
* Abstract contract that allows children to implement an
* emergency stop mechanism.
*/
contract Pausable is Ownable {
bool public stopped;
modifier stopInEmergency {
if (stopped) {
throw;
}
_;
}
modifier onlyInEmergency {
if (!stopped) {
throw;
}
_;
}
// called by the owner on emergency, triggers stopped state
function emergencyStop() external onlyOwner {
stopped = true;
}
// called by the owner on end of emergency, returns to normal state
function release() external onlyOwner onlyInEmergency {
stopped = false;
}
}
contract ERC20Basic {
uint public totalSupply;
function balanceOf(address who) constant returns (uint);
function transfer(address to, uint value);
event Transfer(address indexed from, address indexed to, uint value);
}
contract ERC20 is ERC20Basic {
function allowance(address owner, address spender) constant returns (uint);
function transferFrom(address from, address to, uint value);
function approve(address spender, uint value);
event Approval(address indexed owner, address indexed spender, uint value);
}
/*
* PullPayment
* Base contract supporting async send for pull payments.
* Inherit from this contract and use asyncSend instead of send.
*/
contract PullPayment {
using SafeMath for uint;
mapping(address => uint) public payments;
event LogRefundETH(address to, uint value);
/**
* Store sent amount as credit to be pulled, called by payer
**/
function asyncSend(address dest, uint amount) internal {
payments[dest] = payments[dest].add(amount);
}
// withdraw accumulated balance, called by payee
function withdrawPayments() {
address payee = msg.sender;
uint payment = payments[payee];
if (payment == 0) {
throw;
}
if (this.balance < payment) {
throw;
}
payments[payee] = 0;
if (!payee.send(payment)) {
throw;
}
LogRefundETH(payee,payment);
}
}
contract BasicToken is ERC20Basic {
using SafeMath for uint;
mapping(address => uint) balances;
/*
* Fix for the ERC20 short address attack
*/
modifier onlyPayloadSize(uint size) {
if(msg.data.length < size + 4) {
throw;
}
_;
}
function transfer(address _to, uint _value) onlyPayloadSize(2 * 32) {
balances[msg.sender] = balances[msg.sender].sub(_value);
balances[_to] = balances[_to].add(_value);
Transfer(msg.sender, _to, _value);
}
function balanceOf(address _owner) constant returns (uint balance) {
return balances[_owner];
}
}
contract StandardToken is BasicToken, ERC20 {
mapping (address => mapping (address => uint)) allowed;
function transferFrom(address _from, address _to, uint _value) onlyPayloadSize(3 * 32) {
var _allowance = allowed[_from][msg.sender];
// Check is not needed because sub(_allowance, _value) will already throw if this condition is not met
// if (_value > _allowance) throw;
balances[_to] = balances[_to].add(_value);
balances[_from] = balances[_from].sub(_value);
allowed[_from][msg.sender] = _allowance.sub(_value);
Transfer(_from, _to, _value);
}
function approve(address _spender, uint _value) {
// To change the approve amount you first have to reduce the addresses`
// allowance to zero by calling `approve(_spender, 0)` if it is not
// already 0 to mitigate the race condition described here:
// https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
if ((_value != 0) && (allowed[msg.sender][_spender] != 0)) throw;
allowed[msg.sender][_spender] = _value;
Approval(msg.sender, _spender, _value);
}
function allowance(address _owner, address _spender) constant returns (uint remaining) {
return allowed[_owner][_spender];
}
}
/**
* SkinCoin token contract. Implements
*/
contract SkinCoin is StandardToken, Ownable {
string public constant name = "SkinCoin";
string public constant symbol = "SKIN";
uint public constant decimals = 6;
// Constructor
function SkinCoin() {
totalSupply = 1000000000000000;
balances[msg.sender] = totalSupply; // Send all tokens to owner
}
/**
* Burn away the specified amount of SkinCoin tokens
*/
function burn(uint _value) onlyOwner returns (bool) {
balances[msg.sender] = balances[msg.sender].sub(_value);
totalSupply = totalSupply.sub(_value);
Transfer(msg.sender, 0x0, _value);
return true;
}
}
/*
Crowdsale Smart Contract for the skincoin.org project
This smart contract collects ETH, and in return emits SkinCoin tokens to the backers
*/
contract Crowdsale is Pausable, PullPayment {
using SafeMath for uint;
struct Backer {
uint weiReceived; // Amount of Ether given
uint coinSent;
}
/*
* Constants
*/
/* Minimum number of SkinCoin to sell */
uint public constant MIN_CAP = 30000000000000; // 30,000,000 SkinCoins
/* Maximum number of SkinCoin to sell */
uint public constant MAX_CAP = 600000000000000; // 600,000,000 SkinCoins
/* Minimum amount to invest */
uint public constant MIN_INVEST_ETHER = 100 finney;
/* Crowdsale period */
uint private constant CROWDSALE_PERIOD = 30 days;
/* Number of SkinCoins per Ether */
uint public constant COIN_PER_ETHER = 6000000000; // 6,000 SkinCoins
/*
* Variables
*/
/* SkinCoin contract reference */
SkinCoin public coin;
/* Multisig contract that will receive the Ether */
address public multisigEther;
/* Number of Ether received */
uint public etherReceived;
/* Number of SkinCoins sent to Ether contributors */
uint public coinSentToEther;
/* Crowdsale start time */
uint public startTime;
/* Crowdsale end time */
uint public endTime;
/* Is crowdsale still on going */
bool public crowdsaleClosed;
/* Backers Ether indexed by their Ethereum address */
mapping(address => Backer) public backers;
/*
* Modifiers
*/
modifier minCapNotReached() {
if ((now < endTime) || coinSentToEther >= MIN_CAP ) throw;
_;
}
modifier respectTimeFrame() {
if ((now < startTime) || (now > endTime )) throw;
_;
}
/*
* Event
*/
event LogReceivedETH(address addr, uint value);
event LogCoinsEmited(address indexed from, uint amount);
/*
* Constructor
*/
function Crowdsale(address _skinCoinAddress, address _to) {
coin = SkinCoin(_skinCoinAddress);
multisigEther = _to;
}
/*
* The fallback function corresponds to a donation in ETH
*/
function() stopInEmergency respectTimeFrame payable {
receiveETH(msg.sender);
}
/*
* To call to start the crowdsale
*/
function start() onlyOwner {
if (startTime != 0) throw; // Crowdsale was already started
startTime = now ;
endTime = now + CROWDSALE_PERIOD;
}
/*
* Receives a donation in Ether
*/
function receiveETH(address beneficiary) internal {
if (msg.value < MIN_INVEST_ETHER) throw; // Don't accept funding under a predefined threshold
uint coinToSend = bonus(msg.value.mul(COIN_PER_ETHER).div(1 ether)); // Compute the number of SkinCoin to send
if (coinToSend.add(coinSentToEther) > MAX_CAP) throw;
Backer backer = backers[beneficiary];
coin.transfer(beneficiary, coinToSend); // Transfer SkinCoins right now
backer.coinSent = backer.coinSent.add(coinToSend);
backer.weiReceived = backer.weiReceived.add(msg.value); // Update the total wei collected during the crowdfunding for this backer
etherReceived = etherReceived.add(msg.value); // Update the total wei collected during the crowdfunding
coinSentToEther = coinSentToEther.add(coinToSend);
// Send events
LogCoinsEmited(msg.sender ,coinToSend);
LogReceivedETH(beneficiary, etherReceived);
}
/*
*Compute the SkinCoin bonus according to the investment period
*/
function bonus(uint amount) internal constant returns (uint) {
if (now < startTime.add(2 days)) return amount.add(amount.div(5)); // bonus 20%
return amount;
}
/*
* Finalize the crowdsale, should be called after the refund period
*/
function finalize() onlyOwner public {
if (now < endTime) { // Cannot finalise before CROWDSALE_PERIOD or before selling all coins
if (coinSentToEther == MAX_CAP) {
} else {
throw;
}
}
if (coinSentToEther < MIN_CAP && now < endTime + 15 days) throw; // If MIN_CAP is not reached donors have 15days to get refund before we can finalise
if (!multisigEther.send(this.balance)) throw; // Move the remaining Ether to the multisig address
uint remains = coin.balanceOf(this);
if (remains > 0) { // Burn the rest of SkinCoins
if (!coin.burn(remains)) throw ;
}
crowdsaleClosed = true;
}
/*
* Failsafe drain
*/
function drain() onlyOwner {
if (!owner.send(this.balance)) throw;
}
/**
* Allow to change the team multisig address in the case of emergency.
*/
function setMultisig(address addr) onlyOwner public {
if (addr == address(0)) throw;
multisigEther = addr;
}
/**
* Manually back SkinCoin owner address.
*/
function backSkinCoinOwner() onlyOwner public {
coin.transferOwnership(owner);
}
/**
* Transfer remains to owner in case if impossible to do min invest
*/
function getRemainCoins() onlyOwner public {
var remains = MAX_CAP - coinSentToEther;
uint minCoinsToSell = bonus(MIN_INVEST_ETHER.mul(COIN_PER_ETHER) / (1 ether));
if(remains > minCoinsToSell) throw;
Backer backer = backers[owner];
coin.transfer(owner, remains); // Transfer SkinCoins right now
backer.coinSent = backer.coinSent.add(remains);
coinSentToEther = coinSentToEther.add(remains);
// Send events
LogCoinsEmited(this ,remains);
LogReceivedETH(owner, etherReceived);
}
/*
* When MIN_CAP is not reach:
* 1) backer call the "approve" function of the SkinCoin token contract with the amount of all SkinCoins they got in order to be refund
* 2) backer call the "refund" function of the Crowdsale contract with the same amount of SkinCoins
* 3) backer call the "withdrawPayments" function of the Crowdsale contract to get a refund in ETH
*/
function refund(uint _value) minCapNotReached public {
if (_value != backers[msg.sender].coinSent) throw; // compare value from backer balance
coin.transferFrom(msg.sender, address(this), _value); // get the token back to the crowdsale contract
if (!coin.burn(_value)) throw ; // token sent for refund are burnt
uint ETHToSend = backers[msg.sender].weiReceived;
backers[msg.sender].weiReceived=0;
if (ETHToSend > 0) {
asyncSend(msg.sender, ETHToSend); // pull payment to get refund in ETH
}
}
}