pragma solidity ^0.4.18;
/// @title Ownable contract
library SafeMath {
function mul(uint256 a, uint256 b) internal constant returns (uint256) {
uint256 c = a * b;
assert(a == 0 || c / a == b);
return c;
}
function div(uint256 a, uint256 b) internal constant returns (uint256) {
uint256 c = a / b;
return c;
}
function sub(uint256 a, uint256 b) internal constant returns (uint256) {
assert(b <= a);
return a - b;
}
function add(uint256 a, uint256 b) internal constant returns (uint256) {
uint256 c = a + b;
assert(c >= a);
return c;
}
}
/// @title Ownable contract
contract Ownable {
address public owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
function Ownable() public {
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
/// @dev Change ownership
/// @param newOwner Address of the new owner
function transferOwnership(address newOwner) onlyOwner public {
require(newOwner != address(0));
OwnershipTransferred(owner, newOwner);
owner = newOwner;
}
}
/// @title ERC20 contract
/// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md
contract ERC20 {
uint public totalSupply;
function balanceOf(address who) public constant returns (uint);
function transfer(address to, uint value) public returns (bool);
event Transfer(address indexed from, address indexed to, uint value);
function allowance(address owner, address spender) public constant returns (uint);
function transferFrom(address from, address to, uint value) public returns (bool);
function approve(address spender, uint value) public returns (bool);
event Approval(address indexed owner, address indexed spender, uint value);
}
/// @title ExtendedERC20 contract
contract ExtendedERC20 is ERC20 {
function mint(address _to, uint _amount) public returns (bool);
}
/// @title WizzleInfinityHelper contract
contract WizzleInfinityHelper {
function isWhitelisted(address addr) public constant returns (bool);
}
/// @title Crowdsale contract
contract Crowdsale is Ownable {
using SafeMath for uint256;
/// Token reference
ExtendedERC20 public token;
/// WizzleInfinityHelper reference - helper for whitelisting
WizzleInfinityHelper public helper;
/// Presale start time (inclusive)
uint256 public startTimePre;
/// Presale end time (inclusive)
uint256 public endTimePre;
/// ICO start time (inclusive)
uint256 public startTimeIco;
/// ICO end time (inclusive)
uint256 public endTimeIco;
/// Address where the funds will be collected
address public wallet;
/// EUR per 1 ETH rate
uint32 public rate;
/// Amount of tokens sold in presale
uint256 public tokensSoldPre;
/// Amount of tokens sold in ICO
uint256 public tokensSoldIco;
/// Amount of raised ethers expressed in weis
uint256 public weiRaised;
/// Number of contributors
uint256 public contributors;
/// Presale cap
uint256 public preCap;
/// ICO cap
uint256 public icoCap;
/// Presale discount percentage
uint8 public preDiscountPercentage;
/// Amount of tokens in ICO discount level 1
uint256 public icoDiscountLevel1;
/// Amount of tokens in ICO discount level 2
uint256 public icoDiscountLevel2;
/// ICO discount percentage 1
uint8 public icoDiscountPercentageLevel1;
/// ICO discount percentage 2
uint8 public icoDiscountPercentageLevel2;
/// ICO discount percentage 3
uint8 public icoDiscountPercentageLevel3;
function Crowdsale(uint256 _startTimePre, uint256 _endTimePre, uint256 _startTimeIco, uint256 _endTimeIco, uint32 _rate, address _wallet, address _tokenAddress, address _helperAddress) {
require(_startTimePre >= now);
require(_endTimePre >= _startTimePre);
require(_startTimeIco >= _endTimePre);
require(_endTimeIco >= _startTimeIco);
require(_rate > 0);
require(_wallet != address(0));
require(_tokenAddress != address(0));
require(_helperAddress != address(0));
startTimePre = _startTimePre;
endTimePre = _endTimePre;
startTimeIco = _startTimeIco;
endTimeIco = _endTimeIco;
rate = _rate;
wallet = _wallet;
token = ExtendedERC20(_tokenAddress);
helper = WizzleInfinityHelper(_helperAddress);
preCap = 1500 * 10**24; // 1500m tokens
preDiscountPercentage = 50; // 50% discount
icoCap = 3450 * 10**24; // 3450m tokens (500m + 500m + 2450m)
icoDiscountLevel1 = 500 * 10**24; // 500m tokens
icoDiscountLevel2 = 500 * 10**24; // 500m tokens
icoDiscountPercentageLevel1 = 40; // 40% discount
icoDiscountPercentageLevel2 = 30; // 30% discount
icoDiscountPercentageLevel3 = 25; // 25% discount
}
/// @dev Set the rate of ETH - EUR
/// @param _rate Rate of ETH - EUR
function setRate(uint32 _rate) public onlyOwner {
require(_rate > 0);
rate = _rate;
}
/// @dev Fallback function for crowdsale contribution
function () payable {
buyTokens(msg.sender);
}
/// @dev Buy tokens function
/// @param beneficiary Address which will receive the tokens
function buyTokens(address beneficiary) public payable {
require(beneficiary != address(0));
require(helper.isWhitelisted(beneficiary));
uint256 weiAmount = msg.value;
require(weiAmount > 0);
uint256 tokenAmount = 0;
if (isPresale()) {
/// Minimum contribution of 1 ether during presale
require(weiAmount >= 1 ether);
tokenAmount = getTokenAmount(weiAmount, preDiscountPercentage);
uint256 newTokensSoldPre = tokensSoldPre.add(tokenAmount);
require(newTokensSoldPre <= preCap);
tokensSoldPre = newTokensSoldPre;
} else if (isIco()) {
uint8 discountPercentage = getIcoDiscountPercentage();
tokenAmount = getTokenAmount(weiAmount, discountPercentage);
/// Minimum contribution 1 token during ICO
require(tokenAmount >= 10**18);
uint256 newTokensSoldIco = tokensSoldIco.add(tokenAmount);
require(newTokensSoldIco <= icoCap);
tokensSoldIco = newTokensSoldIco;
} else {
/// Stop execution and return remaining gas
require(false);
}
executeTransaction(beneficiary, weiAmount, tokenAmount);
}
/// @dev Internal function used for calculating ICO discount percentage depending on levels
function getIcoDiscountPercentage() internal constant returns (uint8) {
if (tokensSoldIco <= icoDiscountLevel1) {
return icoDiscountPercentageLevel1;
} else if (tokensSoldIco <= icoDiscountLevel1.add(icoDiscountLevel2)) {
return icoDiscountPercentageLevel2;
} else {
return icoDiscountPercentageLevel3; //for everything else
}
}
/// @dev Internal function used to calculate amount of tokens based on discount percentage
function getTokenAmount(uint256 weiAmount, uint8 discountPercentage) internal constant returns (uint256) {
/// Less than 100 to avoid division with zero
require(discountPercentage >= 0 && discountPercentage < 100);
uint256 baseTokenAmount = weiAmount.mul(rate);
uint256 tokenAmount = baseTokenAmount.mul(10000).div(100 - discountPercentage);
return tokenAmount;
}
/// @dev Internal function for execution of crowdsale transaction and proper logging used by payable functions
function executeTransaction(address beneficiary, uint256 weiAmount, uint256 tokenAmount) internal {
weiRaised = weiRaised.add(weiAmount);
token.mint(beneficiary, tokenAmount);
TokenPurchase(msg.sender, beneficiary, weiAmount, tokenAmount);
contributors = contributors.add(1);
wallet.transfer(weiAmount);
}
/// @dev Used to change presale cap (maximum tokens sold during presale)
/// @param _preCap Presale cap
function changePresaleCap(uint256 _preCap) public onlyOwner {
require(_preCap > 0);
PresaleCapChanged(owner, _preCap);
preCap = _preCap;
}
/// @dev Used to change presale discount percentage
/// @param _preDiscountPercentage Presale discount percentage
function changePresaleDiscountPercentage(uint8 _preDiscountPercentage) public onlyOwner {
require(_preDiscountPercentage >= 0 && _preDiscountPercentage < 100);
PresaleDiscountPercentageChanged(owner, _preDiscountPercentage);
preDiscountPercentage = _preDiscountPercentage;
}
/// @dev Used to change presale time
/// @param _startTimePre Start time of presale
/// @param _endTimePre End time of presale
function changePresaleTimeRange(uint256 _startTimePre, uint256 _endTimePre) public onlyOwner {
require(_endTimePre >= _startTimePre);
PresaleTimeRangeChanged(owner, _startTimePre, _endTimePre);
startTimePre = _startTimePre;
endTimePre = _endTimePre;
}
/// @dev Used to change ICO cap in case the hard cap has been reached
/// @param _icoCap ICO cap
function changeIcoCap(uint256 _icoCap) public onlyOwner {
require(_icoCap > 0);
IcoCapChanged(owner, _icoCap);
icoCap = _icoCap;
}
/// @dev Used to change time of ICO
/// @param _startTimeIco Start time of ICO
/// @param _endTimeIco End time of ICO
function changeIcoTimeRange(uint256 _startTimeIco, uint256 _endTimeIco) public onlyOwner {
require(_endTimeIco >= _startTimeIco);
IcoTimeRangeChanged(owner, _startTimeIco, _endTimeIco);
startTimeIco = _startTimeIco;
endTimeIco = _endTimeIco;
}
/// @dev Change amount of tokens in discount phases
/// @param _icoDiscountLevel1 Amount of tokens in first phase
/// @param _icoDiscountLevel2 Amount of tokens in second phase
function changeIcoDiscountLevels(uint256 _icoDiscountLevel1, uint256 _icoDiscountLevel2) public onlyOwner {
require(_icoDiscountLevel1 > 0 && _icoDiscountLevel2 > 0);
IcoDiscountLevelsChanged(owner, _icoDiscountLevel1, _icoDiscountLevel2);
icoDiscountLevel1 = _icoDiscountLevel1;
icoDiscountLevel2 = _icoDiscountLevel2;
}
/// @dev Change discount percentages for different phases
/// @param _icoDiscountPercentageLevel1 Discount percentage of phase 1
/// @param _icoDiscountPercentageLevel2 Discount percentage of phase 2
/// @param _icoDiscountPercentageLevel3 Discount percentage of phase 3
function changeIcoDiscountPercentages(uint8 _icoDiscountPercentageLevel1, uint8 _icoDiscountPercentageLevel2, uint8 _icoDiscountPercentageLevel3) public onlyOwner {
require(_icoDiscountPercentageLevel1 >= 0 && _icoDiscountPercentageLevel1 < 100);
require(_icoDiscountPercentageLevel2 >= 0 && _icoDiscountPercentageLevel2 < 100);
require(_icoDiscountPercentageLevel3 >= 0 && _icoDiscountPercentageLevel3 < 100);
IcoDiscountPercentagesChanged(owner, _icoDiscountPercentageLevel1, _icoDiscountPercentageLevel2, _icoDiscountPercentageLevel3);
icoDiscountPercentageLevel1 = _icoDiscountPercentageLevel1;
icoDiscountPercentageLevel2 = _icoDiscountPercentageLevel2;
icoDiscountPercentageLevel3 = _icoDiscountPercentageLevel3;
}
/// @dev Check if presale is active
function isPresale() public constant returns (bool) {
return now >= startTimePre && now <= endTimePre;
}
/// @dev Check if ICO is active
function isIco() public constant returns (bool) {
return now >= startTimeIco && now <= endTimeIco;
}
/// @dev Check if presale has ended
function hasPresaleEnded() public constant returns (bool) {
return now > endTimePre;
}
/// @dev Check if ICO has ended
function hasIcoEnded() public constant returns (bool) {
return now > endTimeIco;
}
/// @dev Amount of tokens that have been sold during both presale and ICO phase
function cummulativeTokensSold() public constant returns (uint256) {
return tokensSoldPre + tokensSoldIco;
}
/// @dev Function to extract mistakenly sent ERC20 tokens sent to Crowdsale contract
/// @param _token Address of token we want to extract
function claimTokens(address _token) public onlyOwner {
if (_token == address(0)) {
owner.transfer(this.balance);
return;
}
ERC20 erc20Token = ERC20(_token);
uint balance = erc20Token.balanceOf(this);
erc20Token.transfer(owner, balance);
ClaimedTokens(_token, owner, balance);
}
/// Events
event TokenPurchase(address indexed _purchaser, address indexed _beneficiary, uint256 _value, uint256 _amount);
event PresaleTimeRangeChanged(address indexed _owner, uint256 _startTimePre, uint256 _endTimePre);
event PresaleCapChanged(address indexed _owner, uint256 _preCap);
event PresaleDiscountPercentageChanged(address indexed _owner, uint8 _preDiscountPercentage);
event IcoCapChanged(address indexed _owner, uint256 _icoCap);
event IcoTimeRangeChanged(address indexed _owner, uint256 _startTimeIco, uint256 _endTimeIco);
event IcoDiscountLevelsChanged(address indexed _owner, uint256 _icoDiscountLevel1, uint256 _icoDiscountLevel2);
event IcoDiscountPercentagesChanged(address indexed _owner, uint8 _icoDiscountPercentageLevel1, uint8 _icoDiscountPercentageLevel2, uint8 _icoDiscountPercentageLevel3);
event ClaimedTokens(address indexed _token, address indexed _owner, uint _amount);
}
/// @title WizzleInfinityTokenCrowdsale contract
contract WizzleInfinityTokenCrowdsale is Crowdsale {
function WizzleInfinityTokenCrowdsale(uint256 _startTimePre, uint256 _endTimePre, uint256 _startTimeIco, uint256 _endTimeIco, uint32 _rate, address _wallet, address _tokenAddress, address _helperAddress)
Crowdsale(_startTimePre, _endTimePre, _startTimeIco, _endTimeIco, _rate, _wallet, _tokenAddress, _helperAddress) public {
}
}
{
"compilationTarget": {
"WizzleInfinityTokenCrowdsale.sol": "WizzleInfinityTokenCrowdsale"
},
"libraries": {},
"optimizer": {
"enabled": false,
"runs": 200
},
"remappings": []
}
[{"constant":false,"inputs":[{"name":"_startTimePre","type":"uint256"},{"name":"_endTimePre","type":"uint256"}],"name":"changePresaleTimeRange","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"startTimePre","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"icoDiscountLevel1","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"icoDiscountPercentageLevel3","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"icoDiscountPercentageLevel1","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"rate","outputs":[{"name":"","type":"uint32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_startTimeIco","type":"uint256"},{"name":"_endTimeIco","type":"uint256"}],"name":"changeIcoTimeRange","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"startTimeIco","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"weiRaised","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tokensSoldPre","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"endTimeIco","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"wallet","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"hasPresaleEnded","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"preCap","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"helper","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tokensSoldIco","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"contributors","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"hasIcoEnded","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"icoDiscountLevel2","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"endTimePre","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_icoDiscountPercentageLevel1","type":"uint8"},{"name":"_icoDiscountPercentageLevel2","type":"uint8"},{"name":"_icoDiscountPercentageLevel3","type":"uint8"}],"name":"changeIcoDiscountPercentages","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"isPresale","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_preDiscountPercentage","type":"uint8"}],"name":"changePresaleDiscountPercentage","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"icoDiscountPercentageLevel2","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_icoCap","type":"uint256"}],"name":"changeIcoCap","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_preCap","type":"uint256"}],"name":"changePresaleCap","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"cummulativeTokensSold","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"icoCap","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"preDiscountPercentage","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_token","type":"address"}],"name":"claimTokens","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_icoDiscountLevel1","type":"uint256"},{"name":"_icoDiscountLevel2","type":"uint256"}],"name":"changeIcoDiscountLevels","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"beneficiary","type":"address"}],"name":"buyTokens","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_rate","type":"uint32"}],"name":"setRate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"token","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"isIco","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_startTimePre","type":"uint256"},{"name":"_endTimePre","type":"uint256"},{"name":"_startTimeIco","type":"uint256"},{"name":"_endTimeIco","type":"uint256"},{"name":"_rate","type":"uint32"},{"name":"_wallet","type":"address"},{"name":"_tokenAddress","type":"address"},{"name":"_helperAddress","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_purchaser","type":"address"},{"indexed":true,"name":"_beneficiary","type":"address"},{"indexed":false,"name":"_value","type":"uint256"},{"indexed":false,"name":"_amount","type":"uint256"}],"name":"TokenPurchase","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_owner","type":"address"},{"indexed":false,"name":"_startTimePre","type":"uint256"},{"indexed":false,"name":"_endTimePre","type":"uint256"}],"name":"PresaleTimeRangeChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_owner","type":"address"},{"indexed":false,"name":"_preCap","type":"uint256"}],"name":"PresaleCapChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_owner","type":"address"},{"indexed":false,"name":"_preDiscountPercentage","type":"uint8"}],"name":"PresaleDiscountPercentageChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_owner","type":"address"},{"indexed":false,"name":"_icoCap","type":"uint256"}],"name":"IcoCapChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_owner","type":"address"},{"indexed":false,"name":"_startTimeIco","type":"uint256"},{"indexed":false,"name":"_endTimeIco","type":"uint256"}],"name":"IcoTimeRangeChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_owner","type":"address"},{"indexed":false,"name":"_icoDiscountLevel1","type":"uint256"},{"indexed":false,"name":"_icoDiscountLevel2","type":"uint256"}],"name":"IcoDiscountLevelsChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_owner","type":"address"},{"indexed":false,"name":"_icoDiscountPercentageLevel1","type":"uint8"},{"indexed":false,"name":"_icoDiscountPercentageLevel2","type":"uint8"},{"indexed":false,"name":"_icoDiscountPercentageLevel3","type":"uint8"}],"name":"IcoDiscountPercentagesChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_token","type":"address"},{"indexed":true,"name":"_owner","type":"address"},{"indexed":false,"name":"_amount","type":"uint256"}],"name":"ClaimedTokens","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"}]