/** *Submitted for verification at Etherscan.io on 2017-06-10 */ pragma solidity ^0.4.11; /* Overflow protected math functions */ contract SafeMath { /** constructor */ function SafeMath() { } /** @dev returns the sum of _x and _y, asserts if the calculation overflows @param _x value 1 @param _y value 2 @return sum */ function safeAdd(uint256 _x, uint256 _y) internal returns (uint256) { uint256 z = _x + _y; assert(z >= _x); return z; } /** @dev returns the difference of _x minus _y, asserts if the subtraction results in a negative number @param _x minuend @param _y subtrahend @return difference */ function safeSub(uint256 _x, uint256 _y) internal returns (uint256) { assert(_x >= _y); return _x - _y; } /** @dev returns the product of multiplying _x by _y, asserts if the calculation overflows @param _x factor 1 @param _y factor 2 @return product */ function safeMul(uint256 _x, uint256 _y) internal returns (uint256) { uint256 z = _x * _y; assert(_x == 0 || z / _x == _y); return z; } } /* Owned contract interface */ contract IOwned { // this function isn't abstract since the compiler emits automatically generated getter functions as external function owner() public constant returns (address owner) { owner; } function transferOwnership(address _newOwner) public; function acceptOwnership() public; } /* Provides support and utilities for contract ownership */ contract Owned is IOwned { address public owner; address public newOwner; event OwnerUpdate(address _prevOwner, address _newOwner); /** @dev constructor */ function Owned() { owner = msg.sender; } // allows execution by the owner only modifier ownerOnly { assert(msg.sender == owner); _; } /** @dev allows transferring the contract ownership the new owner still need to accept the transfer can only be called by the contract owner @param _newOwner new contract owner */ function transferOwnership(address _newOwner) public ownerOnly { require(_newOwner != owner); newOwner = _newOwner; } /** @dev used by a new owner to accept an ownership transfer */ function acceptOwnership() public { require(msg.sender == newOwner); OwnerUpdate(owner, newOwner); owner = newOwner; newOwner = 0x0; } } /* ERC20 Standard Token interface */ contract IERC20Token { // these functions aren't abstract since the compiler emits automatically generated getter functions as external function name() public constant returns (string name) { name; } function symbol() public constant returns (string symbol) { symbol; } function decimals() public constant returns (uint8 decimals) { decimals; } function totalSupply() public constant returns (uint256 totalSupply) { totalSupply; } function balanceOf(address _owner) public constant returns (uint256 balance) { _owner; balance; } function allowance(address _owner, address _spender) public constant returns (uint256 remaining) { _owner; _spender; remaining; } function transfer(address _to, uint256 _value) public returns (bool success); function transferFrom(address _from, address _to, uint256 _value) public returns (bool success); function approve(address _spender, uint256 _value) public returns (bool success); } /* Token Holder interface */ contract ITokenHolder is IOwned { function withdrawTokens(IERC20Token _token, address _to, uint256 _amount) public; } /* We consider every contract to be a 'token holder' since it's currently not possible for a contract to deny receiving tokens. The TokenHolder's contract sole purpose is to provide a safety mechanism that allows the owner to send tokens that were sent to the contract by mistake back to their sender. */ contract TokenHolder is ITokenHolder, Owned { /** @dev constructor */ function TokenHolder() { } // validates an address - currently only checks that it isn't null modifier validAddress(address _address) { require(_address != 0x0); _; } // verifies that the address is different than this contract address modifier notThis(address _address) { require(_address != address(this)); _; } /** @dev withdraws tokens held by the contract and sends them to an account can only be called by the owner @param _token ERC20 token contract address @param _to account to receive the new amount @param _amount amount to withdraw */ function withdrawTokens(IERC20Token _token, address _to, uint256 _amount) public ownerOnly validAddress(_token) validAddress(_to) notThis(_to) { assert(_token.transfer(_to, _amount)); } } /* Smart Token interface */ contract ISmartToken is ITokenHolder, IERC20Token { function disableTransfers(bool _disable) public; function issue(address _to, uint256 _amount) public; function destroy(address _from, uint256 _amount) public; } /* The smart token controller is an upgradable part of the smart token that allows more functionality as well as fixes for bugs/exploits. Once it accepts ownership of the token, it becomes the token's sole controller that can execute any of its functions. To upgrade the controller, ownership must be transferred to a new controller, along with any relevant data. The smart token must be set on construction and cannot be changed afterwards. Wrappers are provided (as opposed to a single 'execute' function) for each of the token's functions, for easier access. Note that the controller can transfer token ownership to a new controller that doesn't allow executing any function on the token, for a trustless solution. Doing that will also remove the owner's ability to upgrade the controller. */ contract SmartTokenController is TokenHolder { ISmartToken public token; // smart token /** @dev constructor */ function SmartTokenController(ISmartToken _token) validAddress(_token) { token = _token; } // ensures that the controller is the token's owner modifier active() { assert(token.owner() == address(this)); _; } // ensures that the controller is not the token's owner modifier inactive() { assert(token.owner() != address(this)); _; } /** @dev allows transferring the token ownership the new owner still need to accept the transfer can only be called by the contract owner @param _newOwner new token owner */ function transferTokenOwnership(address _newOwner) public ownerOnly { token.transferOwnership(_newOwner); } /** @dev used by a new owner to accept a token ownership transfer can only be called by the contract owner */ function acceptTokenOwnership() public ownerOnly { token.acceptOwnership(); } /** @dev disables/enables token transfers can only be called by the contract owner @param _disable true to disable transfers, false to enable them */ function disableTokenTransfers(bool _disable) public ownerOnly { token.disableTransfers(_disable); } /** @dev allows the owner to execute the token's issue function @param _to account to receive the new amount @param _amount amount to increase the supply by */ function issueTokens(address _to, uint256 _amount) public ownerOnly { token.issue(_to, _amount); } /** @dev allows the owner to execute the token's destroy function @param _from account to remove the amount from @param _amount amount to decrease the supply by */ function destroyTokens(address _from, uint256 _amount) public ownerOnly { token.destroy(_from, _amount); } /** @dev withdraws tokens held by the token and sends them to an account can only be called by the owner @param _token ERC20 token contract address @param _to account to receive the new amount @param _amount amount to withdraw */ function withdrawFromToken(IERC20Token _token, address _to, uint256 _amount) public ownerOnly { token.withdrawTokens(_token, _to, _amount); } } /* Crowdsale v0.1 The crowdsale version of the smart token controller, allows contributing ether in exchange for Bancor tokens The price remains fixed for the entire duration of the crowdsale Note that 20% of the contributions are the Bancor token's reserve */ contract CrowdsaleController is SmartTokenController, SafeMath { uint256 public constant DURATION = 14 days; // crowdsale duration uint256 public constant TOKEN_PRICE_N = 1; // initial price in wei (numerator) uint256 public constant TOKEN_PRICE_D = 100; // initial price in wei (denominator) uint256 public constant BTCS_ETHER_CAP = 50000 ether; // maximum bitcoin suisse ether contribution uint256 public constant MAX_GAS_PRICE = 50000000000 wei; // maximum gas price for contribution transactions string public version = '0.1'; uint256 public startTime = 0; // crowdsale start time (in seconds) uint256 public endTime = 0; // crowdsale end time (in seconds) uint256 public totalEtherCap = 1000000 ether; // current ether contribution cap, initialized with a temp value as a safety mechanism until the real cap is revealed uint256 public totalEtherContributed = 0; // ether contributed so far bytes32 public realEtherCapHash; // ensures that the real cap is predefined on deployment and cannot be changed later address public beneficiary = 0x0; // address to receive all ether contributions address public btcs = 0x0; // bitcoin suisse address // triggered on each contribution event Contribution(address indexed _contributor, uint256 _amount, uint256 _return); /** @dev constructor @param _token smart token the crowdsale is for @param _startTime crowdsale start time @param _beneficiary address to receive all ether contributions @param _btcs bitcoin suisse address */ function CrowdsaleController(ISmartToken _token, uint256 _startTime, address _beneficiary, address _btcs, bytes32 _realEtherCapHash) SmartTokenController(_token) validAddress(_beneficiary) validAddress(_btcs) earlierThan(_startTime) validAmount(uint256(_realEtherCapHash)) { startTime = _startTime; endTime = startTime + DURATION; beneficiary = _beneficiary; btcs = _btcs; realEtherCapHash = _realEtherCapHash; } // verifies that an amount is greater than zero modifier validAmount(uint256 _amount) { require(_amount > 0); _; } // verifies that the gas price is lower than 50 gwei modifier validGasPrice() { assert(tx.gasprice <= MAX_GAS_PRICE); _; } // verifies that the ether cap is valid based on the key provided modifier validEtherCap(uint256 _cap, uint256 _key) { require(computeRealCap(_cap, _key) == realEtherCapHash); _; } // ensures that it's earlier than the given time modifier earlierThan(uint256 _time) { assert(now < _time); _; } // ensures that the current time is between _startTime (inclusive) and _endTime (exclusive) modifier between(uint256 _startTime, uint256 _endTime) { assert(now >= _startTime && now < _endTime); _; } // ensures that the sender is bitcoin suisse modifier btcsOnly() { assert(msg.sender == btcs); _; } // ensures that we didn't reach the ether cap modifier etherCapNotReached(uint256 _contribution) { assert(safeAdd(totalEtherContributed, _contribution) <= totalEtherCap); _; } // ensures that we didn't reach the bitcoin suisse ether cap modifier btcsEtherCapNotReached(uint256 _ethContribution) { assert(safeAdd(totalEtherContributed, _ethContribution) <= BTCS_ETHER_CAP); _; } /** @dev computes the real cap based on the given cap & key @param _cap cap @param _key key used to compute the cap hash @return computed real cap hash */ function computeRealCap(uint256 _cap, uint256 _key) public constant returns (bytes32) { return keccak256(_cap, _key); } /** @dev enables the real cap defined on deployment @param _cap predefined cap @param _key key used to compute the cap hash */ function enableRealCap(uint256 _cap, uint256 _key) public ownerOnly active between(startTime, endTime) validEtherCap(_cap, _key) { require(_cap < totalEtherCap); // validate input totalEtherCap = _cap; } /** @dev computes the number of tokens that should be issued for a given contribution @param _contribution contribution amount @return computed number of tokens */ function computeReturn(uint256 _contribution) public constant returns (uint256) { return safeMul(_contribution, TOKEN_PRICE_D) / TOKEN_PRICE_N; } /** @dev ETH contribution can only be called during the crowdsale @return tokens issued in return */ function contributeETH() public payable between(startTime, endTime) returns (uint256 amount) { return processContribution(); } /** @dev Contribution through BTCs (Bitcoin Suisse only) can only be called before the crowdsale started @return tokens issued in return */ function contributeBTCs() public payable btcsOnly btcsEtherCapNotReached(msg.value) earlierThan(startTime) returns (uint256 amount) { return processContribution(); } /** @dev handles contribution logic note that the Contribution event is triggered using the sender as the contributor, regardless of the actual contributor @return tokens issued in return */ function processContribution() private active etherCapNotReached(msg.value) validGasPrice returns (uint256 amount) { uint256 tokenAmount = computeReturn(msg.value); assert(beneficiary.send(msg.value)); // transfer the ether to the beneficiary account totalEtherContributed = safeAdd(totalEtherContributed, msg.value); // update the total contribution amount token.issue(msg.sender, tokenAmount); // issue new funds to the contributor in the smart token token.issue(beneficiary, tokenAmount); // issue tokens to the beneficiary Contribution(msg.sender, msg.value, tokenAmount); return tokenAmount; } // fallback function() payable { contributeETH(); } }