pragma solidity 0.4.18;
contract CrowdsaleParameters {
// Accounts (2017-11-30)
address internal constant presalePoolAddress = 0xF373BfD05C8035bE6dcB44CABd17557e49D5364C;
address internal constant foundersAddress = 0x0ED375dd94c878703147580F044B6B1CE6a7F053;
address internal constant incentiveReserveAddress = 0xD34121E853af290e61a0F0313B99abb24D4Dc6ea;
address internal constant generalSaleAddress = 0xC107EC2077BA7d65944267B64F005471A6c05692;
address internal constant lotteryAddress = 0x98631b688Bcf78D233C48E464fCfe6dC7aBd32A7;
address internal constant marketingAddress = 0x2C1C916a4aC3d0f2442Fe0A9b9e570eB656582d8;
// PreICO and Main sale ICO Timing per requirements
uint256 internal constant presaleStartDate = 1512121500; // 2017-12-01 09:45 GMT
uint256 internal constant presaleEndDate = 1513382430; // 2017-12-16 00:00:30 GMT
uint256 internal constant generalSaleStartDate = 1515319200; // 2018-01-07 10:00 GMT
uint256 internal constant generalSaleEndDate = 1518602400; // 2018-02-14 10:00 GMT
}
contract TokenRecipient {
function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData) public;
}
contract Owned {
address public owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* Constructor
*
* Sets contract owner to address of constructor caller
*/
function Owned() public {
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
/**
* Change Owner
*
* Changes ownership of this contract. Only owner can call this method.
*
* @param newOwner - new owner's address
*/
function changeOwner(address newOwner) onlyOwner public {
require(newOwner != address(0));
require(newOwner != owner);
OwnershipTransferred(owner, newOwner);
owner = newOwner;
}
}
contract SeedToken is Owned, CrowdsaleParameters {
uint8 public decimals;
function totalSupply() public returns (uint256 result);
function balanceOf(address _address) public returns (uint256 balance);
function allowance(address _owner, address _spender) public returns (uint256 remaining);
function transfer(address _to, uint256 _value) public returns (bool success);
function approve(address _spender, uint256 _value) public returns (bool success);
function approveAndCall(address _spender, uint256 _value, bytes _extraData) public returns (bool success);
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success);
function accountBalance(address _address) public returns (uint256 balance);
}
contract LiveTreeCrowdsale is Owned, CrowdsaleParameters {
uint[] public ICOStagePeriod;
bool public icoClosedManually = false;
bool public allowRefunds = false;
uint public totalCollected = 0;
address private saleWalletAddress;
address private presaleWalletAddress;
uint private tokenMultiplier = 10;
SeedToken private tokenReward;
uint private reasonableCostsPercentage;
mapping (address => uint256) private investmentRecords;
event FundTransfer(address indexed _from, address indexed _to, uint _value);
event TokenTransfer(address indexed baker, uint tokenAmount, uint pricePerToken);
event Refund(address indexed backer, uint amount);
enum Stage { PreSale, GeneralSale, Inactive }
/**
* Constructor
*
* @param _tokenAddress - address of SED token (deployed before this contract)
*/
function LiveTreeCrowdsale(address _tokenAddress) public {
tokenReward = SeedToken(_tokenAddress);
tokenMultiplier = tokenMultiplier ** tokenReward.decimals();
saleWalletAddress = CrowdsaleParameters.generalSaleAddress;
presaleWalletAddress = CrowdsaleParameters.presalePoolAddress;
ICOStagePeriod.push(CrowdsaleParameters.presaleStartDate);
ICOStagePeriod.push(CrowdsaleParameters.presaleEndDate);
ICOStagePeriod.push(CrowdsaleParameters.generalSaleStartDate);
ICOStagePeriod.push(CrowdsaleParameters.generalSaleEndDate);
}
/**
* Get active stage (pre-sale or general sale)
*
* @return stage - active stage
*/
function getActiveStage() internal constant returns (Stage) {
if (ICOStagePeriod[0] <= now && now < ICOStagePeriod[1])
return Stage.PreSale;
if (ICOStagePeriod[2] <= now && now < ICOStagePeriod[3])
return Stage.GeneralSale;
return Stage.Inactive;
}
/**
* Process received payment
*
* Determine the number of tokens that was purchased considering current
* stage, bonus tier and remaining amount of tokens in the sale wallet.
* Transfer purchased tokens to bakerAddress and return unused portion of
* ether (change)
*
* @param bakerAddress - address that ether was sent from
* @param amount - amount of Wei received
*/
function processPayment(address bakerAddress, uint amount) internal {
// Check current stage, either pre-sale or general sale should be active
Stage currentStage = getActiveStage();
require(currentStage != Stage.Inactive);
// Validate if ICO is not closed manually or reached the threshold
require(!icoClosedManually);
// Before Metropolis update require will not refund gas, but
// for some reason require statement around msg.value always throws
assert(amount > 0 finney);
// Validate that we received less than a billion ETH to prevent overflow
require(amount < 1e27);
// Tell everyone about the transfer
FundTransfer(bakerAddress, address(this), amount);
// Calculate tokens per ETH for this tier
uint tokensPerEth = 1130;
if (amount < 1.5 ether)
tokensPerEth = 1000;
else if (amount < 3 ether)
tokensPerEth = 1005;
else if (amount < 5 ether)
tokensPerEth = 1010;
else if (amount < 7 ether)
tokensPerEth = 1015;
else if (amount < 10 ether)
tokensPerEth = 1020;
else if (amount < 15 ether)
tokensPerEth = 1025;
else if (amount < 20 ether)
tokensPerEth = 1030;
else if (amount < 30 ether)
tokensPerEth = 1035;
else if (amount < 50 ether)
tokensPerEth = 1040;
else if (amount < 75 ether)
tokensPerEth = 1045;
else if (amount < 100 ether)
tokensPerEth = 1050;
else if (amount < 150 ether)
tokensPerEth = 1055;
else if (amount < 250 ether)
tokensPerEth = 1060;
else if (amount < 350 ether)
tokensPerEth = 1070;
else if (amount < 500 ether)
tokensPerEth = 1075;
else if (amount < 750 ether)
tokensPerEth = 1080;
else if (amount < 1000 ether)
tokensPerEth = 1090;
else if (amount < 1500 ether)
tokensPerEth = 1100;
else if (amount < 2000 ether)
tokensPerEth = 1110;
else if (amount < 3500 ether)
tokensPerEth = 1120;
if (currentStage == Stage.PreSale)
tokensPerEth = tokensPerEth * 2;
// Calculate token amount that is purchased,
// truncate to integer
uint weiPerEth = 1e18;
uint tokenAmount = amount * tokensPerEth * tokenMultiplier / weiPerEth;
// Check that stage wallet has enough tokens. If not, sell the rest and
// return change.
address tokenSaleWallet = currentStage == Stage.PreSale ? presaleWalletAddress : saleWalletAddress;
uint remainingTokenBalance = tokenReward.accountBalance(tokenSaleWallet);
if (remainingTokenBalance < tokenAmount) {
tokenAmount = remainingTokenBalance;
}
// Calculate Wei amount that was received in this transaction
// adjusted to rounding and remaining token amount
uint acceptedAmount = tokenAmount * weiPerEth / (tokensPerEth * tokenMultiplier);
// Transfer tokens to baker and return ETH change
tokenReward.transferFrom(tokenSaleWallet, bakerAddress, tokenAmount);
TokenTransfer(bakerAddress, tokenAmount, tokensPerEth);
uint change = amount - acceptedAmount;
if (change > 0) {
if (bakerAddress.send(change)) {
FundTransfer(address(this), bakerAddress, change);
}
else
revert();
}
// Update crowdsale performance
investmentRecords[bakerAddress] += acceptedAmount;
totalCollected += acceptedAmount;
}
/**
* Change pre-sale end date
*
* @param endDate - end date of pre-sale in milliseconds from unix epoch
*/
function changePresaleEndDate(uint256 endDate) external onlyOwner {
require(ICOStagePeriod[0] < endDate);
require(ICOStagePeriod[2] >= endDate);
ICOStagePeriod[1] = endDate;
}
/**
* Change general sale start date
*
* @param startDate - start date of general sale in milliseconds from unix epoch
*/
function changeGeneralSaleStartDate(uint256 startDate) external onlyOwner {
require(now < startDate);
require(ICOStagePeriod[1] <= startDate);
ICOStagePeriod[2] = startDate;
}
/**
* Change general sale end date
*
* @param endDate - end date of general sale in milliseconds from unix epoch
*/
function changeGeneralSaleEndDate(uint256 endDate) external onlyOwner {
require(ICOStagePeriod[2] < endDate);
ICOStagePeriod[3] = endDate;
}
/**
* Stop ICO manually
*/
function pauseICO() external onlyOwner {
require(!icoClosedManually);
icoClosedManually = true;
}
/**
* Reopen ICO
*/
function unpauseICO() external onlyOwner {
require(icoClosedManually);
icoClosedManually = false;
}
/**
* Close main sale and destroy unsold tokens
*/
function closeMainSaleICO() external onlyOwner {
var amountToDestroy = tokenReward.balanceOf(CrowdsaleParameters.generalSaleAddress);
tokenReward.transferFrom(CrowdsaleParameters.generalSaleAddress, 0, amountToDestroy);
ICOStagePeriod[3] = now;
TokenTransfer(0, amountToDestroy, 0);
}
/**
* Close pre ICO and transfer all unsold tokens to main sale wallet
*/
function closePreICO() external onlyOwner {
var amountToTransfer = tokenReward.balanceOf(CrowdsaleParameters.presalePoolAddress);
ICOStagePeriod[1] = now;
tokenReward.transferFrom(CrowdsaleParameters.presalePoolAddress, CrowdsaleParameters.generalSaleAddress, amountToTransfer);
}
/**
* Allow or disallow refunds
*
* @param value - if true, refunds will be allowed; if false, disallowed
* @param _reasonableCostsPercentage - non-refundable fraction of total
* collections in tens of a percent. Valid range is 0 to 1000:
* 0 = 0.0%, 123 = 12.3%, 1000 = 100.0%
*/
function setAllowRefunds(bool value, uint _reasonableCostsPercentage) external onlyOwner {
require(isICOClosed());
require(_reasonableCostsPercentage >= 1 && _reasonableCostsPercentage <= 999);
allowRefunds = value;
reasonableCostsPercentage = _reasonableCostsPercentage;
}
/**
* Transfer ETH amount from contract to owner's address.
*
* @param amount - ETH amount to transfer in Wei
*/
function safeWithdrawal(uint amount) external onlyOwner {
require(this.balance >= amount);
if (owner.send(amount))
FundTransfer(address(this), owner, amount);
}
/**
function
* Is ICO closed (either closed manually or not started)
*
* @return true if ICO is closed manually or stage is "Inactive", otherwise false
*/
function isICOClosed() public constant returns (bool closed) {
Stage currentStage = getActiveStage();
return icoClosedManually || currentStage == Stage.Inactive;
}
/**
* Default method
*
* Processes all ETH that it receives and credits SED tokens to sender
* according to current stage and tier bonus
*/
function () external payable {
processPayment(msg.sender, msg.value);
}
/**
* Kill method
*
* Destructs this contract
*/
function kill() external onlyOwner {
require(isICOClosed());
selfdestruct(owner);
}
/**
* Refund
*
* Sends a partial refund to the sender who calls this method.
* Fraction of collected amount will not be refunded
*/
function refund() external {
require(isICOClosed() && allowRefunds && investmentRecords[msg.sender] > 0);
var amountToReturn = investmentRecords[msg.sender] * (1000 - reasonableCostsPercentage) / 1000;
require(this.balance >= amountToReturn);
investmentRecords[msg.sender] = 0;
msg.sender.transfer(amountToReturn);
Refund(msg.sender, amountToReturn);
}
}
{
"compilationTarget": {
"LiveTreeCrowdsale.sol": "LiveTreeCrowdsale"
},
"libraries": {},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"constant":false,"inputs":[{"name":"endDate","type":"uint256"}],"name":"changeGeneralSaleEndDate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"ICOStagePeriod","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"endDate","type":"uint256"}],"name":"changePresaleEndDate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"pauseICO","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"kill","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"refund","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"allowRefunds","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"amount","type":"uint256"}],"name":"safeWithdrawal","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"unpauseICO","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"value","type":"bool"},{"name":"_reasonableCostsPercentage","type":"uint256"}],"name":"setAllowRefunds","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"closePreICO","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"changeOwner","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"icoClosedManually","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"isICOClosed","outputs":[{"name":"closed","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"closeMainSaleICO","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalCollected","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"startDate","type":"uint256"}],"name":"changeGeneralSaleStartDate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_tokenAddress","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_from","type":"address"},{"indexed":true,"name":"_to","type":"address"},{"indexed":false,"name":"_value","type":"uint256"}],"name":"FundTransfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"baker","type":"address"},{"indexed":false,"name":"tokenAmount","type":"uint256"},{"indexed":false,"name":"pricePerToken","type":"uint256"}],"name":"TokenTransfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"backer","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"Refund","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"}]