pragma solidity ^0.4.13; /* taking ideas from FirstBlood token */ contract SafeMath { function safeAdd(uint256 x, uint256 y) internal returns (uint256) { uint256 z = x + y; assert((z >= x) && (z >= y)); return z; } function safeSubtract(uint256 x, uint256 y) internal returns (uint256) { assert(x >= y); uint256 z = x - y; return z; } function safeMult(uint256 x, uint256 y) internal returns (uint256) { uint256 z = x * y; assert((x == 0) || (z / x == y)); return z; } } /* * Ownable * * Base contract with an owner. * Provides onlyOwner modifier, which prevents function from running if it is called by anyone other than the owner. */ contract Ownable { address public owner; function Ownable() { owner = msg.sender; } modifier onlyOwner() { require(msg.sender == owner); _; } function transferOwnership(address newOwner) onlyOwner { if (newOwner != address(0)) { owner = newOwner; } } } /* * Haltable * * Abstract contract that allows children to implement an * emergency stop mechanism. Differs from Pausable by causing a throw when in halt mode. * * * Originally envisioned in FirstBlood ICO contract. */ contract Haltable is Ownable { bool public halted; modifier stopInEmergency { require(!halted); _; } modifier onlyInEmergency { require(halted); _; } // called by the owner on emergency, triggers stopped state function halt() external onlyOwner { halted = true; } // called by the owner on end of emergency, returns to normal state function unhalt() external onlyOwner onlyInEmergency { halted = false; } } contract FluencePreSale is Haltable, SafeMath { mapping (address => uint256) public balanceOf; /*/ * Constants /*/ string public constant name = "Fluence Presale Token"; string public constant symbol = "FPT"; uint public constant decimals = 18; // 6% of tokens uint256 public constant SUPPLY_LIMIT = 6000000 ether; // What is given to contributors, <= SUPPLY_LIMIT uint256 public totalSupply; // If soft cap is not reached, refund process is started uint256 public softCap = 1000 ether; // Basic price uint256 public constant basicThreshold = 500 finney; uint public constant basicTokensPerEth = 1500; // Advanced price uint256 public constant advancedThreshold = 5 ether; uint public constant advancedTokensPerEth = 2250; // Expert price uint256 public constant expertThreshold = 100 ether; uint public constant expertTokensPerEth = 3000; // As we have different prices for different amounts, // we keep a mapping of contributions to make refund mapping (address => uint256) public etherContributions; // Max balance of the contract uint256 public etherCollected; // Address to withdraw ether to address public beneficiary; uint public startAtBlock; uint public endAtBlock; // All tokens are sold event GoalReached(uint amountRaised); // Minimal ether cap collected event SoftCapReached(uint softCap); // New contribution received and tokens are issued event NewContribution(address indexed holder, uint256 tokenAmount, uint256 etherAmount); // Ether is taken back event Refunded(address indexed holder, uint256 amount); // If soft cap is reached, withdraw should be available modifier softCapReached { if (etherCollected < softCap) { revert(); } assert(etherCollected >= softCap); _; } // Allow contribution only during presale modifier duringPresale { if (block.number < startAtBlock || block.number > endAtBlock || totalSupply >= SUPPLY_LIMIT) { revert(); } assert(block.number >= startAtBlock && block.number <= endAtBlock && totalSupply < SUPPLY_LIMIT); _; } // Allow withdraw only during refund modifier duringRefund { if(block.number <= endAtBlock || etherCollected >= softCap || this.balance == 0) { revert(); } assert(block.number > endAtBlock && etherCollected < softCap && this.balance > 0); _; } function FluencePreSale(uint _startAtBlock, uint _endAtBlock, uint softCapInEther){ require(_startAtBlock > 0 && _endAtBlock > 0); beneficiary = msg.sender; startAtBlock = _startAtBlock; endAtBlock = _endAtBlock; softCap = softCapInEther * 1 ether; } // Change beneficiary address function setBeneficiary(address to) onlyOwner external { require(to != address(0)); beneficiary = to; } // Withdraw contract's balance to beneficiary account function withdraw() onlyOwner softCapReached external { require(this.balance > 0); beneficiary.transfer(this.balance); } // Process contribution, issue tokens to user function contribute(address _address) private stopInEmergency duringPresale { if(msg.value < basicThreshold && owner != _address) { revert(); } assert(msg.value >= basicThreshold || owner == _address); // Minimal contribution uint256 tokensToIssue; if (msg.value >= expertThreshold) { tokensToIssue = safeMult(msg.value, expertTokensPerEth); } else if (msg.value >= advancedThreshold) { tokensToIssue = safeMult(msg.value, advancedTokensPerEth); } else { tokensToIssue = safeMult(msg.value, basicTokensPerEth); } assert(tokensToIssue > 0); totalSupply = safeAdd(totalSupply, tokensToIssue); // Goal is already reached, can't issue any more tokens if(totalSupply > SUPPLY_LIMIT) { revert(); } assert(totalSupply <= SUPPLY_LIMIT); // Saving ether contributions for the case of refund etherContributions[_address] = safeAdd(etherContributions[_address], msg.value); // Track ether before adding current contribution to notice the event of reaching soft cap uint collectedBefore = etherCollected; etherCollected = safeAdd(etherCollected, msg.value); // Tokens are issued balanceOf[_address] = safeAdd(balanceOf[_address], tokensToIssue); NewContribution(_address, tokensToIssue, msg.value); if (totalSupply == SUPPLY_LIMIT) { GoalReached(etherCollected); } if (etherCollected >= softCap && collectedBefore < softCap) { SoftCapReached(etherCollected); } } function() external payable { contribute(msg.sender); } function refund() stopInEmergency duringRefund external { uint tokensToBurn = balanceOf[msg.sender]; // Sender must have tokens require(tokensToBurn > 0); // Burn balanceOf[msg.sender] = 0; // User contribution amount uint amount = etherContributions[msg.sender]; // Amount must be positive -- refund is not processed yet assert(amount > 0); etherContributions[msg.sender] = 0; // Clear state // Reduce counters etherCollected = safeSubtract(etherCollected, amount); totalSupply = safeSubtract(totalSupply, tokensToBurn); // Process refund. In case of error, it will be thrown msg.sender.transfer(amount); Refunded(msg.sender, amount); } }