/**
* Crowdsale for Monetha Tokens.
* Raised Ether will be stored safely at the wallet and returned to the ICO in case the funding goal is not reached,
* allowing the investors to withdraw their funds.
* Author: Julia Altenried
* Internal audit: Alex Bazhanau, Andrej Ruckij
* Audit: Blockchain & Smart Contract Security Group
**/
pragma solidity ^0.4.15;
contract token {
function transferFrom(address sender, address receiver, uint amount) returns(bool success) {}
function burn() {}
function setStart(uint newStart) {}
}
contract SafeMath {
//internals
function safeMul(uint a, uint b) internal returns(uint) {
uint c = a * b;
assert(a == 0 || c / a == b);
return c;
}
function safeSub(uint a, uint b) internal returns(uint) {
assert(b <= a);
return a - b;
}
function safeAdd(uint a, uint b) internal returns(uint) {
uint c = a + b;
assert(c >= a && c >= b);
return c;
}
}
contract Crowdsale is SafeMath {
/* tokens will be transfered from this address */
address public tokenOwner;
/* if the funding goal is not reached, investors may withdraw their funds */
uint constant public fundingGoal = 672000000000;
/* when the soft cap is reached, the price for monetha tokens will rise */
uint constant public softCap = 6720000000000;
/* the maximum amount of tokens to be sold */
uint constant public maxGoal = 20120000000000;
/* how much has been raised by crowdale (in ETH) */
uint public amountRaised;
/* the start date of the crowdsale */
uint public start;
/* the end date of the crowdsale*/
uint public end;
/* time after reaching the soft cap, while the crowdsale will be still available*/
uint public timeAfterSoftCap;
/* the number of tokens already sold */
uint public tokensSold = 0;
/* the rates before and after the soft cap is reached */
uint constant public rateSoft = 24;
uint constant public rateHard = 20;
uint constant public rateCoefficient = 100000000000;
/* the address of the token contract */
token public tokenReward;
/* the balances (in ETH) of all investors */
mapping(address => uint) public balanceOf;
/* indicates if the crowdsale has been closed already */
bool public crowdsaleClosed = false;
/* the wallet on which the funds will be stored */
address msWallet;
/* notifying transfers and the success of the crowdsale*/
event GoalReached(address _tokenOwner, uint _amountRaised);
event FundTransfer(address backer, uint amount, bool isContribution, uint _amountRaised);
/* initialization, set the token address */
function Crowdsale(
address _tokenAddr,
address _walletAddr,
address _tokenOwner,
uint _start,
uint _end,
uint _timeAfterSoftCap) {
tokenReward = token(_tokenAddr);
msWallet = _walletAddr;
tokenOwner = _tokenOwner;
require(_start < _end);
start = _start;
end = _end;
timeAfterSoftCap = _timeAfterSoftCap;
}
/* invest by sending ether to the contract. */
function() payable {
if (msg.sender != msWallet) //do not trigger investment if the wallet is returning the funds
invest(msg.sender);
}
/* make an investment
* only callable if the crowdsale started and hasn't been closed already and the maxGoal wasn't reached yet.
* the current token price is looked up and the corresponding number of tokens is transfered to the receiver.
* the sent value is directly forwarded to a safe wallet.
* this method allows to purchase tokens in behalf of another address.*/
function invest(address _receiver) payable {
uint amount = msg.value;
var (numTokens, reachedSoftCap) = getNumTokens(amount);
require(numTokens>0);
require(!crowdsaleClosed && now >= start && now <= end && safeAdd(tokensSold, numTokens) <= maxGoal);
msWallet.transfer(amount);
balanceOf[_receiver] = safeAdd(balanceOf[_receiver], amount);
amountRaised = safeAdd(amountRaised, amount);
tokensSold += numTokens;
assert(tokenReward.transferFrom(tokenOwner, _receiver, numTokens));
FundTransfer(_receiver, amount, true, amountRaised);
if (reachedSoftCap) {
uint newEnd = now + timeAfterSoftCap;
if (newEnd < end) {
end = newEnd;
tokenReward.setStart(newEnd);
}
}
}
function getNumTokens(uint _value) constant returns(uint numTokens, bool reachedSoftCap) {
if (tokensSold < softCap) {
numTokens = safeMul(_value,rateSoft)/rateCoefficient;
if (safeAdd(tokensSold,numTokens) < softCap)
return (numTokens, false);
else if (safeAdd(tokensSold,numTokens) == softCap)
return (numTokens, true);
else {
numTokens = safeSub(softCap, tokensSold);
uint missing = safeSub(_value, safeMul(numTokens,rateCoefficient)/rateSoft);
return (safeAdd(numTokens, safeMul(missing,rateHard)/rateCoefficient), true);
}
}
else
return (safeMul(_value,rateHard)/rateCoefficient, false);
}
modifier afterDeadline() {
if (now > end)
_;
}
/* checks if the goal or time limit has been reached and ends the campaign */
function checkGoalReached() afterDeadline {
require(msg.sender == tokenOwner);
if (tokensSold >= fundingGoal) {
tokenReward.burn(); //burn remaining tokens but the reserved ones
GoalReached(tokenOwner, amountRaised);
}
crowdsaleClosed = true;
}
/* allows the funders to withdraw their funds if the goal has not been reached.
* only works after funds have been returned from the wallet. */
function safeWithdrawal() afterDeadline {
uint amount = balanceOf[msg.sender];
if (address(this).balance >= amount) {
balanceOf[msg.sender] = 0;
if (amount > 0) {
msg.sender.transfer(amount);
FundTransfer(msg.sender, amount, false, amountRaised);
}
}
}
}
{
"compilationTarget": {
"Crowdsale.sol": "Crowdsale"
},
"libraries": {},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"constant":false,"inputs":[],"name":"checkGoalReached","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_receiver","type":"address"}],"name":"invest","outputs":[],"payable":true,"type":"function"},{"constant":true,"inputs":[],"name":"rateSoft","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_value","type":"uint256"}],"name":"getNumTokens","outputs":[{"name":"numTokens","type":"uint256"},{"name":"reachedSoftCap","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"timeAfterSoftCap","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"tokensSold","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"tokenReward","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"fundingGoal","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"amountRaised","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"rateCoefficient","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"softCap","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"tokenOwner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"start","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"rateHard","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"crowdsaleClosed","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"end","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"maxGoal","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"safeWithdrawal","outputs":[],"payable":false,"type":"function"},{"inputs":[{"name":"_tokenAddr","type":"address"},{"name":"_walletAddr","type":"address"},{"name":"_tokenOwner","type":"address"},{"name":"_start","type":"uint256"},{"name":"_end","type":"uint256"},{"name":"_timeAfterSoftCap","type":"uint256"}],"payable":false,"type":"constructor"},{"payable":true,"type":"fallback"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_tokenOwner","type":"address"},{"indexed":false,"name":"_amountRaised","type":"uint256"}],"name":"GoalReached","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"backer","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"isContribution","type":"bool"},{"indexed":false,"name":"_amountRaised","type":"uint256"}],"name":"FundTransfer","type":"event"}]