smartcontracts/contracts/FluencePreSale.sol

285 lines
7.5 KiB
Solidity
Raw Permalink Normal View History

2017-08-07 17:13:21 +03:00
pragma solidity ^0.4.13;
2017-08-08 22:35:10 +03:00
/* taking ideas from FirstBlood token */
contract SafeMath {
2017-08-07 17:13:21 +03:00
2017-08-11 21:12:44 +03:00
function safeAdd(uint256 x, uint256 y) internal returns (uint256) {
2017-08-08 22:35:10 +03:00
uint256 z = x + y;
assert((z >= x) && (z >= y));
return z;
2017-08-07 17:13:21 +03:00
}
2017-08-11 21:12:44 +03:00
function safeSubtract(uint256 x, uint256 y) internal returns (uint256) {
2017-08-08 22:35:10 +03:00
assert(x >= y);
uint256 z = x - y;
return z;
2017-08-07 17:13:21 +03:00
}
2017-08-11 21:12:44 +03:00
function safeMult(uint256 x, uint256 y) internal returns (uint256) {
2017-08-08 22:35:10 +03:00
uint256 z = x * y;
2017-08-11 21:12:44 +03:00
assert((x == 0) || (z / x == y));
2017-08-08 22:35:10 +03:00
return z;
2017-08-07 17:13:21 +03:00
}
}
/*
* 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;
}
}
2017-08-08 22:35:10 +03:00
contract FluencePreSale is Haltable, SafeMath {
2017-08-07 17:13:21 +03:00
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
2017-08-09 12:19:24 +03:00
uint256 public constant SUPPLY_LIMIT = 6000000 ether;
2017-08-07 17:13:21 +03:00
// 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
2017-08-07 17:50:16 +03:00
uint256 public constant basicThreshold = 500 finney;
2017-08-11 21:12:44 +03:00
2017-08-07 17:13:21 +03:00
uint public constant basicTokensPerEth = 1500;
// Advanced price
uint256 public constant advancedThreshold = 5 ether;
2017-08-11 21:12:44 +03:00
2017-08-07 17:13:21 +03:00
uint public constant advancedTokensPerEth = 2250;
// Expert price
uint256 public constant expertThreshold = 100 ether;
2017-08-11 21:12:44 +03:00
2017-08-07 17:13:21 +03:00
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;
2017-08-11 21:12:44 +03:00
// All tokens are sold
2017-08-07 17:13:21 +03:00
event GoalReached(uint amountRaised);
2017-08-11 21:12:44 +03:00
// Minimal ether cap collected
2017-08-07 17:13:21 +03:00
event SoftCapReached(uint softCap);
2017-08-11 21:12:44 +03:00
// New contribution received and tokens are issued
2017-08-07 17:13:21 +03:00
event NewContribution(address indexed holder, uint256 tokenAmount, uint256 etherAmount);
2017-08-11 21:12:44 +03:00
// Ether is taken back
2017-08-07 17:13:21 +03:00
event Refunded(address indexed holder, uint256 amount);
// If soft cap is reached, withdraw should be available
modifier softCapReached {
2017-08-11 21:12:44 +03:00
if (etherCollected < softCap) {
revert();
}
assert(etherCollected >= softCap);
2017-08-07 17:13:21 +03:00
_;
}
2017-08-11 21:12:44 +03:00
// Allow contribution only during presale
2017-08-07 17:13:21 +03:00
modifier duringPresale {
2017-08-11 21:12:44 +03:00
if (block.number < startAtBlock || block.number > endAtBlock || totalSupply >= SUPPLY_LIMIT) {
revert();
}
assert(block.number >= startAtBlock && block.number <= endAtBlock && totalSupply < SUPPLY_LIMIT);
2017-08-07 17:13:21 +03:00
_;
}
2017-08-11 21:12:44 +03:00
// Allow withdraw only during refund
2017-08-07 17:13:21 +03:00
modifier duringRefund {
2017-08-11 21:12:44 +03:00
if(block.number <= endAtBlock || etherCollected >= softCap || this.balance == 0) {
revert();
}
assert(block.number > endAtBlock && etherCollected < softCap && this.balance > 0);
2017-08-07 17:13:21 +03:00
_;
}
2017-08-11 21:12:44 +03:00
function FluencePreSale(uint _startAtBlock, uint _endAtBlock, uint softCapInEther){
2017-08-07 17:13:21 +03:00
require(_startAtBlock > 0 && _endAtBlock > 0);
beneficiary = msg.sender;
startAtBlock = _startAtBlock;
endAtBlock = _endAtBlock;
2017-08-11 21:12:44 +03:00
softCap = softCapInEther * 1 ether;
2017-08-07 17:13:21 +03:00
}
2017-08-11 21:12:44 +03:00
// Change beneficiary address
2017-08-07 17:13:21 +03:00
function setBeneficiary(address to) onlyOwner external {
2017-08-08 22:35:10 +03:00
require(to != address(0));
2017-08-07 17:13:21 +03:00
beneficiary = to;
}
2017-08-11 21:12:44 +03:00
// Withdraw contract's balance to beneficiary account
2017-08-07 17:13:21 +03:00
function withdraw() onlyOwner softCapReached external {
require(this.balance > 0);
beneficiary.transfer(this.balance);
}
2017-08-11 21:12:44 +03:00
// Process contribution, issue tokens to user
2017-08-07 17:13:21 +03:00
function contribute(address _address) private stopInEmergency duringPresale {
2017-08-11 21:12:44 +03:00
if(msg.value < basicThreshold && owner != _address) {
revert();
}
assert(msg.value >= basicThreshold || owner == _address);
// Minimal contribution
2017-08-07 17:13:21 +03:00
uint256 tokensToIssue;
2017-08-11 21:12:44 +03:00
if (msg.value >= expertThreshold) {
2017-08-08 22:35:10 +03:00
tokensToIssue = safeMult(msg.value, expertTokensPerEth);
2017-08-11 21:12:44 +03:00
}
else if (msg.value >= advancedThreshold) {
2017-08-08 22:35:10 +03:00
tokensToIssue = safeMult(msg.value, advancedTokensPerEth);
2017-08-11 21:12:44 +03:00
}
else {
2017-08-08 22:35:10 +03:00
tokensToIssue = safeMult(msg.value, basicTokensPerEth);
2017-08-07 17:13:21 +03:00
}
2017-08-08 22:35:10 +03:00
assert(tokensToIssue > 0);
totalSupply = safeAdd(totalSupply, tokensToIssue);
2017-08-07 17:13:21 +03:00
2017-08-11 21:12:44 +03:00
// 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
2017-08-08 22:35:10 +03:00
etherContributions[_address] = safeAdd(etherContributions[_address], msg.value);
2017-08-11 21:12:44 +03:00
// Track ether before adding current contribution to notice the event of reaching soft cap
2017-08-07 17:13:21 +03:00
uint collectedBefore = etherCollected;
2017-08-08 22:35:10 +03:00
etherCollected = safeAdd(etherCollected, msg.value);
2017-08-11 21:12:44 +03:00
// Tokens are issued
2017-08-08 22:35:10 +03:00
balanceOf[_address] = safeAdd(balanceOf[_address], tokensToIssue);
2017-08-07 17:13:21 +03:00
NewContribution(_address, tokensToIssue, msg.value);
2017-08-11 21:12:44 +03:00
if (totalSupply == SUPPLY_LIMIT) {
2017-08-07 17:13:21 +03:00
GoalReached(etherCollected);
}
2017-08-11 21:12:44 +03:00
if (etherCollected >= softCap && collectedBefore < softCap) {
2017-08-07 17:13:21 +03:00
SoftCapReached(etherCollected);
}
}
function() external payable {
contribute(msg.sender);
}
function refund() stopInEmergency duringRefund external {
uint tokensToBurn = balanceOf[msg.sender];
2017-08-11 21:12:44 +03:00
// Sender must have tokens
require(tokensToBurn > 0);
// Burn
balanceOf[msg.sender] = 0;
// User contribution amount
uint amount = etherContributions[msg.sender];
2017-08-07 17:13:21 +03:00
2017-08-11 21:12:44 +03:00
// Amount must be positive -- refund is not processed yet
assert(amount > 0);
2017-08-07 17:13:21 +03:00
2017-08-11 21:12:44 +03:00
etherContributions[msg.sender] = 0;
// Clear state
2017-08-07 17:13:21 +03:00
// Reduce counters
2017-08-08 22:35:10 +03:00
etherCollected = safeSubtract(etherCollected, amount);
totalSupply = safeSubtract(totalSupply, tokensToBurn);
2017-08-07 17:13:21 +03:00
2017-08-11 21:12:44 +03:00
// Process refund. In case of error, it will be thrown
msg.sender.transfer(amount);
2017-08-07 17:13:21 +03:00
Refunded(msg.sender, amount);
}
}