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=
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); | |
} | |
} |
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); | |
} | |
} |
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); | |
} | |
} |
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; | |
} | |
} |
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); | |
} | |
} |
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); | |
} | |
} |
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); | |
} | |
} |
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); | |
} | |
} |
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); | |
_; | |
} | |
} |
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); | |
} |
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