Created
June 2, 2018 17:54
-
-
Save bitdollarfund/ab4ad8ad8daf392f6b3fa5077fdd99f0 to your computer and use it in GitHub Desktop.
Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=soljson-v0.4.24+commit.e67f0147.js&optimize=true&gist=
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma solidity 0.4.24; | |
contract owned { | |
address public owner; | |
function owned() public { | |
owner = msg.sender; | |
} | |
modifier onlyOwner { | |
require(msg.sender == owner); | |
_; | |
} | |
function transferOwnership(address newOwner) onlyOwner public { | |
owner = newOwner; | |
} | |
} | |
contract tokenRecipient { | |
event receivedEther(address sender, uint amount); | |
event receivedTokens(address _from, uint256 _value, address _token, bytes _extraData); | |
function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData) public { | |
Token t = Token(_token); | |
require(t.transferFrom(_from, this, _value)); | |
receivedTokens(_from, _value, _token, _extraData); | |
} | |
function () payable public { | |
receivedEther(msg.sender, msg.value); | |
} | |
} | |
contract Token { | |
function balanceOf(address _owner) public view returns (uint256 balance); | |
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success); | |
function totalSupply() public view returns (uint256 total); | |
} | |
/** | |
* The shareholder association contract itself | |
*/ | |
contract BitdollarAssociation is owned, tokenRecipient { | |
uint public minimumQuorum; | |
uint public debatingPeriodInMinutes; | |
Proposal[] public proposals; | |
uint public numProposals; | |
Token public sharesTokenAddress; | |
event ProposalAdded(uint proposalID, address recipient, uint amount, string description); | |
event Voted(uint proposalID, bool position, address voter); | |
event ProposalTallied(uint proposalID, int result, uint quorum, bool active); | |
event ChangeOfRules(uint newMinimumQuorum, uint newDebatingPeriodInMinutes, address newSharesTokenAddress); | |
event Summary(uint proposalID, uint participationStatistic); | |
event MCQ(uint proposalID, uint8 option, address voter); | |
event WinningMCQ(uint indexNumber, uint tally); | |
struct Proposal { | |
address recipient; | |
uint amount; | |
string description; | |
uint votingDeadline; | |
bool executed; | |
bool proposalPassed; | |
uint numberOfVotes; | |
bytes32 proposalHash; | |
Vote[] votes; | |
mapping (address => bool) voted; | |
bool multipleChoice; | |
uint numberOfChoices; | |
uint[11] choices; // choices[0] is dummy variable | |
} | |
struct Vote { | |
bool inSupport; | |
uint8 choice; | |
address voter; | |
} | |
// Modifier that allows only shareholders to vote and create new proposals | |
modifier onlyShareholders { | |
require(sharesTokenAddress.balanceOf(msg.sender) > 0); | |
_; | |
} | |
/** | |
* Constructor function | |
* | |
* First time setup | |
*/ | |
function BitdollarAssociation(Token sharesAddress, uint minimumSharesToPassAVote, uint minutesForDebate) payable public { | |
changeVotingRules(sharesAddress, minimumSharesToPassAVote, minutesForDebate); | |
} | |
/** | |
* Change voting rules | |
* | |
* Make so that proposals need to be discussed for at least `minutesForDebate/60` hours | |
* and all voters combined must own more than `minimumSharesToPassAVote` shares of token `sharesAddress` to be executed | |
* | |
* @param sharesAddress token address | |
* @param minimumSharesToPassAVote proposal can vote only if the sum of shares held by all voters exceed this number | |
* @param minutesForDebate the minimum amount of delay between when a proposal is made and when it can be executed | |
*/ | |
function changeVotingRules(Token sharesAddress, uint minimumSharesToPassAVote, uint minutesForDebate) onlyOwner public { | |
sharesTokenAddress = Token(sharesAddress); | |
if (minimumSharesToPassAVote == 0 ) minimumSharesToPassAVote = 1; | |
minimumQuorum = minimumSharesToPassAVote; | |
debatingPeriodInMinutes = minutesForDebate; | |
ChangeOfRules(minimumQuorum, debatingPeriodInMinutes, sharesTokenAddress); | |
} | |
/** | |
* Add Proposal | |
* | |
* Propose to send `weiAmount / 1e18` ether to `beneficiary` for `jobDescription`. `transactionBytecode ? Contains : Does not contain` code. | |
* | |
* @param beneficiary who to send the ether to | |
* @param weiAmount amount of ether to send, in wei | |
* @param jobDescription Description of job | |
* @param transactionBytecode bytecode of transaction | |
*/ | |
function newProposal( | |
address beneficiary, | |
uint weiAmount, | |
string jobDescription, | |
bytes transactionBytecode | |
) | |
onlyShareholders | |
public returns (uint proposalID) | |
{ | |
proposalID = proposals.length++; | |
Proposal storage p = proposals[proposalID]; | |
p.recipient = beneficiary; | |
p.amount = weiAmount; | |
p.description = jobDescription; | |
p.proposalHash = keccak256(beneficiary, weiAmount, transactionBytecode); | |
p.votingDeadline = now + debatingPeriodInMinutes * 1 minutes; | |
p.executed = false; | |
p.proposalPassed = false; | |
p.numberOfVotes = 0; | |
ProposalAdded(proposalID, beneficiary, weiAmount, jobDescription); | |
numProposals = proposalID+1; | |
p.multipleChoice = false; // no multiple choice for default yea/nay votes | |
p.numberOfChoices = 0; | |
return proposalID; | |
} | |
/** | |
* Add proposal in Ether | |
* | |
* Propose to send `etherAmount` ether to `beneficiary` for `jobDescription`. `transactionBytecode ? Contains : Does not contain` code. | |
* This is a convenience function to use if the amount to be given is in round number of ether units. | |
* | |
* @param beneficiary who to send the ether to | |
* @param etherAmount amount of ether to send | |
* @param jobDescription Description of job | |
* @param transactionBytecode bytecode of transaction | |
*/ | |
function newProposalInEther( | |
address beneficiary, | |
uint etherAmount, | |
string jobDescription, | |
bytes transactionBytecode | |
) | |
onlyShareholders | |
public returns (uint proposalID) | |
{ | |
return newProposal(beneficiary, etherAmount * 1 ether, jobDescription, transactionBytecode); | |
} | |
function newMCQProposal ( | |
address beneficiary, | |
uint weiAmount, | |
string jobDescription, | |
uint numChoices, | |
bytes transactionBytecode | |
) | |
onlyShareholders | |
public returns (uint proposalID) | |
{ | |
require(numChoices <= 10); | |
proposalID = proposals.length++; | |
Proposal storage p = proposals[proposalID]; | |
p.recipient = beneficiary; | |
p.amount = weiAmount; | |
p.description = jobDescription; | |
p.proposalHash = keccak256(beneficiary, weiAmount, transactionBytecode); | |
p.votingDeadline = now + debatingPeriodInMinutes * 1 minutes; | |
p.executed = false; | |
p.proposalPassed = false; | |
p.numberOfVotes = 0; | |
ProposalAdded(proposalID, beneficiary, weiAmount, jobDescription); | |
numProposals = proposalID+1; | |
p.multipleChoice = true; // no multiple choice for default yea/nay votes | |
p.numberOfChoices = numChoices; | |
return proposalID; | |
} | |
/** | |
* Check if a proposal code matches | |
* | |
* @param proposalNumber ID number of the proposal to query | |
* @param beneficiary who to send the ether to | |
* @param weiAmount amount of ether to send | |
* @param transactionBytecode bytecode of transaction | |
*/ | |
function checkProposalCode( | |
uint proposalNumber, | |
address beneficiary, | |
uint weiAmount, | |
bytes transactionBytecode | |
) | |
constant | |
public returns (bool codeChecksOut) | |
{ | |
Proposal storage p = proposals[proposalNumber]; | |
return p.proposalHash == keccak256(beneficiary, weiAmount, transactionBytecode); | |
} | |
/** | |
* Log a vote for a proposal | |
* | |
* Vote `supportsProposal? in support of : against` proposal #`proposalNumber` | |
* | |
* @param proposalNumber number of proposal | |
* @param supportsProposal either in favor or against it | |
*/ | |
function vote( | |
uint proposalNumber, | |
bool supportsProposal | |
) | |
onlyShareholders | |
public returns (uint voteID) | |
{ | |
Proposal storage p = proposals[proposalNumber]; | |
require(!p.multipleChoice); | |
require(p.voted[msg.sender] != true); | |
voteID = p.votes.length++; | |
p.votes[voteID] = Vote({inSupport: supportsProposal, choice: 0, voter: msg.sender}); | |
p.voted[msg.sender] = true; | |
p.numberOfVotes = voteID +1; | |
Voted(proposalNumber, supportsProposal, msg.sender); | |
return voteID; | |
} | |
/** | |
* Log a vote for an MCQ proposal | |
* | |
* Vote `supportsProposal? in support of : against` proposal #`proposalNumber` | |
* | |
* @param proposalNumber number of proposal | |
* @param optionNumber the choice made in MCQ | |
*/ | |
function multipleChoiceVote( | |
uint proposalNumber, | |
uint8 optionNumber | |
) | |
onlyShareholders | |
public returns (uint voteID) | |
{ | |
Proposal storage p = proposals[proposalNumber]; | |
require(p.multipleChoice); | |
require(optionNumber <= p.numberOfChoices && optionNumber > 0); | |
require(p.voted[msg.sender] != true); | |
voteID = p.votes.length++; | |
p.votes[voteID] = Vote({inSupport: true, choice: optionNumber, voter: msg.sender}); | |
p.voted[msg.sender] = true; | |
p.numberOfVotes = voteID +1; | |
p.choices[optionNumber]++; | |
MCQ(proposalNumber, optionNumber, msg.sender); | |
return voteID; | |
} | |
/** | |
* Finish vote | |
* | |
* Count the votes proposal #`proposalNumber` and execute it if approved | |
* | |
* @param proposalNumber proposal number | |
* @param transactionBytecode optional: if the transaction contained a bytecode, you need to send it | |
*/ | |
function executeProposal(uint proposalNumber, bytes transactionBytecode) public { | |
Proposal storage p = proposals[proposalNumber]; | |
require(now > p.votingDeadline // If it is past the voting deadline | |
&& !p.executed // and it has not already been executed | |
&& !p.multipleChoice | |
&& p.proposalHash == keccak256(p.recipient, p.amount, transactionBytecode)); // and the supplied code matches the proposal... | |
// ...then tally the results | |
uint quorum = 0; | |
uint yea = 0; | |
uint nay = 0; | |
for (uint i = 0; i < p.votes.length; ++i) { | |
Vote storage v = p.votes[i]; | |
uint voteWeight = sharesTokenAddress.balanceOf(v.voter); | |
quorum += voteWeight; | |
if (v.inSupport) { | |
yea += voteWeight; | |
} else { | |
nay += voteWeight; | |
} | |
} | |
require(quorum >= minimumQuorum); // Check if a minimum quorum has been reached | |
if (yea > nay ) { | |
// Proposal passed; execute the transaction | |
p.executed = true; | |
require(p.recipient.call.value(p.amount)(transactionBytecode)); | |
p.proposalPassed = true; | |
} else { | |
// Proposal failed | |
p.proposalPassed = false; | |
} | |
// Fire Events | |
ProposalTallied(proposalNumber, int(yea) - int(nay), quorum, p.proposalPassed); | |
uint statistic = quorum * 100000 / sharesTokenAddress.totalSupply(); // the percentage of "coins" that voted; at 5 degrees precision | |
Summary(proposalNumber, statistic); | |
} | |
/** | |
* Finish vote | |
* | |
* Count the votes proposal #`proposalNumber` and execute it if approved | |
* | |
* @param proposalNumber proposal number | |
* @param transactionBytecode optional: if the transaction contained a bytecode, you need to send it | |
*/ | |
function analyzeMCQProposal(uint proposalNumber, bytes transactionBytecode) public { | |
Proposal storage p = proposals[proposalNumber]; | |
require(now > p.votingDeadline // If it is past the voting deadline | |
&& !p.executed // and it has not already been executed | |
&& p.multipleChoice | |
&& p.proposalHash == keccak256(p.recipient, p.amount, transactionBytecode)); // and the supplied code matches the proposal... | |
uint max = 0; | |
uint index = 0; | |
for(uint j = 1; j <= p.numberOfChoices; j++) { | |
if(p.choices[j] >= max) { | |
max = p.choices[j]; | |
index = j; | |
} | |
} | |
uint quorum = 0; | |
for (uint i = 0; i < p.votes.length; ++i) { | |
Vote storage v = p.votes[i]; | |
uint voteWeight = sharesTokenAddress.balanceOf(v.voter); | |
quorum += voteWeight; | |
} | |
require(quorum >= minimumQuorum); // Check if a minimum quorum has been reached | |
uint statistic = quorum * 100000 / sharesTokenAddress.totalSupply(); // the percentage of "coins" that voted; at 5 degrees precision | |
Summary(proposalNumber, statistic); | |
WinningMCQ(index, max); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma solidity 0.4.24; | |
import './BitdollarToken.sol'; | |
import './SafeMath.sol'; | |
// deploy BitdollarToken contract first and pass that address as parameter to this contract's constructor | |
// deploy BitdollarVault and pass to it the BitdollarToken address and this contract's address as parameters | |
// finally, assign this contract's address as tokenContoller and the BitdollarVault address as tokenVault in BitdollarToken by invoking | |
// the changeTokenController function | |
contract BitdollarCampaign is SafeMath { | |
BitdollarToken bitdollarTokenContract; | |
// keeps track of total number of tokens sold throughout all phases | |
uint256 public totalSold; | |
// current phase token cap | |
uint256 public currentPhaseCap; | |
// tokens sold in current phase | |
uint256 public currentPhaseTokenSales; | |
// running total of development allocation accumulated in current phase | |
uint256 public developmentAllocationRunningTotal; | |
// current phase development allocation percent (takes 17 digit input for precision) | |
uint256 public developmentAllocationPercent; | |
// current phase starting block | |
uint256 public fundingStartBlock; | |
// current phase ending block | |
uint256 public fundingEndBlock; | |
// current phase minimum purchase threshold | |
uint256 public minAmount; | |
// current phase price | |
Price public currentPrice; | |
// state variable indicates if active sale phase | |
bool public icoMode; | |
// state variable to indicate ico parameters have been entered | |
bool public icoPrimed; | |
// amount of tokens unsold during current phase | |
// fundWallet may add this amount to subsequent phase cap at their discretion | |
uint256 public tokenCapRollOverToNextPhase; | |
// vesting fields | |
address public vestingContract; | |
bool private vestingSet; | |
// root control | |
address public fundWallet; | |
// control of liquidity and limited control of updatePrice | |
address public controlWallet; | |
// time to wait between controlWallet price updates | |
uint256 public waitTime; | |
// halted: halt buying due to emergency, tradeable: signal that assets have been acquired | |
bool public halted; | |
// is maintained inside token contract | |
//bool public tradeable; | |
// last update timestamp | |
uint256 public previousUpdateTime; | |
// maps previousUpdateTime to the next price | |
mapping (uint256 => Price) public prices; | |
// maps addresses | |
mapping (address => bool) public whitelist; | |
// TYPES | |
struct Price { // tokensPerEth | |
uint256 numerator; | |
uint256 denominator; | |
} | |
// EVENTS | |
event Buy(address indexed participant, address indexed beneficiary, uint256 ethValue, uint256 amountTokens); | |
event AllocatePresale(address indexed participant, uint256 amountTokens); | |
event Whitelist(address indexed participant); | |
event PriceUpdate(uint256 numerator, uint256 denominator); | |
event ICO(uint256 phaseCap, uint256 phaseStart, uint256 phaseEnd); | |
//enum Phase { Initial, PreSale, Sale, Closed } | |
enum Phase { PreSale, Sale, Closed } | |
Phase public phase; | |
// MODIFIERS | |
//TODO this modifier is not used /////////////////////////////////////////////////////////////////// | |
// modifier isTradeable { // exempt vestingContract and fundWallet to allow dev allocations | |
// require(tradeable || msg.sender == fundWallet || msg.sender == vestingContract); | |
// _; | |
// } | |
modifier onlyWhitelist { | |
require(whitelist[msg.sender]); | |
_; | |
} | |
modifier onlyFundWallet { | |
require(msg.sender == fundWallet); | |
_; | |
} | |
modifier onlyManagingWallets { | |
require(msg.sender == controlWallet || msg.sender == fundWallet); | |
_; | |
} | |
modifier only_if_controlWallet { | |
if (msg.sender == controlWallet) | |
_; | |
} | |
modifier require_waited { | |
require(safeSub(now, waitTime) >= previousUpdateTime); | |
_; | |
} | |
modifier only_if_increase (uint256 newNumerator) { | |
if (newNumerator > currentPrice.numerator) | |
_; | |
} | |
modifier whileSaleActive { | |
require(block.number >= fundingStartBlock && block.number < fundingEndBlock && icoMode); | |
_; | |
} | |
// CONSTRUCTOR | |
constructor(address controlWalletInput, | |
address tokenContractInput, | |
uint256 priceNumeratorInput) public { | |
require(controlWalletInput != address(0) && tokenContractInput != address(0) && priceNumeratorInput > 0); | |
controlWallet = controlWalletInput; | |
bitdollarTokenContract = BitdollarToken(tokenContractInput); | |
currentPrice = Price(priceNumeratorInput, 1000); // 1 token = 1 usd at ICO start | |
previousUpdateTime = now; | |
fundWallet = msg.sender; | |
whitelist[fundWallet] = true; | |
whitelist[controlWallet] = true; | |
waitTime = 5 hours; | |
vestingSet = false; | |
halted = false; | |
icoMode = false; | |
// phase = Phase.Initial; | |
phase = Phase.Closed; | |
} | |
// METHODS | |
// TODO set three vesting contracts | |
function setVestingContract(address vestingContractInput) external onlyFundWallet { | |
require(vestingContractInput != address(0)); | |
vestingContract = vestingContractInput; | |
//whitelist[vestingContract] = true; any reason for vestingContract address to purchase more tokens? | |
vestingSet = true; | |
} | |
// allows controlWallet to update the price within a time contstraint, allows fundWallet complete control | |
function updatePrice(uint256 newNumerator) external onlyManagingWallets { | |
require(newNumerator > 0); | |
require_limited_change(newNumerator); | |
// either controlWallet command is compliant or transaction came from fundWallet | |
currentPrice.numerator = newNumerator; | |
// maps time to new Price (if not during ICO) | |
prices[previousUpdateTime] = currentPrice; | |
previousUpdateTime = now; | |
PriceUpdate(newNumerator, currentPrice.denominator); | |
} | |
function require_limited_change (uint256 newNumerator) | |
private | |
only_if_controlWallet | |
require_waited | |
only_if_increase(newNumerator) | |
view | |
{ | |
uint256 percentage_diff = 0; | |
percentage_diff = safeMul(newNumerator, 100) / currentPrice.numerator; | |
percentage_diff = safeSub(percentage_diff, 100); | |
// controlWallet can only increase price by max 20% and only every waitTime | |
require(percentage_diff <= 20); | |
} | |
function updatePriceDenominator(uint256 newDenominator) external onlyFundWallet { | |
require(!icoMode); | |
//require(block.number > fundingEndBlock); | |
require(newDenominator > 0); | |
currentPrice.denominator = newDenominator; | |
// maps time to new Price | |
prices[previousUpdateTime] = currentPrice; | |
previousUpdateTime = now; | |
PriceUpdate(currentPrice.numerator, newDenominator); | |
} | |
function allocateTokens(address participant, uint256 amountTokens) internal { | |
//require(vestingSet); | |
// 15% of total allocated for PR, Marketing, Team, Advisors | |
// uint256 devAllocation = safeMul(amountTokens, 17647058823529400) / 100000000000000000; | |
uint256 devAllocation = safeMul(amountTokens, developmentAllocationPercent) / 100000000000000000; | |
// check that token cap is not exceeded | |
uint256 newTokens = safeAdd(amountTokens, devAllocation); | |
require(safeAdd(currentPhaseTokenSales, newTokens) <= currentPhaseCap); | |
//require(safeAdd(tokenSold, newTokens) <= tokenCap); | |
// increase token supply, assign tokens to participant | |
currentPhaseTokenSales = safeAdd(currentPhaseTokenSales, newTokens); | |
totalSold = safeAdd(totalSold, newTokens); | |
//tokenSold = safeAdd(tokenSold, newTokens); | |
developmentAllocationRunningTotal = safeAdd(developmentAllocationRunningTotal, devAllocation); | |
require(bitdollarTokenContract.generateTokens(participant, amountTokens)); | |
//balances[participant] = safeAdd(balances[participant], amountTokens); | |
//balances[vestingContract] = safeAdd(balances[vestingContract], developmentAllocation); | |
} | |
function allocatePresaleTokens(address participant, uint amountTokens) external onlyFundWallet whileSaleActive { | |
//require(block.number < fundingEndBlock); | |
require(participant != address(0) && amountTokens > 0); | |
whitelist[participant] = true; // automatically whitelist accepted presale | |
allocateTokens(participant, amountTokens); | |
Whitelist(participant); | |
AllocatePresale(participant, amountTokens); | |
} | |
function verifyParticipant(address participant) external onlyManagingWallets { | |
require(participant != address(0)); | |
whitelist[participant] = true; | |
Whitelist(participant); | |
} | |
// fallback function | |
function() external payable whileSaleActive { | |
// TODO is this check necessary? ////////////////////////////////////// | |
// require(tx.origin == msg.sender); | |
buy(msg.sender); | |
} | |
function buy(address msgsender) public payable whileSaleActive { | |
require(!halted); | |
// if presale phase | |
if(phase == Phase.PreSale) { | |
// must be whitelisted participant | |
if(whitelist[msgsender]) { | |
buyTo(msgsender); | |
// else check if minimum amount in ether is supplied | |
} else if (msg.value > minAmount) { | |
// add them to whitelist | |
whitelist[msgsender] = true; | |
Whitelist(msgsender); | |
buyTo(msgsender); | |
} | |
} | |
else | |
require(msg.value > minAmount); | |
buyTo(msgsender); | |
} | |
function buyTo(address participant) internal returns (bool) { | |
uint256 icoDenominator = icoDenominatorPrice(); | |
uint256 tokensToBuy = safeMul(msg.value, currentPrice.numerator) / icoDenominator; | |
allocateTokens(participant, tokensToBuy); | |
// send ether to fundWallet | |
fundWallet.transfer(msg.value); | |
Buy(msg.sender, participant, msg.value, tokensToBuy); | |
} | |
// time based on blocknumbers, assuming a blocktime of 30s | |
function icoDenominatorPrice() public constant returns (uint256) { | |
uint256 icoDuration = safeSub(block.number, fundingStartBlock); | |
uint256 denominator; | |
if (icoDuration < 2880) { // #blocks = 24*60*60/30 = 2880 | |
return currentPrice.denominator; | |
} else if (icoDuration < 80640 ) { // #blocks = 4*7*24*60*60/30 = 80640 | |
denominator = safeMul(currentPrice.denominator, 105) / 100; | |
return denominator; | |
} else { | |
denominator = safeMul(currentPrice.denominator, 110) / 100; | |
return denominator; | |
} | |
} | |
function changeFundWallet(address newFundWallet) external onlyFundWallet { | |
require(newFundWallet != address(0)); | |
fundWallet = newFundWallet; | |
} | |
function changeControlWallet(address newControlWallet) external onlyFundWallet { | |
require(newControlWallet != address(0)); | |
controlWallet = newControlWallet; | |
} | |
function changeWaitTime(uint256 newWaitTime) external onlyFundWallet { | |
waitTime = newWaitTime; | |
} | |
function updateFundingStartBlock(uint256 newFundingStartBlock) external onlyFundWallet { | |
require(block.number < fundingStartBlock); | |
require(block.number < newFundingStartBlock); | |
require(newFundingStartBlock < fundingEndBlock); // sanity check | |
fundingStartBlock = newFundingStartBlock; | |
} | |
function updateFundingEndBlock(uint256 newFundingEndBlock) external onlyFundWallet { | |
require(block.number < fundingEndBlock); | |
require(block.number < newFundingEndBlock); | |
require(fundingStartBlock < newFundingEndBlock); // sanity check | |
fundingEndBlock = newFundingEndBlock; | |
} | |
// update minimum amount for investment | |
// @param newMinAmount the units in ether; eg input 10 for a 10 ETH minimum | |
function updateMinimumEtherAmount(uint256 newMinAmount) external onlyFundWallet { | |
require(!icoMode); | |
minAmount = newMinAmount * 1 ether; | |
} | |
// update developer percentage allocation | |
// @param newDevAllocationPercentage takes a 17 digit precision input | |
function updateDevAllocationPercentage(uint256 newDevAllocationPercentage) external onlyFundWallet { | |
require(!icoMode); | |
developmentAllocationPercent = newDevAllocationPercentage; | |
} | |
// update currentPhaseCap | |
// @param newCurrentPhaseCap is expected to be between 100 000 000 and 400 000 000; will be converted internally | |
function updateCurrentPhaseCap(uint256 newCurrentPhaseCap) external onlyFundWallet { | |
require(!icoMode); | |
currentPhaseCap = newCurrentPhaseCap * 10**18; | |
} | |
// internal function to select phase state | |
// @param presale a boolean to indicate if presale, if false will activate Sale phase state | |
function setPhase(bool presale) internal { | |
// // cannot change if Initial Phase | |
// if(phase == Phase.Initial) { | |
// return; | |
// } else if(presale) { | |
// phase = Phase.PreSale; | |
// } else { | |
// phase = Phase.Sale; | |
// } | |
if(presale) { | |
phase = Phase.PreSale; | |
} else { | |
phase = Phase.Sale; | |
} | |
} | |
// update phase state | |
// only allows switching from PreSale to Sale and vice versa | |
function updatePhase() external onlyFundWallet { | |
if(phase == Phase.PreSale) { | |
phase = Phase.Sale; | |
} else if (phase == Phase.Sale) { | |
phase = Phase.PreSale; | |
} | |
} | |
function halt() external onlyFundWallet { | |
halted = true; | |
} | |
function unhalt() external onlyFundWallet { | |
halted = false; | |
} | |
// enables trading, withdrawals and token transfers are allowed in this state | |
function enableTrading() external onlyFundWallet { | |
// cannot enable during an active crowdsale | |
require(!icoMode); | |
bitdollarTokenContract.toggleTrading(true); | |
//tradeable = true; | |
} | |
// disables token trading, cannot make withdrawals from BitdollarVault or transfer tokens between accounts in this state | |
function disableTrading() external onlyFundWallet { | |
bitdollarTokenContract.toggleTrading(false); | |
//tradeable = false; | |
} | |
function claimTokens(address _token) external onlyFundWallet { | |
require(_token != address(0)); | |
Token token = Token(_token); | |
uint256 balance = token.balanceOf(this); | |
token.transfer(fundWallet, balance); | |
} | |
function _balanceOf(address _addr) internal view returns(uint256) { | |
return bitdollarTokenContract.balanceOf(_addr); | |
} | |
function getPrice(uint256 timestamp) external view returns (uint256, uint256) { | |
return (prices[timestamp].numerator, prices[timestamp].denominator); | |
} | |
function getCurrentPrice() external view returns (uint256, uint256) { | |
return (currentPrice.numerator, currentPrice.denominator); | |
} | |
function getPreviousUpdateTime() external view returns (uint256) { | |
return previousUpdateTime; | |
} | |
function setupParameters(uint256 startBlockInput, | |
uint256 endBlockInput, | |
uint256 minAmountInput, // in wei | |
uint256 devPercentInput, // takes 17 digit precision input | |
uint256 phaseCapInput, // cap for the current phase - must multiply coin count by 10**18 | |
bool isPresale, // set true for presale, false for sale | |
bool primeICO) // set true if there is confidence in paramaters supplied - ready to activate | |
external onlyFundWallet { | |
// must not be in active ico | |
require(!icoMode); | |
// specified blocks must be in the future and have valid duration | |
require(block.number < startBlockInput && startBlockInput < endBlockInput); | |
// prices must have been previously set - if not updated the new phase will carry the same price as the previous phase | |
// fundWallet must invoke updatePrice and updatePriceDenominator externally to change prices | |
// set to current ether equivalent of $100000 minimum to qualify for presale purchase | |
// set to an arbitrary minimum eg 0.01 ether during normal sale to prevent spam calls from congesting the contract | |
require(minAmountInput > 0); | |
minAmount = minAmountInput; | |
// reset running total | |
developmentAllocationRunningTotal = 0; | |
developmentAllocationPercent = devPercentInput; | |
// reset current token sales count | |
currentPhaseTokenSales = 0; | |
currentPhaseCap = phaseCapInput; | |
// crowdsale parameters | |
fundingStartBlock = startBlockInput; | |
fundingEndBlock = endBlockInput; | |
// cannot transfer BTD when active ICO to avoid price undercutting by current holders at third party exchange | |
bitdollarTokenContract.toggleTrading(false); | |
setPhase(isPresale); | |
// indicate ico parameters are primed | |
icoPrimed = primeICO; | |
} | |
// activates ico to start accepting ether | |
function activateICO() external onlyFundWallet { | |
require(!icoMode && icoPrimed); | |
icoPrimed = false; | |
icoMode = true; | |
} | |
// closes current ico and generates dev allocation tokens | |
function finalizeCurrentICO() external onlyFundWallet { | |
require(icoMode); | |
require(block.number > fundingEndBlock); | |
//balances[vestingContract] = safeAdd(balances[vestingContract], developmentAllocation); | |
require(vestingSet); | |
// generate allocation tokens accumulated throughout the sale | |
require(bitdollarTokenContract.generateTokens(vestingContract, developmentAllocationRunningTotal)); | |
icoMode = false; | |
phase = Phase.Closed; | |
tokenCapRollOverToNextPhase = safeSub(currentPhaseCap, currentPhaseTokenSales); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma solidity 0.4.24; | |
contract owned { | |
address public owner; | |
function owned() public { | |
owner = msg.sender; | |
} | |
modifier onlyOwner { | |
require(msg.sender == owner); | |
_; | |
} | |
function transferOwnership(address newOwner) onlyOwner public { | |
owner = newOwner; | |
} | |
} | |
contract tokenRecipient { | |
event receivedEther(address sender, uint amount); | |
event receivedTokens(address _from, uint256 _value, address _token, bytes _extraData); | |
function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData) public { | |
Token t = Token(_token); | |
require(t.transferFrom(_from, this, _value)); | |
receivedTokens(_from, _value, _token, _extraData); | |
} | |
function () payable public { | |
receivedEther(msg.sender, msg.value); | |
} | |
} | |
contract Token { | |
function balanceOf(address _owner) public view returns (uint256 balance); | |
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success); | |
function totalSupply() public view returns (uint256 total); | |
} | |
/** | |
* The shareholder association contract itself | |
*/ | |
contract BitdollarMembersAssociation is owned, tokenRecipient { | |
uint public minimumQuorum; | |
uint public debatingPeriodInMinutes; | |
Proposal[] public proposals; | |
uint public numProposals; | |
Token public sharesTokenAddress; | |
mapping (address => uint) public memberId; | |
Member[] public members; | |
event ProposalAdded(uint proposalID, address recipient, uint amount, string description); | |
event Voted(uint proposalID, bool position, address voter); | |
event ProposalTallied(uint proposalID, int result, uint quorum, bool active); | |
event ChangeOfRules(uint newMinimumQuorum, uint newDebatingPeriodInMinutes, address newSharesTokenAddress); | |
event MembershipChanged(address member, bool isMember); | |
event Summary(uint proposalID, uint participationStatistic); | |
event MCQ(uint proposalID, uint8 option, address voter); | |
event WinningMCQ(uint indexNumber, uint tally); | |
struct Member { | |
address member; | |
string name; | |
uint memberSince; | |
bool delegated; | |
uint256 weight; | |
uint256 representingCount; | |
} | |
struct Proposal { | |
address recipient; | |
uint amount; | |
string description; | |
uint votingDeadline; | |
bool executed; | |
bool proposalPassed; | |
uint numberOfVotes; | |
bytes32 proposalHash; | |
Vote[] votes; | |
mapping (address => bool) voted; | |
bool multipleChoice; | |
uint numberOfChoices; | |
uint[11] choices; // choices[0] is dummy variable | |
} | |
struct Vote { | |
bool inSupport; | |
uint8 choice; | |
address voter; | |
} | |
// // Modifier that allows only shareholders to vote and create new proposals | |
// modifier onlyShareholders { | |
// require(sharesTokenAddress.balanceOf(msg.sender) > 0); | |
// _; | |
// } | |
// Modifier that allows only shareholders to vote and create new proposals | |
modifier onlyMembers { | |
require(memberId[msg.sender] != 0); | |
_; | |
} | |
/** | |
* Constructor function | |
* | |
* First time setup | |
*/ | |
function BitdollarAssociation(Token sharesAddress, uint minimumSharesToPassAVote, uint minutesForDebate) payable public { | |
changeVotingRules(sharesAddress, minimumSharesToPassAVote, minutesForDebate); | |
// It’s necessary to add an empty first member | |
addMember(0, ""); | |
// and let's add the founder, to save a step later | |
addMember(owner, 'founder'); | |
} | |
function delegateVote(address nominee) public onlyMembers returns(bool) { | |
uint ID = memberId[msg.sender]; | |
Member storage m = members[ID]; | |
m.delegated = true; | |
uint nomineeID = memberId[nominee]; | |
Member storage n = members[nomineeID]; | |
n.representingCount++; | |
n.weight = n.weight+m.weight; | |
return true; | |
} | |
function undoDelegation(address nominee) public onlyMembers returns(bool) { | |
uint ID = memberId[msg.sender]; | |
Member storage m = members[ID]; | |
m.delegated = false; | |
uint nomineeID = memberId[nominee]; | |
Member storage n = members[nomineeID]; | |
n.representingCount--; | |
n.weight = n.weight-m.weight; | |
return true; | |
} | |
/** | |
* Add member | |
* | |
* Make `targetMember` a member named `memberName` | |
* | |
* @param targetMember ethereum address to be added | |
* @param memberName public name for that member | |
* WARNING resets any delegations | |
*/ | |
function addMember(address targetMember, string memberName) onlyOwner public { | |
require(memberId[targetMember] ==0); | |
//if (id == 0) | |
memberId[targetMember] = members.length; | |
uint id = members.length++; | |
uint voteWeight = sharesTokenAddress.balanceOf(msg.sender); | |
members[id] = Member({member: targetMember, memberSince: now, name: memberName, delegated:false, weight: voteWeight, representingCount: 1}); | |
MembershipChanged(targetMember, true); | |
} | |
/** | |
* Remove member | |
* | |
* @notice Remove membership from `targetMember` | |
* | |
* @param targetMember ethereum address to be removed | |
*/ | |
function removeMember(address targetMember) onlyOwner public { | |
require(memberId[targetMember] != 0); | |
for (uint i = memberId[targetMember]; i<members.length-1; i++){ | |
members[i] = members[i+1]; | |
} | |
delete members[members.length-1]; | |
members.length--; | |
} | |
/** | |
* Change voting rules | |
* | |
* Make so that proposals need to be discussed for at least `minutesForDebate/60` hours | |
* and all voters combined must own more than `minimumSharesToPassAVote` shares of token `sharesAddress` to be executed | |
* | |
* @param sharesAddress token address | |
* @param minimumSharesToPassAVote proposal can vote only if the sum of shares held by all voters exceed this number | |
* @param minutesForDebate the minimum amount of delay between when a proposal is made and when it can be executed | |
*/ | |
function changeVotingRules(Token sharesAddress, uint minimumSharesToPassAVote, uint minutesForDebate) onlyOwner public { | |
sharesTokenAddress = Token(sharesAddress); | |
if (minimumSharesToPassAVote == 0 ) minimumSharesToPassAVote = 1; | |
minimumQuorum = minimumSharesToPassAVote; | |
debatingPeriodInMinutes = minutesForDebate; | |
ChangeOfRules(minimumQuorum, debatingPeriodInMinutes, sharesTokenAddress); | |
} | |
/** | |
* Add Proposal | |
* | |
* Propose to send `weiAmount / 1e18` ether to `beneficiary` for `jobDescription`. `transactionBytecode ? Contains : Does not contain` code. | |
* | |
* @param beneficiary who to send the ether to | |
* @param weiAmount amount of ether to send, in wei | |
* @param jobDescription Description of job | |
* @param transactionBytecode bytecode of transaction | |
*/ | |
function newProposal( | |
address beneficiary, | |
uint weiAmount, | |
string jobDescription, | |
bytes transactionBytecode | |
) | |
onlyMembers | |
public returns (uint proposalID) | |
{ | |
proposalID = proposals.length++; | |
Proposal storage p = proposals[proposalID]; | |
p.recipient = beneficiary; | |
p.amount = weiAmount; | |
p.description = jobDescription; | |
p.proposalHash = keccak256(beneficiary, weiAmount, transactionBytecode); | |
p.votingDeadline = now + debatingPeriodInMinutes * 1 minutes; | |
p.executed = false; | |
p.proposalPassed = false; | |
p.numberOfVotes = 0; | |
ProposalAdded(proposalID, beneficiary, weiAmount, jobDescription); | |
numProposals = proposalID+1; | |
p.multipleChoice = false; // no multiple choice for default yea/nay votes | |
p.numberOfChoices = 0; | |
return proposalID; | |
} | |
/** | |
* Add proposal in Ether | |
* | |
* Propose to send `etherAmount` ether to `beneficiary` for `jobDescription`. `transactionBytecode ? Contains : Does not contain` code. | |
* This is a convenience function to use if the amount to be given is in round number of ether units. | |
* | |
* @param beneficiary who to send the ether to | |
* @param etherAmount amount of ether to send | |
* @param jobDescription Description of job | |
* @param transactionBytecode bytecode of transaction | |
*/ | |
function newProposalInEther( | |
address beneficiary, | |
uint etherAmount, | |
string jobDescription, | |
bytes transactionBytecode | |
) | |
onlyMembers | |
public returns (uint proposalID) | |
{ | |
return newProposal(beneficiary, etherAmount * 1 ether, jobDescription, transactionBytecode); | |
} | |
function newMCQProposal ( | |
address beneficiary, | |
uint weiAmount, | |
string jobDescription, | |
uint numChoices, | |
bytes transactionBytecode | |
) | |
onlyMembers | |
public returns (uint proposalID) | |
{ | |
require(numChoices <= 10); | |
proposalID = proposals.length++; | |
Proposal storage p = proposals[proposalID]; | |
p.recipient = beneficiary; | |
p.amount = weiAmount; | |
p.description = jobDescription; | |
p.proposalHash = keccak256(beneficiary, weiAmount, transactionBytecode); | |
p.votingDeadline = now + debatingPeriodInMinutes * 1 minutes; | |
p.executed = false; | |
p.proposalPassed = false; | |
p.numberOfVotes = 0; | |
ProposalAdded(proposalID, beneficiary, weiAmount, jobDescription); | |
numProposals = proposalID+1; | |
p.multipleChoice = true; // no multiple choice for default yea/nay votes | |
p.numberOfChoices = numChoices; | |
return proposalID; | |
} | |
/** | |
* Check if a proposal code matches | |
* | |
* @param proposalNumber ID number of the proposal to query | |
* @param beneficiary who to send the ether to | |
* @param weiAmount amount of ether to send | |
* @param transactionBytecode bytecode of transaction | |
*/ | |
function checkProposalCode( | |
uint proposalNumber, | |
address beneficiary, | |
uint weiAmount, | |
bytes transactionBytecode | |
) | |
constant | |
public returns (bool codeChecksOut) | |
{ | |
Proposal storage p = proposals[proposalNumber]; | |
return p.proposalHash == keccak256(beneficiary, weiAmount, transactionBytecode); | |
} | |
/** | |
* Log a vote for a proposal | |
* | |
* Vote `supportsProposal? in support of : against` proposal #`proposalNumber` | |
* | |
* @param proposalNumber number of proposal | |
* @param supportsProposal either in favor or against it | |
*/ | |
function vote( | |
uint proposalNumber, | |
bool supportsProposal | |
) | |
onlyMembers | |
public returns (uint voteID) | |
{ | |
Proposal storage p = proposals[proposalNumber]; | |
require(!p.multipleChoice); | |
require(p.voted[msg.sender] != true); | |
uint ID = memberId[msg.sender]; | |
require(!members[ID].delegated); | |
voteID = p.votes.length++; | |
p.votes[voteID] = Vote({inSupport: supportsProposal, choice: 0, voter: msg.sender}); | |
p.voted[msg.sender] = true; | |
p.numberOfVotes = voteID +1; | |
Voted(proposalNumber, supportsProposal, msg.sender); | |
return voteID; | |
} | |
/** | |
* Log a vote for an MCQ proposal | |
* | |
* Vote `supportsProposal? in support of : against` proposal #`proposalNumber` | |
* | |
* @param proposalNumber number of proposal | |
* @param optionNumber the choice made in MCQ | |
*/ | |
function multipleChoiceVote( | |
uint proposalNumber, | |
uint8 optionNumber | |
) | |
onlyMembers | |
public returns (uint voteID) | |
{ | |
Proposal storage p = proposals[proposalNumber]; | |
require(p.multipleChoice); | |
require(optionNumber <= p.numberOfChoices && optionNumber > 0); | |
require(p.voted[msg.sender] != true); | |
uint ID = memberId[msg.sender]; | |
Member storage m = members[ID]; | |
require(!m.delegated); | |
voteID = p.votes.length++; | |
p.votes[voteID] = Vote({inSupport: true, choice: optionNumber, voter: msg.sender}); | |
p.voted[msg.sender] = true; | |
p.numberOfVotes = voteID +1; | |
uint count = m.representingCount; | |
p.choices[optionNumber] = p.choices[optionNumber] + count; | |
MCQ(proposalNumber, optionNumber, msg.sender); | |
return voteID; | |
} | |
/** | |
* Finish vote | |
* | |
* Count the votes proposal #`proposalNumber` and execute it if approved | |
* | |
* @param proposalNumber proposal number | |
* @param transactionBytecode optional: if the transaction contained a bytecode, you need to send it | |
*/ | |
function executeProposal(uint proposalNumber, bytes transactionBytecode) public { | |
Proposal storage p = proposals[proposalNumber]; | |
require(now > p.votingDeadline // If it is past the voting deadline | |
&& !p.executed // and it has not already been executed | |
&& !p.multipleChoice | |
&& p.proposalHash == keccak256(p.recipient, p.amount, transactionBytecode)); // and the supplied code matches the proposal... | |
// ...then tally the results | |
uint quorum = 0; | |
uint yea = 0; | |
uint nay = 0; | |
for (uint i = 0; i < p.votes.length; ++i) { | |
Vote storage v = p.votes[i]; | |
uint ID = memberId[v.voter]; | |
uint voteWeight = members[ID].weight;//sharesTokenAddress.balanceOf(v.voter); | |
quorum += voteWeight; | |
if (v.inSupport) { | |
yea += voteWeight; | |
} else { | |
nay += voteWeight; | |
} | |
} | |
require(quorum >= minimumQuorum); // Check if a minimum quorum has been reached | |
if (yea > nay ) { | |
// Proposal passed; execute the transaction | |
p.executed = true; | |
require(p.recipient.call.value(p.amount)(transactionBytecode)); | |
p.proposalPassed = true; | |
} else { | |
// Proposal failed | |
p.proposalPassed = false; | |
} | |
// Fire Events | |
ProposalTallied(proposalNumber, int(yea) - int(nay), quorum, p.proposalPassed); | |
uint statistic = quorum * 100000 / sharesTokenAddress.totalSupply(); // the percentage of "coins" that voted; at 5 degrees precision | |
Summary(proposalNumber, statistic); | |
} | |
/** | |
* Finish vote | |
* | |
* Count the votes proposal #`proposalNumber` and execute it if approved | |
* | |
* @param proposalNumber proposal number | |
* @param transactionBytecode optional: if the transaction contained a bytecode, you need to send it | |
*/ | |
function analyzeMCQProposal(uint proposalNumber, bytes transactionBytecode) public { | |
Proposal storage p = proposals[proposalNumber]; | |
require(now > p.votingDeadline // If it is past the voting deadline | |
&& !p.executed // and it has not already been executed | |
&& p.multipleChoice | |
&& p.proposalHash == keccak256(p.recipient, p.amount, transactionBytecode)); // and the supplied code matches the proposal... | |
uint max = 0; | |
uint index = 0; | |
for(uint j = 1; j <= p.numberOfChoices; j++) { | |
if(p.choices[j] >= max) { | |
max = p.choices[j]; | |
index = j; | |
} | |
} | |
uint quorum = 0; | |
for (uint i = 0; i < p.votes.length; ++i) { | |
Vote storage v = p.votes[i]; | |
uint ID = memberId[v.voter]; | |
uint voteWeight = members[ID].weight;//sharesTokenAddress.balanceOf(v.voter); | |
quorum += voteWeight; | |
} | |
require(quorum >= minimumQuorum); // Check if a minimum quorum has been reached | |
uint statistic = quorum * 100000 / sharesTokenAddress.totalSupply(); // the percentage of "coins" that voted; at 5 degrees precision | |
Summary(proposalNumber, statistic); | |
WinningMCQ(index, max); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma solidity 0.4.24; | |
import './Token.sol'; | |
import './TokenController.sol'; | |
import './SafeMath.sol'; | |
// assign tokenVault address to BitdollarVault to enable transferFrom super user privilege | |
// assign tokenController address to BitdollarCampaign to enable generateTokens to be called by the contract | |
contract BitdollarToken is Token, TokenController, SafeMath { | |
string public constant name = "Bitdollar Fund Token"; | |
string public constant symbol = "BTD"; | |
uint8 public constant decimals = 18; // ERC20 standard is uint8 not uint256 | |
string public version = "1.0"; | |
// the maximum number of Bitdollars there may exist is capped at 1 billion tokens | |
uint256 public constant maximumTokenIssue = 1000000000 * 10**18; | |
uint256 internal totalSupply_; | |
mapping (address => uint256) internal balances; | |
mapping (address => mapping (address => uint256)) internal allowed; | |
bool internal tradeable = false; | |
modifier isTradeable { | |
require(tradeable); | |
_; | |
} | |
function toggleTrading(bool _toggle) external onlyTokenController { | |
tradeable = _toggle; | |
} | |
function totalSupply() public view returns (uint256 total) { | |
return totalSupply_; | |
} | |
function transfer(address _to, uint256 _value) onlyPayloadSize(2) isTradeable public returns (bool success) { | |
return _transfer(msg.sender, _to, _value); | |
} | |
function transferFrom(address _from, address _to, uint256 _value) onlyPayloadSize(3) isTradeable public returns (bool success) { | |
// The tokenVault contract can move tokens around at will | |
if (msg.sender != tokenVault) { | |
require(allowed[_from][msg.sender] >= _value); | |
allowed[_from][msg.sender] = safeSub(allowed[_from][msg.sender], _value); | |
} | |
return _transfer(_from, _to, _value); | |
} | |
function _transfer(address _from, address _to, uint256 _value) internal returns (bool) { | |
require((_to != address(0)) && (_to != address(this))); | |
require(balances[_from] >= _value && _value > 0); | |
balances[_from] = safeSub(balances[_from], _value); | |
balances[_to] = safeAdd(balances[_to], _value); | |
emit Transfer(_from, _to, _value); | |
return true; | |
} | |
/* | |
function transfer(address _to, uint256 _value) onlyPayloadSize(2) isTradeable public returns (bool success) { | |
require(_to != address(0)); | |
require(balances[msg.sender] >= _value && _value > 0); | |
balances[msg.sender] = safeSub(balances[msg.sender], _value); | |
balances[_to] = safeAdd(balances[_to], _value); | |
Transfer(msg.sender, _to, _value); | |
return true; | |
} | |
function transferFrom(address _from, address _to, uint256 _value) onlyPayloadSize(3) isTradeable public returns (bool success) { | |
require(_to != address(0)); | |
require(balances[_from] >= _value && allowed[_from][msg.sender] >= _value && _value > 0); | |
balances[_from] = safeSub(balances[_from], _value); | |
balances[_to] = safeAdd(balances[_to], _value); | |
allowed[_from][msg.sender] = safeSub(allowed[_from][msg.sender], _value); | |
Transfer(_from, _to, _value); | |
return true; | |
} | |
*/ | |
function balanceOf(address _owner) public view returns (uint256 balance) { | |
return balances[_owner]; | |
} | |
// To change the approve amount you first have to reduce the addresses' | |
// allowance to zero by calling 'approve(_spender, 0)' if it is not | |
// already 0 to mitigate the race condition described here: | |
// https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 | |
function approve(address _spender, uint256 _value) onlyPayloadSize(2) public returns (bool success) { | |
require((_value == 0) || (allowed[msg.sender][_spender] == 0)); | |
allowed[msg.sender][_spender] = _value; | |
emit Approval(msg.sender, _spender, _value); | |
return true; | |
} | |
function changeApproval(address _spender, uint256 _oldValue, uint256 _newValue) onlyPayloadSize(3) public returns (bool success) { | |
require(allowed[msg.sender][_spender] == _oldValue); | |
allowed[msg.sender][_spender] = _newValue; | |
emit Approval(msg.sender, _spender, _newValue); | |
return true; | |
} | |
function allowance(address _owner, address _spender) public view returns (uint256 remaining) { | |
return allowed[_owner][_spender]; | |
} | |
function isTrading() public view returns (bool trading) { | |
return tradeable; | |
} | |
// if ether is sent to this address revert the transaction | |
function () public payable { | |
revert(); | |
} | |
function generateTokens(address _to, uint _amount) public onlyTokenController returns (bool) { | |
require(safeAdd(totalSupply_, _amount) <= maximumTokenIssue); | |
totalSupply_ = safeAdd(totalSupply_, _amount); | |
balances[_to] = safeAdd(balances[_to], _amount); | |
emit Transfer(address(0), _to, _amount); | |
return true; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma solidity 0.4.24; | |
import './BitdollarToken.sol'; | |
import './BitdollarCampaign.sol'; | |
import './SafeMath.sol'; | |
// assign TokenController tokenVault parameter to this contracts address | |
// has super user privelege to transfer tokens from any account to fundWallet | |
// deploy BitdollarToken and BitdollarCampaign first and supply their addresses as parameter to this contracts constructor | |
contract BitdollarVault is SafeMath { | |
BitdollarToken bitdollarTokenContract; | |
BitdollarCampaign bitdollarCampaignContract; | |
//uint256 previousUpdateTime; | |
// root control | |
address public fundWallet; | |
// control of liquidity and limited control of updatePrice | |
address public controlWallet; | |
// map participant address to a withdrawal request | |
mapping (address => Withdrawal) public withdrawals; | |
struct Withdrawal { | |
uint256 tokens; | |
uint256 time; // time for each withdrawal is set to the previousUpdateTime | |
} | |
event AddLiquidity(uint256 ethAmount); | |
event RemoveLiquidity(uint256 ethAmount); | |
event WithdrawRequest(address indexed participant, uint256 amountTokens); | |
event Withdraw(address indexed participant, uint256 amountTokens, uint256 etherAmount); | |
modifier onlyFundWallet { | |
require(msg.sender == fundWallet); | |
_; | |
} | |
modifier onlyManagingWallets { | |
require(msg.sender == controlWallet || msg.sender == fundWallet); | |
_; | |
} | |
constructor(address controlWalletInput, address tokenContractInput, address campaignContractInput) public { | |
controlWallet = controlWalletInput; | |
bitdollarTokenContract = BitdollarToken(tokenContractInput); | |
bitdollarCampaignContract = BitdollarCampaign(campaignContractInput); | |
} | |
function requestWithdrawal(uint256 amountTokensToWithdraw) external { | |
//require(block.number > fundingEndBlock); | |
require(amountTokensToWithdraw > 0); | |
address participant = msg.sender; | |
// transferFrom already checks for sufficient balance | |
//require(_balanceOf(participant) >= amountTokensToWithdraw); | |
require(withdrawals[participant].tokens == 0); // participant cannot have outstanding withdrawals | |
//balances[participant] = safeSub(balances[participant], amountTokensToWithdraw); | |
//uint256 remainingBalance = safeSub(_balanceOf(participant), amountTokensToWithdraw); | |
uint256 previousUpdateTime = bitdollarCampaignContract.getPreviousUpdateTime(); | |
withdrawals[participant] = Withdrawal({tokens: amountTokensToWithdraw, time: previousUpdateTime}); | |
WithdrawRequest(participant, amountTokensToWithdraw); | |
} | |
function withdraw() external { | |
address participant = msg.sender; | |
uint256 tokens = withdrawals[participant].tokens; | |
require(tokens > 0); // participant must have requested a withdrawal | |
uint256 requestTime = withdrawals[participant].time; | |
// obtain the next price that was set after the request | |
// Price storage price = prices[requestTime]; | |
uint256 numerator; | |
uint256 denominator; | |
(numerator, denominator) = bitdollarCampaignContract.getPrice(requestTime); | |
require(numerator > 0); // price must have been set | |
uint256 withdrawValue = safeMul(tokens, denominator) / numerator; | |
// if contract ethbal > then send + transfer tokens to fundWallet, otherwise give tokens back | |
withdrawals[participant].tokens = 0; | |
if (address(this).balance >= withdrawValue) | |
enact_withdrawal_greater_equal(participant, withdrawValue, tokens); | |
else | |
enact_withdrawal_less(participant, withdrawValue, tokens); | |
} | |
function enact_withdrawal_greater_equal(address participant, uint256 withdrawValue, uint256 tokens) | |
internal | |
{ | |
assert(address(this).balance >= withdrawValue); | |
// balances[fundWallet] = safeAdd(balances[fundWallet], tokens); | |
// will revert if tradeable boolean is false in the token contract | |
require(bitdollarTokenContract.transferFrom(participant, fundWallet, tokens)); | |
participant.transfer(withdrawValue); | |
Withdraw(participant, tokens, withdrawValue); | |
} | |
function enact_withdrawal_less(address participant, uint256 withdrawValue, uint256 tokens) | |
internal | |
{ | |
assert(address(this).balance < withdrawValue); | |
//balances[participant] = safeAdd(balances[participant], tokens); | |
Withdraw(participant, tokens, 0); // indicate a failed withdrawal | |
} | |
function checkWithdrawValue(uint256 amountTokensToWithdraw) constant public returns (uint256 etherValue) { | |
require(amountTokensToWithdraw > 0); | |
require(_balanceOf(msg.sender) >= amountTokensToWithdraw); | |
uint256 numerator; | |
uint256 denominator; | |
(numerator, denominator) = bitdollarCampaignContract.getCurrentPrice(); | |
//is this computation correct? | |
uint256 withdrawValue = safeMul(amountTokensToWithdraw, denominator) / numerator; | |
//is this check necessary? it only returns the eth value?? | |
//require(this.balance >= withdrawValue); | |
return withdrawValue; | |
} | |
// allow fundWallet or controlWallet to add ether to contract | |
function addLiquidity() external onlyManagingWallets payable { | |
require(msg.value > 0); | |
AddLiquidity(msg.value); | |
} | |
// allow fundWallet to remove ether from contract | |
function removeLiquidity(uint256 amount) external onlyManagingWallets { | |
require(amount <= address(this).balance); | |
fundWallet.transfer(amount); | |
RemoveLiquidity(amount); | |
} | |
function changeFundWallet(address newFundWallet) external onlyFundWallet { | |
require(newFundWallet != address(0)); | |
fundWallet = newFundWallet; | |
} | |
function changeControlWallet(address newControlWallet) external onlyFundWallet { | |
require(newControlWallet != address(0)); | |
controlWallet = newControlWallet; | |
} | |
function _balanceOf(address _addr) internal view returns(uint256) { | |
return bitdollarTokenContract.balanceOf(_addr); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma solidity 0.4.24; | |
import './Token.sol'; | |
import './SafeMath.sol'; | |
/** | |
* @title BTDVesting1Yr | |
* @dev BTDVesting1Yr is a token holder contract that allows the specified beneficiary | |
* to claim stored tokens after 3 month intervals | |
*/ | |
contract BTDVesting1Yr is SafeMath { | |
address public beneficiary; | |
uint256 public fundingEndBlock; | |
bool private initClaim = false; // state tracking variables | |
uint256 public firstRelease; // vesting times | |
bool private firstDone = false; | |
uint256 public secondRelease; | |
bool private secondDone = false; | |
uint256 public thirdRelease; | |
bool private thirdDone = false; | |
uint256 public fourthRelease; | |
Token public ERC20Token; // ERC20 basic token contract to hold | |
enum Stages { | |
initClaim, | |
firstRelease, | |
secondRelease, | |
thirdRelease, | |
fourthRelease | |
} | |
Stages public stage = Stages.initClaim; | |
modifier atStage(Stages _stage) { | |
if(stage == _stage) _; | |
} | |
function BTDVesting1Yr(address _beneficiary, address _token, uint256 fundingEndBlockInput) public { | |
require(_token != address(0)); | |
beneficiary = _beneficiary; | |
fundingEndBlock = fundingEndBlockInput; | |
ERC20Token = Token(_token); | |
} | |
function changeBeneficiary(address newBeneficiary) external { | |
require(newBeneficiary != address(0)); | |
require(msg.sender == beneficiary); | |
beneficiary = newBeneficiary; | |
} | |
function updateFundingEndBlock(uint256 newFundingEndBlock) public { | |
require(msg.sender == beneficiary); | |
require(block.number < fundingEndBlock); | |
require(block.number < newFundingEndBlock); | |
fundingEndBlock = newFundingEndBlock; | |
} | |
function checkBalance() constant public returns (uint256 tokenBalance) { | |
return ERC20Token.balanceOf(this); | |
} | |
// in total 15% of BTD tokens will be sent to this contract | |
// EXPENSE ALLOCATION: 6.0% | TEAM ALLOCATION: 9.0% (vest over 1 years) | |
// 3.0% - Marketing | initalPayment: 1.8% | |
// 1% - Security | firstRelease: 1.8% | |
// 1% - Legal | secondRelease: 1.8% | |
// 0.5% - Advisors | thirdRelease: 1.8% | |
// 0.5% - Bounty | fourthRelease: 1.8% | |
// initial claim is tot expenses + initial team payment | |
// initial claim is thus (6.0 + 1.8)/15 = 52.0% of BTD tokens sent here | |
// each other release (for team) is 12.0% of tokens sent here | |
function claim() external { | |
require(msg.sender == beneficiary); | |
require(block.number > fundingEndBlock); | |
uint256 balance = ERC20Token.balanceOf(this); | |
// in reverse order so stages changes don't carry within one claim | |
fourth_release(balance); | |
third_release(balance); | |
second_release(balance); | |
first_release(balance); | |
init_claim(balance); | |
} | |
function nextStage() private { | |
stage = Stages(uint256(stage) + 1); | |
} | |
function init_claim(uint256 balance) private atStage(Stages.initClaim) { | |
firstRelease = now + 13 weeks; // assign 4 claiming times | |
secondRelease = firstRelease + 13 weeks; | |
thirdRelease = secondRelease + 13 weeks; | |
fourthRelease = thirdRelease + 13 weeks; | |
uint256 amountToTransfer = safeMul(balance, 52000000000) / 100000000000; | |
ERC20Token.transfer(beneficiary, amountToTransfer); // now 48.0% tokens left | |
nextStage(); | |
} | |
function first_release(uint256 balance) private atStage(Stages.firstRelease) { | |
require(now > firstRelease); | |
uint256 amountToTransfer = balance / 4; | |
ERC20Token.transfer(beneficiary, amountToTransfer); // send 25 % of team releases | |
nextStage(); | |
} | |
function second_release(uint256 balance) private atStage(Stages.secondRelease) { | |
require(now > secondRelease); | |
uint256 amountToTransfer = balance / 3; | |
ERC20Token.transfer(beneficiary, amountToTransfer); // send 25 % of team releases | |
nextStage(); | |
} | |
function third_release(uint256 balance) private atStage(Stages.thirdRelease) { | |
require(now > thirdRelease); | |
uint256 amountToTransfer = balance / 2; | |
ERC20Token.transfer(beneficiary, amountToTransfer); // send 25 % of team releases | |
nextStage(); | |
} | |
function fourth_release(uint256 balance) private atStage(Stages.fourthRelease) { | |
require(now > fourthRelease); | |
ERC20Token.transfer(beneficiary, balance); // send remaining 25 % of team releases | |
} | |
function claimOtherTokens(address _token) external { | |
require(msg.sender == beneficiary); | |
require(_token != address(0)); | |
Token token = Token(_token); | |
require(token != ERC20Token); | |
uint256 balance = token.balanceOf(this); | |
token.transfer(beneficiary, balance); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma solidity 0.4.24; | |
import './Token.sol'; | |
import './SafeMath.sol'; | |
/** | |
* @title BTDVesting4Wk | |
* @dev BTDVesting4Wk is a token holder contract that allows the specified beneficiary | |
* to claim stored tokens after 1 week intervals | |
*/ | |
contract BTDVesting4Wk is SafeMath { | |
address public beneficiary; | |
uint256 public fundingEndBlock; | |
bool private initClaim = false; // state tracking variables | |
uint256 public firstRelease; // vesting times | |
bool private firstDone = false; | |
uint256 public secondRelease; | |
bool private secondDone = false; | |
uint256 public thirdRelease; | |
bool private thirdDone = false; | |
uint256 public fourthRelease; | |
Token public ERC20Token; // ERC20 basic token contract to hold | |
enum Stages { | |
initClaim, | |
firstRelease, | |
secondRelease, | |
thirdRelease, | |
fourthRelease | |
} | |
Stages public stage = Stages.initClaim; | |
modifier atStage(Stages _stage) { | |
if(stage == _stage) _; | |
} | |
function BTDVesting4Wk(address _beneficiary, address _token, uint256 fundingEndBlockInput) public { | |
require(_token != address(0)); | |
beneficiary = _beneficiary; | |
fundingEndBlock = fundingEndBlockInput; | |
ERC20Token = Token(_token); | |
} | |
function changeBeneficiary(address newBeneficiary) external { | |
require(newBeneficiary != address(0)); | |
require(msg.sender == beneficiary); | |
beneficiary = newBeneficiary; | |
} | |
function updateFundingEndBlock(uint256 newFundingEndBlock) public { | |
require(msg.sender == beneficiary); | |
require(block.number < fundingEndBlock); | |
require(block.number < newFundingEndBlock); | |
fundingEndBlock = newFundingEndBlock; | |
} | |
function checkBalance() constant public returns (uint256 tokenBalance) { | |
return ERC20Token.balanceOf(this); | |
} | |
function claim() external { | |
require(msg.sender == beneficiary); | |
require(block.number > fundingEndBlock); | |
uint256 balance = ERC20Token.balanceOf(this); | |
// in reverse order so stages changes don't carry within one claim | |
fourth_release(balance); | |
third_release(balance); | |
second_release(balance); | |
first_release(balance); | |
init_claim(balance); | |
} | |
function nextStage() private { | |
stage = Stages(uint256(stage) + 1); | |
} | |
function init_claim(uint256 balance) private atStage(Stages.initClaim) { | |
firstRelease = now + 1 weeks; // assign 4 claiming times | |
secondRelease = firstRelease + 1 weeks; | |
thirdRelease = secondRelease + 1 weeks; | |
fourthRelease = thirdRelease + 1 weeks; | |
uint256 amountToTransfer = safeMul(balance, 20000000000) / 100000000000; | |
ERC20Token.transfer(beneficiary, amountToTransfer); // now 80.0% tokens left | |
nextStage(); | |
} | |
function first_release(uint256 balance) private atStage(Stages.firstRelease) { | |
require(now > firstRelease); | |
uint256 amountToTransfer = balance / 4; | |
ERC20Token.transfer(beneficiary, amountToTransfer); // send 25 % | |
nextStage(); | |
} | |
function second_release(uint256 balance) private atStage(Stages.secondRelease) { | |
require(now > secondRelease); | |
uint256 amountToTransfer = balance / 3; | |
ERC20Token.transfer(beneficiary, amountToTransfer); // send 25 % | |
nextStage(); | |
} | |
function third_release(uint256 balance) private atStage(Stages.thirdRelease) { | |
require(now > thirdRelease); | |
uint256 amountToTransfer = balance / 2; | |
ERC20Token.transfer(beneficiary, amountToTransfer); // send 25 % | |
nextStage(); | |
} | |
function fourth_release(uint256 balance) private atStage(Stages.fourthRelease) { | |
require(now > fourthRelease); | |
ERC20Token.transfer(beneficiary, balance); // send remaining 25 % | |
} | |
function claimOtherTokens(address _token) external { | |
require(msg.sender == beneficiary); | |
require(_token != address(0)); | |
Token token = Token(_token); | |
require(token != ERC20Token); | |
uint256 balance = token.balanceOf(this); | |
token.transfer(beneficiary, balance); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma solidity 0.4.18; | |
contract Token { // ERC20 standard | |
function balanceOf(address _owner) constant public returns (uint256 balance); | |
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); | |
function allowance(address _owner, address _spender) constant public returns (uint256 remaining); | |
event Transfer(address indexed _from, address indexed _to, uint256 _value); | |
event Approval(address indexed _owner, address indexed _spender, uint256 _value); | |
} | |
contract SafeMath { | |
function safeMul(uint a, uint b) internal pure returns (uint) { | |
uint c = a * b; | |
assert(a == 0 || c / a == b); | |
return c; | |
} | |
function safeSub(uint a, uint b) internal pure returns (uint) { | |
assert(b <= a); | |
return a - b; | |
} | |
function safeAdd(uint a, uint b) internal pure returns (uint) { | |
uint c = a + b; | |
assert(c>=a && c>=b); | |
return c; | |
} | |
modifier onlyPayloadSize(uint numWords) { | |
assert(msg.data.length >= numWords * 32 + 4); | |
_; | |
} | |
} | |
contract TokenController { | |
// assign to BitdollarCampaign | |
address public tokenController; | |
// assign to BitdollarVault | |
address public tokenVault; | |
// root control | |
address public fundWallet; | |
function TokenController() public { | |
fundWallet = msg.sender; | |
} | |
modifier onlyTokenController { | |
require(msg.sender == tokenController); | |
_; | |
} | |
modifier onlyFundWallet { | |
require(msg.sender == fundWallet); | |
_; | |
} | |
function changeTokenController(address newTokenController, address newTokenVault) external onlyTokenController { | |
require(newTokenController != address(0) && newTokenVault != address(0)); | |
tokenController = newTokenController; | |
tokenVault = newTokenVault; | |
} | |
} | |
/** | |
* @title BTDVesting1Yr | |
* @dev BTDVesting1Yr is a token holder contract that allows the specified beneficiary | |
* to claim stored tokens after 3 month intervals | |
*/ | |
contract BTDVesting1Yr is SafeMath { | |
address public beneficiary; | |
uint256 public fundingEndBlock; | |
bool private initClaim = false; // state tracking variables | |
uint256 public firstRelease; // vesting times | |
bool private firstDone = false; | |
uint256 public secondRelease; | |
bool private secondDone = false; | |
uint256 public thirdRelease; | |
bool private thirdDone = false; | |
uint256 public fourthRelease; | |
Token public ERC20Token; // ERC20 basic token contract to hold | |
enum Stages { | |
initClaim, | |
firstRelease, | |
secondRelease, | |
thirdRelease, | |
fourthRelease | |
} | |
Stages public stage = Stages.initClaim; | |
modifier atStage(Stages _stage) { | |
if(stage == _stage) _; | |
} | |
function BTDVesting1Yr(address _beneficiary, address _token, uint256 fundingEndBlockInput) public { | |
require(_token != address(0)); | |
beneficiary = _beneficiary; | |
fundingEndBlock = fundingEndBlockInput; | |
ERC20Token = Token(_token); | |
} | |
function changeBeneficiary(address newBeneficiary) external { | |
require(newBeneficiary != address(0)); | |
require(msg.sender == beneficiary); | |
beneficiary = newBeneficiary; | |
} | |
function updateFundingEndBlock(uint256 newFundingEndBlock) public { | |
require(msg.sender == beneficiary); | |
require(block.number < fundingEndBlock); | |
require(block.number < newFundingEndBlock); | |
fundingEndBlock = newFundingEndBlock; | |
} | |
function checkBalance() constant public returns (uint256 tokenBalance) { | |
return ERC20Token.balanceOf(this); | |
} | |
// in total 15% of BTD tokens will be sent to this contract | |
// EXPENSE ALLOCATION: 6.0% | TEAM ALLOCATION: 9.0% (vest over 1 years) | |
// 3.0% - Marketing | initalPayment: 1.8% | |
// 1% - Security | firstRelease: 1.8% | |
// 1% - Advisors | secondRelease: 1.8% | |
// 0.5% - Legal | thirdRelease: 1.8% | |
// 0.5% - Bounty | fourthRelease: 1.8% | |
// initial claim is tot expenses + initial team payment | |
// initial claim is thus (6.0 + 1.8)/15 = 52.0% of BTD tokens sent here | |
// each other release (for team) is 12.0% of tokens sent here | |
function claim() external { | |
require(msg.sender == beneficiary); | |
require(block.number > fundingEndBlock); | |
uint256 balance = ERC20Token.balanceOf(this); | |
// in reverse order so stages changes don't carry within one claim | |
fourth_release(balance); | |
third_release(balance); | |
second_release(balance); | |
first_release(balance); | |
init_claim(balance); | |
} | |
function nextStage() private { | |
stage = Stages(uint256(stage) + 1); | |
} | |
function init_claim(uint256 balance) private atStage(Stages.initClaim) { | |
firstRelease = now + 13 weeks; // assign 4 claiming times | |
secondRelease = firstRelease + 13 weeks; | |
thirdRelease = secondRelease + 13 weeks; | |
fourthRelease = thirdRelease + 13 weeks; | |
uint256 amountToTransfer = safeMul(balance, 52000000000) / 100000000000; | |
ERC20Token.transfer(beneficiary, amountToTransfer); // now 48.0% tokens left | |
nextStage(); | |
} | |
function first_release(uint256 balance) private atStage(Stages.firstRelease) { | |
require(now > firstRelease); | |
uint256 amountToTransfer = balance / 4; | |
ERC20Token.transfer(beneficiary, amountToTransfer); // send 25 % of team releases | |
nextStage(); | |
} | |
function second_release(uint256 balance) private atStage(Stages.secondRelease) { | |
require(now > secondRelease); | |
uint256 amountToTransfer = balance / 3; | |
ERC20Token.transfer(beneficiary, amountToTransfer); // send 25 % of team releases | |
nextStage(); | |
} | |
function third_release(uint256 balance) private atStage(Stages.thirdRelease) { | |
require(now > thirdRelease); | |
uint256 amountToTransfer = balance / 2; | |
ERC20Token.transfer(beneficiary, amountToTransfer); // send 25 % of team releases | |
nextStage(); | |
} | |
function fourth_release(uint256 balance) private atStage(Stages.fourthRelease) { | |
require(now > fourthRelease); | |
ERC20Token.transfer(beneficiary, balance); // send remaining 25 % of team releases | |
} | |
function claimOtherTokens(address _token) external { | |
require(msg.sender == beneficiary); | |
require(_token != address(0)); | |
Token token = Token(_token); | |
require(token != ERC20Token); | |
uint256 balance = token.balanceOf(this); | |
token.transfer(beneficiary, balance); | |
} | |
} | |
/** | |
* @title BTDVesting4Wk | |
* @dev BTDVesting4Wk is a token holder contract that allows the specified beneficiary | |
* to claim stored tokens after 1 week intervals | |
*/ | |
contract BTDVesting4Wk is SafeMath { | |
address public beneficiary; | |
uint256 public fundingEndBlock; | |
bool private initClaim = false; // state tracking variables | |
uint256 public firstRelease; // vesting times | |
bool private firstDone = false; | |
uint256 public secondRelease; | |
bool private secondDone = false; | |
uint256 public thirdRelease; | |
bool private thirdDone = false; | |
uint256 public fourthRelease; | |
Token public ERC20Token; // ERC20 basic token contract to hold | |
enum Stages { | |
initClaim, | |
firstRelease, | |
secondRelease, | |
thirdRelease, | |
fourthRelease | |
} | |
Stages public stage = Stages.initClaim; | |
modifier atStage(Stages _stage) { | |
if(stage == _stage) _; | |
} | |
function BTDVesting4Wk(address _beneficiary, address _token, uint256 fundingEndBlockInput) public { | |
require(_token != address(0)); | |
beneficiary = _beneficiary; | |
fundingEndBlock = fundingEndBlockInput; | |
ERC20Token = Token(_token); | |
} | |
function changeBeneficiary(address newBeneficiary) external { | |
require(newBeneficiary != address(0)); | |
require(msg.sender == beneficiary); | |
beneficiary = newBeneficiary; | |
} | |
function updateFundingEndBlock(uint256 newFundingEndBlock) public { | |
require(msg.sender == beneficiary); | |
require(block.number < fundingEndBlock); | |
require(block.number < newFundingEndBlock); | |
fundingEndBlock = newFundingEndBlock; | |
} | |
function checkBalance() constant public returns (uint256 tokenBalance) { | |
return ERC20Token.balanceOf(this); | |
} | |
function claim() external { | |
require(msg.sender == beneficiary); | |
require(block.number > fundingEndBlock); | |
uint256 balance = ERC20Token.balanceOf(this); | |
// in reverse order so stages changes don't carry within one claim | |
fourth_release(balance); | |
third_release(balance); | |
second_release(balance); | |
first_release(balance); | |
init_claim(balance); | |
} | |
function nextStage() private { | |
stage = Stages(uint256(stage) + 1); | |
} | |
function init_claim(uint256 balance) private atStage(Stages.initClaim) { | |
firstRelease = now + 1 weeks; // assign 4 claiming times | |
secondRelease = firstRelease + 1 weeks; | |
thirdRelease = secondRelease + 1 weeks; | |
fourthRelease = thirdRelease + 1 weeks; | |
uint256 amountToTransfer = safeMul(balance, 20000000000) / 100000000000; | |
ERC20Token.transfer(beneficiary, amountToTransfer); // now 80.0% tokens left | |
nextStage(); | |
} | |
function first_release(uint256 balance) private atStage(Stages.firstRelease) { | |
require(now > firstRelease); | |
uint256 amountToTransfer = balance / 4; | |
ERC20Token.transfer(beneficiary, amountToTransfer); // send 25 % | |
nextStage(); | |
} | |
function second_release(uint256 balance) private atStage(Stages.secondRelease) { | |
require(now > secondRelease); | |
uint256 amountToTransfer = balance / 3; | |
ERC20Token.transfer(beneficiary, amountToTransfer); // send 25 % | |
nextStage(); | |
} | |
function third_release(uint256 balance) private atStage(Stages.thirdRelease) { | |
require(now > thirdRelease); | |
uint256 amountToTransfer = balance / 2; | |
ERC20Token.transfer(beneficiary, amountToTransfer); // send 25 % | |
nextStage(); | |
} | |
function fourth_release(uint256 balance) private atStage(Stages.fourthRelease) { | |
require(now > fourthRelease); | |
ERC20Token.transfer(beneficiary, balance); // send remaining 25 % | |
} | |
function claimOtherTokens(address _token) external { | |
require(msg.sender == beneficiary); | |
require(_token != address(0)); | |
Token token = Token(_token); | |
require(token != ERC20Token); | |
uint256 balance = token.balanceOf(this); | |
token.transfer(beneficiary, balance); | |
} | |
} | |
// assign tokenVault address to BitdollarVault to enable destroyTokens privelege | |
// assign tokenController address to BitdollarCampaign to enable generateTokens to be called by the contract | |
contract BitdollarToken is Token, TokenController, SafeMath { | |
string public constant name = "Bitdollar Fund Token"; | |
string public constant symbol = "BTD"; | |
uint8 public constant decimals = 18; // ERC20 standard is uint8 not uint256 | |
string public version = "1.0"; | |
// the maximum number of Bitdollars there may exist is capped at 1 billion tokens | |
uint256 public constant maximumTokenIssue = 1000000000 * 10**18; | |
uint256 internal totalSupply_; | |
mapping (address => uint256) internal balances; | |
mapping (address => mapping (address => uint256)) internal allowed; | |
bool internal tradeable = false; | |
modifier isTradeable { | |
require(tradeable); | |
_; | |
} | |
function toggleTrading(bool _toggle) external onlyTokenController { | |
tradeable = _toggle; | |
} | |
function totalSupply() public view returns (uint256 total) { | |
return totalSupply_; | |
} | |
function transfer(address _to, uint256 _value) onlyPayloadSize(2) isTradeable public returns (bool success) { | |
return _transfer(msg.sender, _to, _value); | |
} | |
function transferFrom(address _from, address _to, uint256 _value) onlyPayloadSize(3) isTradeable public returns (bool success) { | |
if(msg.sender != tokenVault) { | |
require(allowed[_from][msg.sender] >= _value); | |
allowed[_from][msg.sender] = safeSub(allowed[_from][msg.sender], _value); | |
} | |
return _transfer(_from, _to, _value); | |
} | |
function _transfer(address _from, address _to, uint256 _value) internal returns (bool) { | |
require((_to != address(0)) && (_to != address(this))); | |
require(balances[_from] >= _value && _value > 0); | |
balances[_from] = safeSub(balances[_from], _value); | |
balances[_to] = safeAdd(balances[_to], _value); | |
Transfer(_from, _to, _value); | |
return true; | |
} | |
function balanceOf(address _owner) public view returns (uint256 balance) { | |
return balances[_owner]; | |
} | |
// To change the approve amount you first have to reduce the addresses' | |
// allowance to zero by calling 'approve(_spender, 0)' if it is not | |
// already 0 to mitigate the race condition described here: | |
// https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 | |
function approve(address _spender, uint256 _value) onlyPayloadSize(2) public returns (bool success) { | |
require((_value == 0) || (allowed[msg.sender][_spender] == 0)); | |
allowed[msg.sender][_spender] = _value; | |
Approval(msg.sender, _spender, _value); | |
return true; | |
} | |
function changeApproval(address _spender, uint256 _oldValue, uint256 _newValue) onlyPayloadSize(3) public returns (bool success) { | |
require(allowed[msg.sender][_spender] == _oldValue); | |
allowed[msg.sender][_spender] = _newValue; | |
Approval(msg.sender, _spender, _newValue); | |
return true; | |
} | |
function allowance(address _owner, address _spender) public view returns (uint256 remaining) { | |
return allowed[_owner][_spender]; | |
} | |
function isTrading() public view returns (bool trading) { | |
return tradeable; | |
} | |
// if ether is sent to this address revert the transaction | |
function () public payable { | |
revert(); | |
} | |
function generateTokens(address _to, uint _amount) public onlyTokenController returns (bool) { | |
require(safeAdd(totalSupply_, _amount) <= maximumTokenIssue); | |
totalSupply_ = safeAdd(totalSupply_, _amount); | |
balances[_to] = safeAdd(balances[_to], _amount); | |
Transfer(address(0), _to, _amount); | |
return true; | |
} | |
function destroyTokens(address _from, uint _amount) public onlyFundWallet returns (bool) { | |
// cannot destroy tokens outside of fundWallet | |
require(_from == fundWallet); | |
require(_amount <= balances[_from]); | |
balances[_from] = safeSub(balances[_from],_amount); | |
totalSupply_ = safeSub(totalSupply_,_amount); | |
Transfer(_from, address(0), _amount); | |
return true; | |
} | |
} | |
// deploy BitdollarToken contract first and pass that address as parameter to this contract's constructor | |
// deploy BitdollarVault and pass to it the BitdollarToken address and this contract's address as parameters | |
// finally, assign this contract's address as tokenContoller and the BitdollarVault address as tokenVault in BitdollarToken by invoking | |
// the changeTokenController function | |
contract BitdollarCampaign is SafeMath { | |
BitdollarToken bitdollarTokenContract; | |
// keeps track of total number of tokens sold throughout all phases | |
uint256 public totalSold; | |
// current phase token cap | |
uint256 public currentPhaseCap; | |
// tokens sold in current phase | |
uint256 public currentPhaseTokenSales; | |
// running total of development allocation accumulated in current phase | |
uint256 public developmentAllocationRunningTotal; | |
// current phase development allocation percent (takes 17 digit input for precision) | |
uint256 public developmentAllocationPercent; | |
// current phase starting block | |
uint256 public fundingStartBlock; | |
// current phase ending block | |
uint256 public fundingEndBlock; | |
// current phase minimum purchase threshold | |
uint256 public minAmount; | |
// current phase price | |
Price public currentPrice; | |
// state variable indicates if active sale phase | |
bool public icoMode; | |
// state variable to indicate ico parameters have been entered | |
bool public icoPrimed; | |
// amount of tokens unsold during current phase | |
// fundWallet may add this amount to subsequent phase cap at their discretion | |
uint256 public tokenCapRollOverToNextPhase; | |
// vesting fields | |
BTDVesting1Yr public vestingContract; | |
bool private vestingSet; | |
// root control | |
address public fundWallet; | |
// control of liquidity and limited control of updatePrice | |
address public controlWallet; | |
// time to wait between controlWallet price updates | |
uint256 public waitTime; | |
// halted: halt buying due to emergency, tradeable: signal that assets have been acquired | |
bool public halted; | |
bool public vestSoldTokens; | |
// last update timestamp | |
uint256 public previousUpdateTime; | |
// maps previousUpdateTime to the next price | |
mapping (uint256 => Price) public prices; | |
// maps verified addresses | |
mapping (address => bool) public whitelist; | |
// maps list of BTD owners who may skip whitelist and ico minimum requirements | |
mapping (address => bool) public verified; | |
mapping (address => address) public vestingAddresses; | |
// TYPES | |
struct Price { // tokensPerEth | |
uint256 numerator; | |
uint256 denominator; | |
} | |
// EVENTS | |
event Buy(address indexed participant, address indexed beneficiary, uint256 ethValue, uint256 amountTokens); | |
event AllocatePresale(address indexed participant, uint256 amountTokens); | |
event Whitelist(address indexed participant); | |
event PriceUpdate(uint256 numerator, uint256 denominator); | |
event ICO(uint256 phaseCap, uint256 phaseStart, uint256 phaseEnd); | |
// enum Phase { Initial, PreSale, Sale, Closed } | |
// enum Phase { PreSale, Sale, Closed } | |
// Phase public phase; | |
// MODIFIERS | |
//TODO this modifier is not used /////////////////////////////////////////////////////////////////// | |
// modifier isTradeable { // exempt vestingContract and fundWallet to allow dev allocations | |
// require(tradeable || msg.sender == fundWallet || msg.sender == vestingContract); | |
// _; | |
// } | |
modifier onlyWhitelist { | |
require(whitelist[msg.sender]); | |
_; | |
} | |
modifier onlyFundWallet { | |
require(msg.sender == fundWallet); | |
_; | |
} | |
modifier onlyManagingWallets { | |
require(msg.sender == controlWallet || msg.sender == fundWallet); | |
_; | |
} | |
modifier only_if_controlWallet { | |
if (msg.sender == controlWallet) | |
_; | |
} | |
modifier require_waited { | |
require(safeSub(now, waitTime) >= previousUpdateTime); | |
_; | |
} | |
modifier only_if_increase (uint256 newNumerator) { | |
if (newNumerator > currentPrice.numerator) | |
_; | |
} | |
modifier whileSaleActive { | |
require(block.number >= fundingStartBlock && block.number < fundingEndBlock && icoMode); | |
_; | |
} | |
// CONSTRUCTOR | |
function BitdollarCampaign( address controlWalletInput, | |
address tokenContractInput, | |
uint256 priceNumeratorInput) | |
public { | |
require(controlWalletInput != address(0) && tokenContractInput != address(0) && priceNumeratorInput > 0); | |
controlWallet = controlWalletInput; | |
bitdollarTokenContract = BitdollarToken(tokenContractInput); | |
currentPrice = Price(priceNumeratorInput, 1000); // 1 token = 1 usd at ICO start | |
previousUpdateTime = now; | |
fundWallet = msg.sender; | |
//whitelist[fundWallet] = true; | |
//whitelist[controlWallet] = true; | |
waitTime = 5 hours; | |
vestingSet = false; | |
halted = false; | |
icoMode = false; | |
// phase = Phase.Initial; | |
// phase = Phase.Closed; | |
} | |
// METHODS | |
function setVestingContract(uint256 phaseEndBlock) public onlyFundWallet returns (address) { | |
vestingContract = new BTDVesting1Yr(fundWallet, address(bitdollarTokenContract), phaseEndBlock); | |
//whitelist[vestingContract] = true; any reason for vestingContract address to purchase more tokens? | |
vestingSet = true; | |
} | |
// // METHODS | |
// // TODO set three vesting contracts | |
// function setVestingContract(address vestingContractInput) external onlyFundWallet { | |
// require(vestingContractInput != address(0)); | |
// vestingContract = vestingContractInput; | |
// //whitelist[vestingContract] = true; any reason for vestingContract address to purchase more tokens? | |
// vestingSet = true; | |
// } | |
// allows controlWallet to update the price within a time contstraint, allows fundWallet complete control | |
function updatePrice(uint256 newNumerator) external onlyManagingWallets { | |
require(newNumerator > 0); | |
require_limited_change(newNumerator); | |
// either controlWallet command is compliant or transaction came from fundWallet | |
currentPrice.numerator = newNumerator; | |
// maps time to new Price (if not during ICO) | |
prices[previousUpdateTime] = currentPrice; | |
previousUpdateTime = now; | |
PriceUpdate(newNumerator, currentPrice.denominator); | |
} | |
function require_limited_change (uint256 newNumerator) | |
private | |
only_if_controlWallet | |
require_waited | |
only_if_increase(newNumerator) | |
view | |
{ | |
uint256 percentage_diff = 0; | |
percentage_diff = safeMul(newNumerator, 100) / currentPrice.numerator; | |
percentage_diff = safeSub(percentage_diff, 100); | |
// controlWallet can only increase price by max 20% and only every waitTime | |
require(percentage_diff <= 20); | |
} | |
function updatePriceDenominator(uint256 newDenominator) external onlyFundWallet { | |
require(!icoMode); | |
//require(block.number > fundingEndBlock); | |
require(newDenominator > 0); | |
currentPrice.denominator = newDenominator; | |
// maps time to new Price | |
prices[previousUpdateTime] = currentPrice; | |
previousUpdateTime = now; | |
PriceUpdate(currentPrice.numerator, newDenominator); | |
} | |
function allocateTokens(address participant, uint256 amountTokens) internal { | |
//require(vestingSet); | |
// 15% of total allocated for PR, Marketing, Team, Advisors | |
// uint256 devAllocation = safeMul(amountTokens, 17647058823529400) / 100000000000000000; | |
uint256 devAllocation = safeMul(amountTokens, developmentAllocationPercent) / 100000000000000000; | |
// check that token cap is not exceeded | |
uint256 newTokens = safeAdd(amountTokens, devAllocation); | |
require(safeAdd(currentPhaseTokenSales, newTokens) <= currentPhaseCap); | |
//require(safeAdd(tokenSold, newTokens) <= tokenCap); | |
// increase token supply, assign tokens to participant | |
currentPhaseTokenSales = safeAdd(currentPhaseTokenSales, newTokens); | |
totalSold = safeAdd(totalSold, newTokens); | |
//tokenSold = safeAdd(tokenSold, newTokens); | |
developmentAllocationRunningTotal = safeAdd(developmentAllocationRunningTotal, devAllocation); | |
if(!vestSoldTokens) { | |
require(bitdollarTokenContract.generateTokens(participant, amountTokens)); | |
} else { | |
BTDVesting4Wk vestment = new BTDVesting4Wk(participant, address(bitdollarTokenContract), fundingEndBlock); | |
require(bitdollarTokenContract.generateTokens(address(vestment), amountTokens)); | |
// WILL BE OVERWRITTEN IF PARTICIPANT BUYS AGAIN | |
vestingAddresses[participant] = address(vestment); | |
} | |
verified[participant] = true; | |
//balances[participant] = safeAdd(balances[participant], amountTokens); | |
//balances[vestingContract] = safeAdd(balances[vestingContract], developmentAllocation); | |
} | |
function allocatePresaleTokens(address participant, uint amountTokens) external onlyFundWallet whileSaleActive { | |
//require(block.number < fundingEndBlock); | |
require(participant != address(0) && amountTokens > 0); | |
whitelist[participant] = true; // automatically whitelist accepted presale | |
allocateTokens(participant, amountTokens); | |
Whitelist(participant); | |
AllocatePresale(participant, amountTokens); | |
} | |
function verifyParticipant(address participant) external onlyManagingWallets { | |
require(participant != address(0)); | |
whitelist[participant] = true; | |
Whitelist(participant); | |
} | |
function getMyVestingContractAddress() public view returns (address) { | |
return vestingAddresses[msg.sender]; | |
} | |
function getVestingAddresses(address _owner) public view onlyFundWallet returns (address) { | |
return vestingAddresses[_owner]; | |
} | |
// fallback function | |
function() external payable whileSaleActive onlyWhitelist { | |
// TODO is this check necessary? ////////////////////////////////////// | |
// require(tx.origin == msg.sender); | |
buy(msg.sender); | |
} | |
function buy(address msgsender) public payable whileSaleActive onlyWhitelist { | |
require(!halted); | |
// if investor is already ownder of BTD - they will have been previously verified and whitelisted | |
if(verified[msgsender] == true) { | |
// no minimum requirements for tokenHolders | |
buyTo(msgsender); | |
} else { | |
// new investor must meet current rounds minimum to buy BTD at the current round price | |
require(msg.value >= minAmount); | |
buyTo(msgsender); | |
} | |
// if presale phase | |
// if(phase == Phase.PreSale) { | |
// // must be whitelisted participant | |
// if(whitelist[msgsender]) { | |
// return buyTo(msgsender); | |
// // else check if minimum amount in ether is supplied | |
// } else if (msg.value > minAmount) { | |
// // add them to whitelist | |
// whitelist[msgsender] = true; | |
// Whitelist(msgsender); | |
// return buyTo(msgsender); | |
// } | |
// } | |
// else | |
// require(msg.value > minAmount); | |
// return buyTo(msgsender); | |
} | |
function buyTo(address participant) internal returns (bool) { | |
uint256 icoDenominator = icoDenominatorPrice(); | |
uint256 tokensToBuy = safeMul(msg.value, currentPrice.numerator) / icoDenominator; | |
allocateTokens(participant, tokensToBuy); | |
// send ether to fundWallet | |
fundWallet.transfer(msg.value); | |
Buy(msg.sender, participant, msg.value, tokensToBuy); | |
} | |
// time based on blocknumbers, assuming a blocktime of 30s | |
function icoDenominatorPrice() public constant returns (uint256) { | |
uint256 icoDuration = safeSub(block.number, fundingStartBlock); | |
uint256 denominator; | |
if (icoDuration < 2880) { // #blocks = 24*60*60/30 = 2880 | |
return currentPrice.denominator; | |
} else if (icoDuration < 80640 ) { // #blocks = 4*7*24*60*60/30 = 80640 | |
denominator = safeMul(currentPrice.denominator, 105) / 100; | |
return denominator; | |
} else { | |
denominator = safeMul(currentPrice.denominator, 110) / 100; | |
return denominator; | |
} | |
} | |
function changeFundWallet(address newFundWallet) external onlyFundWallet { | |
require(newFundWallet != address(0)); | |
fundWallet = newFundWallet; | |
} | |
function changeControlWallet(address newControlWallet) external onlyFundWallet { | |
require(newControlWallet != address(0)); | |
controlWallet = newControlWallet; | |
} | |
function changeWaitTime(uint256 newWaitTime) external onlyFundWallet { | |
waitTime = newWaitTime; | |
} | |
function updateFundingStartBlock(uint256 newFundingStartBlock) external onlyFundWallet { | |
require(block.number < fundingStartBlock); | |
require(block.number < newFundingStartBlock); | |
require(newFundingStartBlock < fundingEndBlock); // sanity check | |
fundingStartBlock = newFundingStartBlock; | |
} | |
function updateFundingEndBlock(uint256 newFundingEndBlock) external onlyFundWallet { | |
require(block.number < fundingEndBlock); | |
require(block.number < newFundingEndBlock); | |
require(fundingStartBlock < newFundingEndBlock); // sanity check | |
fundingEndBlock = newFundingEndBlock; | |
} | |
// update minimum amount for investment | |
// @param newMinAmount the units in ether; eg input 10 for a 10 ETH minimum | |
function updateMinimumEtherAmount(uint256 newMinAmount) external onlyFundWallet { | |
//require(!icoMode); | |
minAmount = newMinAmount * 1 ether; | |
} | |
// update developer percentage allocation | |
// @param newDevAllocationPercentage takes a 17 digit precision input | |
function updateDevAllocationPercentage(uint256 newDevAllocationPercentage) external onlyFundWallet { | |
require(!icoMode); | |
developmentAllocationPercent = newDevAllocationPercentage; | |
} | |
// update currentPhaseCap | |
// @param newCurrentPhaseCap is expected to be between 100 000 000 and 400 000 000; will be converted internally | |
function updateCurrentPhaseCap(uint256 newCurrentPhaseCap) external onlyFundWallet { | |
require(!icoMode); | |
currentPhaseCap = newCurrentPhaseCap * 10**18; | |
} | |
// internal function to select phase state | |
// @param presale a boolean to indicate if presale, if false will activate Sale phase state | |
// function setPhase(bool presale) internal { | |
// // // cannot change if Initial Phase | |
// // if(phase == Phase.Initial) { | |
// // return; | |
// // } else if(presale) { | |
// // phase = Phase.PreSale; | |
// // } else { | |
// // phase = Phase.Sale; | |
// // } | |
// if(presale) { | |
// phase = Phase.PreSale; | |
// } else { | |
// phase = Phase.Sale; | |
// } | |
// } | |
// update phase state | |
// only allows switching from PreSale to Sale and vice versa | |
// function updatePhase() external onlyFundWallet { | |
// if(phase == Phase.PreSale) { | |
// phase = Phase.Sale; | |
// } else if (phase == Phase.Sale) { | |
// phase = Phase.PreSale; | |
// } | |
// } | |
function halt() external onlyFundWallet { | |
halted = true; | |
} | |
function unhalt() external onlyFundWallet { | |
halted = false; | |
} | |
// enables trading, withdrawals and token transfers are allowed in this state | |
function enableTrading() external onlyFundWallet { | |
bitdollarTokenContract.toggleTrading(true); | |
} | |
// disables token trading, cannot make withdrawals from BitdollarVault or transfer tokens between accounts in this state | |
function disableTrading() external onlyFundWallet { | |
bitdollarTokenContract.toggleTrading(false); | |
} | |
function claimTokens(address _token) external onlyFundWallet { | |
require(_token != address(0)); | |
Token token = Token(_token); | |
uint256 balance = token.balanceOf(this); | |
token.transfer(fundWallet, balance); | |
} | |
function _balanceOf(address _addr) internal view returns (uint256) { | |
return bitdollarTokenContract.balanceOf(_addr); | |
} | |
function getPrice(uint256 timestamp) external view returns (uint256, uint256) { | |
return (prices[timestamp].numerator, prices[timestamp].denominator); | |
} | |
function getCurrentPrice() external view returns (uint256, uint256) { | |
return (currentPrice.numerator, currentPrice.denominator); | |
} | |
function getPreviousUpdateTime() external view returns (uint256) { | |
return previousUpdateTime; | |
} | |
function setupParameters(uint256 startBlockInput, | |
uint256 endBlockInput, | |
uint256 minAmountInput, // in wei | |
uint256 devPercentInput, // takes 17 digit precision input | |
uint256 phaseCapInput, // cap for the current phase - must multiply coin count by 10**18 | |
bool vestedRelease, | |
bool primeICO) // set true if there is confidence in paramaters supplied - ready to activate | |
external onlyFundWallet { | |
// must not be in active ico | |
require(!icoMode); | |
// specified blocks must be in the future and have valid duration | |
require(block.number < startBlockInput && startBlockInput < endBlockInput); | |
// prices must have been previously set - if not updated the new phase will carry the same price as the previous phase | |
// fundWallet must invoke updatePrice and updatePriceDenominator externally to change prices | |
// set to current ether equivalent of $100000 minimum to qualify for presale purchase | |
// set to an arbitrary minimum eg 0.01 ether during normal sale to prevent spam calls from congesting the contract | |
require(minAmountInput > 0); | |
minAmount = minAmountInput; | |
// reset running total | |
developmentAllocationRunningTotal = 0; | |
developmentAllocationPercent = devPercentInput; | |
// reset current token sales count | |
currentPhaseTokenSales = 0; | |
currentPhaseCap = phaseCapInput; | |
// crowdsale parameters | |
fundingStartBlock = startBlockInput; | |
fundingEndBlock = endBlockInput; | |
vestSoldTokens = vestedRelease; | |
setVestingContract(endBlockInput); | |
// setPhase(isPresale); | |
// indicate ico parameters are primed | |
icoPrimed = primeICO; | |
} | |
// activates ico to start accepting ether | |
function activateICO() external onlyFundWallet { | |
require(!icoMode && icoPrimed); | |
icoPrimed = false; | |
icoMode = true; | |
} | |
// closes current ico and generates dev allocation tokens | |
function finalizeCurrentICO() external onlyFundWallet { | |
require(icoMode); | |
require(block.number > fundingEndBlock); | |
//balances[vestingContract] = safeAdd(balances[vestingContract], developmentAllocation); | |
require(vestingSet); | |
// generate allocation tokens accumulated throughout the sale | |
require(bitdollarTokenContract.generateTokens(address(vestingContract), developmentAllocationRunningTotal)); | |
icoMode = false; | |
tokenCapRollOverToNextPhase = safeSub(currentPhaseCap, currentPhaseTokenSales); | |
} | |
} | |
// assign TokenController tokenVault parameter to this contracts address | |
// has super user privelege to transfer tokens from any account to fundWallet | |
// deploy BitdollarToken and BitdollarCampaign first and supply their addresses as parameter to this contracts constructor | |
contract BitdollarVault is SafeMath { | |
BitdollarToken bitdollarTokenContract; | |
BitdollarCampaign bitdollarCampaignContract; | |
//uint256 previousUpdateTime; | |
// root control | |
address public fundWallet; | |
// control of liquidity and limited control of updatePrice | |
address public controlWallet; | |
// map participant address to a withdrawal request | |
mapping (address => Withdrawal) public withdrawals; | |
struct Withdrawal { | |
uint256 tokens; | |
uint256 time; // time for each withdrawal is set to the previousUpdateTime | |
} | |
event AddLiquidity(uint256 ethAmount); | |
event RemoveLiquidity(uint256 ethAmount); | |
event WithdrawRequest(address indexed participant, uint256 amountTokens); | |
event Withdraw(address indexed participant, uint256 amountTokens, uint256 etherAmount); | |
modifier onlyFundWallet { | |
require(msg.sender == fundWallet); | |
_; | |
} | |
modifier onlyManagingWallets { | |
require(msg.sender == controlWallet || msg.sender == fundWallet); | |
_; | |
} | |
function BitdollarVault(address controlWalletInput, address tokenContractInput, address campaignContractInput) public { | |
controlWallet = controlWalletInput; | |
bitdollarTokenContract = BitdollarToken(tokenContractInput); | |
bitdollarCampaignContract = BitdollarCampaign(campaignContractInput); | |
fundWallet = msg.sender; | |
} | |
function requestWithdrawal(uint256 amountTokensToWithdraw) external { | |
//require(block.number > fundingEndBlock); | |
require(amountTokensToWithdraw > 0); | |
address participant = msg.sender; | |
require(_balanceOf(participant) >= amountTokensToWithdraw); | |
require(withdrawals[participant].tokens == 0); // participant cannot have outstanding withdrawals | |
//balances[participant] = safeSub(balances[participant], amountTokensToWithdraw); | |
//uint256 remainingBalance = safeSub(_balanceOf(participant), amountTokensToWithdraw); | |
uint256 previousUpdateTime = bitdollarCampaignContract.getPreviousUpdateTime(); | |
withdrawals[participant] = Withdrawal({tokens: amountTokensToWithdraw, time: previousUpdateTime}); | |
WithdrawRequest(participant, amountTokensToWithdraw); | |
} | |
function withdraw() external { | |
address participant = msg.sender; | |
uint256 tokens = withdrawals[participant].tokens; | |
require(tokens > 0); // participant must have requested a withdrawal | |
uint256 requestTime = withdrawals[participant].time; | |
// obtain the next price that was set after the request | |
// Price storage price = prices[requestTime]; | |
uint256 numerator; | |
uint256 denominator; | |
(numerator, denominator) = bitdollarCampaignContract.getPrice(requestTime); | |
require(numerator > 0); // price must have been set | |
uint256 withdrawValue = safeMul(tokens, denominator) / numerator; | |
// if contract ethbal > then send + transfer tokens to fundWallet, otherwise give tokens back | |
withdrawals[participant].tokens = 0; | |
if (this.balance >= withdrawValue) | |
enact_withdrawal_greater_equal(participant, withdrawValue, tokens); | |
else | |
enact_withdrawal_less(participant, withdrawValue, tokens); | |
} | |
function enact_withdrawal_greater_equal(address participant, uint256 withdrawValue, uint256 tokens) | |
internal | |
{ | |
assert(this.balance >= withdrawValue); | |
// balances[fundWallet] = safeAdd(balances[fundWallet], tokens); | |
// will revert if tradeable boolean is false in the token contract | |
// require(bitdollarTokenContract.destroyTokens(participant,tokens)); | |
require(bitdollarTokenContract.transferFrom(participant, fundWallet, tokens)); | |
participant.transfer(withdrawValue); | |
Withdraw(participant, tokens, withdrawValue); | |
} | |
function enact_withdrawal_less(address participant, uint256 withdrawValue, uint256 tokens) | |
internal | |
{ | |
assert(this.balance < withdrawValue); | |
//balances[participant] = safeAdd(balances[participant], tokens); | |
Withdraw(participant, tokens, 0); // indicate a failed withdrawal | |
} | |
function checkWithdrawValue(uint256 amountTokensToWithdraw) constant public returns (uint256 etherValue) { | |
require(amountTokensToWithdraw > 0); | |
require(_balanceOf(msg.sender) >= amountTokensToWithdraw); | |
uint256 numerator; | |
uint256 denominator; | |
(numerator, denominator) = bitdollarCampaignContract.getCurrentPrice(); | |
//is this computation correct? | |
uint256 withdrawValue = safeMul(amountTokensToWithdraw, denominator) / numerator; | |
//is this check necessary? it only returns the eth value?? | |
//require(this.balance >= withdrawValue); | |
return withdrawValue; | |
} | |
// allow fundWallet or controlWallet to add ether to contract | |
function addLiquidity() external onlyManagingWallets payable { | |
require(msg.value > 0); | |
AddLiquidity(msg.value); | |
} | |
// allow fundWallet to remove ether from contract | |
function removeLiquidity(uint256 amount) external onlyManagingWallets { | |
require(amount <= this.balance); | |
fundWallet.transfer(amount); | |
RemoveLiquidity(amount); | |
} | |
function changeFundWallet(address newFundWallet) external onlyFundWallet { | |
require(newFundWallet != address(0)); | |
fundWallet = newFundWallet; | |
} | |
function changeControlWallet(address newControlWallet) external onlyFundWallet { | |
require(newControlWallet != address(0)); | |
controlWallet = newControlWallet; | |
} | |
function _balanceOf(address _addr) internal view returns (uint256) { | |
return bitdollarTokenContract.balanceOf(_addr); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma solidity 0.4.24; | |
contract SafeMath { | |
function safeMul(uint a, uint b) internal pure returns (uint) { | |
uint c = a * b; | |
assert(a == 0 || c / a == b); | |
return c; | |
} | |
function safeSub(uint a, uint b) internal pure returns (uint) { | |
assert(b <= a); | |
return a - b; | |
} | |
function safeAdd(uint a, uint b) internal pure returns (uint) { | |
uint c = a + b; | |
assert(c>=a && c>=b); | |
return c; | |
} | |
modifier onlyPayloadSize(uint numWords) { | |
assert(msg.data.length >= numWords * 32 + 4); | |
_; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma solidity 0.4.24; | |
contract Token { // ERC20 standard | |
function balanceOf(address _owner) constant public returns (uint256 balance); | |
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); | |
function allowance(address _owner, address _spender) constant public returns (uint256 remaining); | |
event Transfer(address indexed _from, address indexed _to, uint256 _value); | |
event Approval(address indexed _owner, address indexed _spender, uint256 _value); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma solidity 0.4.24; | |
contract TokenController { | |
// assign to BitdollarCampaign | |
address public tokenController; | |
// assign to BitdollarVault | |
address public tokenVault; | |
constructor() public { | |
tokenController = msg.sender; | |
} | |
modifier onlyTokenController { | |
require(msg.sender == tokenController); | |
_; | |
} | |
function changeTokenController(address newTokenController, address newTokenVault) external onlyTokenController { | |
require(newTokenController != address(0) && newTokenVault != address(0)); | |
tokenController = newTokenController; | |
tokenVault = newTokenVault; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment