Contract Name:
EtherollCrowdfund
Contract Source Code:
File 1 of 1 : EtherollCrowdfund
pragma solidity ^0.4.2;
/*
* @notice the token contract used as reward
*/
contract token {
/*
* @notice exposes the transfer method of the token contract
* @param _receiver address receiving tokens
* @param _amount number of tokens being transferred
*/
function transfer(address _receiver, uint _amount) returns (bool success) { }
/*
* @notice exposes the priviledgedAddressBurnUnsoldCoins method of the token contract
* burns all unsold coins
*/
function priviledgedAddressBurnUnsoldCoins(){ }
}
/*
`* is owned
*/
contract owned {
address public owner;
function owned() {
owner = msg.sender;
}
modifier onlyOwner {
if (msg.sender != owner) throw;
_;
}
function ownerTransferOwnership(address newOwner)
onlyOwner
{
owner = newOwner;
}
}
/*
* safe math
*/
contract DSSafeAddSub {
function safeToAdd(uint a, uint b) internal returns (bool) {
return (a + b >= a);
}
function safeAdd(uint a, uint b) internal returns (uint) {
if (!safeToAdd(a, b)) throw;
return a + b;
}
function safeToSubtract(uint a, uint b) internal returns (bool) {
return (b <= a);
}
function safeSub(uint a, uint b) internal returns (uint) {
if (!safeToSubtract(a, b)) throw;
return a - b;
}
}
/*
* EtherollCrowdfund contract
* Funds sent to this address transfer a customized ERC20 token to msg.sender for the duration of the crowdfund
* Deployment order:
* EtherollToken, EtherollCrowdfund
* 1) Send tokens to this
* 2) Assign this as priviledgedAddress in EtherollToken
* 3) Call updateTokenStatus in EtherollToken
* -- crowdfund is open --
* 4) safeWithdraw onlyAfterDeadline in this
* 5) ownerBurnUnsoldTokens onlyAfterDeadline in this
* 6) updateTokenStatus in EtherollToken freezes/thaws tokens
*/
contract EtherollCrowdfund is owned, DSSafeAddSub {
/*
* checks only after crowdfund deadline
*/
modifier onlyAfterDeadline() {
if (now < deadline) throw;
_;
}
/*
* checks only in emergency
*/
modifier isEmergency() {
if (!emergency) throw;
_;
}
/* the crowdfund goal */
uint public fundingGoal;
/* 1 week countdown to price increase */
uint public weekTwoPriceRiseBegin = now + 10080 * 1 minutes;
/* 80% to standard multi-sig wallet contract is house bankroll */
address public bankRollBeneficiary;
/* 20% to etheroll wallet*/
address public etherollBeneficiary;
/* total amount of ether raised */
uint public amountRaised;
/* two weeks */
uint public deadline;
/* 0.01 ETH per token base price */
uint public price = 10000000000000000;
/* address of token used as reward */
token public tokenReward;
/* crowdsale is open */
bool public crowdsaleClosed = false;
/* 80% of funds raised */
uint public bankrollBeneficiaryAmount;
/* 20% of funds raised */
uint public etherollBeneficiaryAmount;
/* map balance of address */
mapping (address => uint) public balanceOf;
/* funding goal has not been reached */
bool public fundingGoalReached = false;
/* escape hatch for all in emergency */
bool public emergency = false;
/* log events */
event LogFundTransfer(address indexed Backer, uint indexed Amount, bool indexed IsContribution);
event LogGoalReached(address indexed Beneficiary, uint indexed AmountRaised);
/*
* @param _ifSuccessfulSendToBeneficiary receives 80% of ether raised end of crowdfund
* @param _ifSuccessfulSendToEtheroll receives 20% of ether raised end of crowdfund
* @param _fundingGoalInEthers the funding goal of the crowdfund
* @param _durationInMinutes the length of the crowdfund in minutes
* @param _addressOfTokenUsedAsReward the token address
*/
function EtherollCrowdfund(
/* multi-sig address to send 80% */
address _ifSuccessfulSendToBeneficiary,
/* address to send 20% */
address _ifSuccessfulSendToEtheroll,
/* funding goal */
uint _fundingGoalInEthers,
/* two weeks: 20160 minutes*/
uint _durationInMinutes,
/* token */
token _addressOfTokenUsedAsReward
) {
bankRollBeneficiary = _ifSuccessfulSendToBeneficiary;
etherollBeneficiary = _ifSuccessfulSendToEtheroll;
fundingGoal = _fundingGoalInEthers * 1 ether;
deadline = now + _durationInMinutes * 1 minutes;
tokenReward = token(_addressOfTokenUsedAsReward);
}
/*
* @notice public function
* default function is payable
* responsible for transfer of tokens based on price, msg.sender and msg.value
* tracks investment total of msg.sender
* refunds any spare change
*/
function ()
payable
{
/* crowdfund period is over */
if(now > deadline) crowdsaleClosed = true;
/* crowdsale is closed */
if (crowdsaleClosed) throw;
/* do not allow creating 0 */
if (msg.value == 0) throw;
/*
* transfer tokens
* check/set week two price rise
*/
if(now < weekTwoPriceRiseBegin) {
/* week 1 power token conversion * 2: 1 ETH = 200 tokens */
if(tokenReward.transfer(msg.sender, ((msg.value*price)/price)*2)) {
LogFundTransfer(msg.sender, msg.value, true);
} else {
throw;
}
}else{
/* week 2 conversion: 1 ETH = 100 tokens */
if(tokenReward.transfer(msg.sender, (msg.value*price)/price)) {
LogFundTransfer(msg.sender, msg.value, true);
} else {
throw;
}
}
/* add to amountRaised */
amountRaised = safeAdd(amountRaised, msg.value);
/* track ETH balanceOf address in case emergency refund is required */
balanceOf[msg.sender] = safeAdd(balanceOf[msg.sender], msg.value);
}
/*
* @notice public function
* onlyAfterDeadline
* moves ether to beneficiary contracts if goal reached
* if goal not reached msg.sender can withdraw their deposit
*/
function safeWithdraw() public
onlyAfterDeadline
{
if (amountRaised >= fundingGoal){
/* allows funds to be moved to beneficiary */
fundingGoalReached = true;
/* log event */
LogGoalReached(bankRollBeneficiary, amountRaised);
}
/* close crowdsale */
crowdsaleClosed = true;
/*
* public
* funding goal not reached
* manual refunds
*/
if (!fundingGoalReached) {
calcRefund(msg.sender);
}
/*
* onlyOwner can call
* funding goal reached
* move funds to beneficiary addresses
*/
if (msg.sender == owner && fundingGoalReached) {
/* multi-sig bankrollBeneficiary receives 80% */
bankrollBeneficiaryAmount = (this.balance*80)/100;
/* send to trusted address bankRollBeneficiary 80% */
if (bankRollBeneficiary.send(bankrollBeneficiaryAmount)) {
/* log event */
LogFundTransfer(bankRollBeneficiary, bankrollBeneficiaryAmount, false);
/* etherollBeneficiary receives remainder */
etherollBeneficiaryAmount = this.balance;
/* send to trusted address etherollBeneficiary the remainder */
if(!etherollBeneficiary.send(etherollBeneficiaryAmount)) throw;
/* log event */
LogFundTransfer(etherollBeneficiary, etherollBeneficiaryAmount, false);
} else {
/* allow manual refunds via safeWithdrawal */
fundingGoalReached = false;
}
}
}
/*
* @notice internal function
* @param _addressToRefund the address being refunded
* accessed via public functions emergencyWithdraw and safeWithdraw
* calculates refund amount available for an address
*/
function calcRefund(address _addressToRefund) internal
{
/* assigns var amount to balance of _addressToRefund */
uint amount = balanceOf[_addressToRefund];
/* sets balance to 0 */
balanceOf[_addressToRefund] = 0;
/* is there any balance? */
if (amount > 0) {
/* call to untrusted address */
if (_addressToRefund.call.value(amount)()) {
/* log event */
LogFundTransfer(_addressToRefund, amount, false);
} else {
/* unsuccessful send so reset the balance */
balanceOf[_addressToRefund] = amount;
}
}
}
/*
* @notice public function
* emergency manual refunds
*/
function emergencyWithdraw() public
isEmergency
{
/* manual refunds */
calcRefund(msg.sender);
}
/*
* @notice owner restricted function
* @param _newEmergencyStatus boolean
* sets contract mode to emergency status to allow individual withdraw via emergencyWithdraw()
*/
function ownerSetEmergencyStatus(bool _newEmergencyStatus) public
onlyOwner
{
/* close crowdsale */
crowdsaleClosed = _newEmergencyStatus;
/* allow manual refunds via emergencyWithdraw */
emergency = _newEmergencyStatus;
}
/*
* @notice owner restricted function
* burns any unsold tokens at end of crowdfund
*/
function ownerBurnUnsoldTokens()
onlyOwner
onlyAfterDeadline
{
tokenReward.priviledgedAddressBurnUnsoldCoins();
}
}