|
pragma solidity ^0.4.16; |
|
|
|
contract CatDemocracy { |
|
mapping (bytes5 => uint256) public voteWeight; |
|
uint public numberOfDelegationRounds; |
|
|
|
function balanceOf(bytes5 _catId) constant returns (uint256 _balance) { |
|
if (numberOfDelegationRounds < 3) { |
|
return 0; |
|
} else { |
|
return this.voteWeight(_catId); |
|
} |
|
} |
|
} |
|
contract MoonCatToken { |
|
mapping (bytes5 => address) public catOwners; |
|
} |
|
|
|
|
|
/** |
|
* The shareholder association contract itself |
|
*/ |
|
contract CatUnitedNations { |
|
|
|
address public owner = msg.sender; |
|
uint public minimumQuorum; |
|
uint public debatingPeriodInMinutes; |
|
Proposal[] public proposals; |
|
uint public numProposals; |
|
CatDemocracy public democracy; |
|
MoonCatToken public moonCats; |
|
|
|
event ProposalAdded(uint proposalID, address target, uint amount, string description); |
|
event Voted(uint proposalID, bool position, bytes5 voter); |
|
event ProposalTallied(uint proposalID, uint result, uint quorum, bool active); |
|
event ChangeOfRules(uint newMinimumQuorum, uint newDebatingPeriodInMinutes, address newSharesTokenAddress); |
|
|
|
struct Proposal { |
|
address target; |
|
uint amount; |
|
string description; |
|
uint votingDeadline; |
|
bool executed; |
|
bool proposalPassed; |
|
uint numberOfVotes; |
|
bytes32 proposalHash; |
|
Vote[] votes; |
|
mapping (bytes5 => bool) voted; |
|
} |
|
|
|
struct Vote { |
|
bool inSupport; |
|
bytes5 voter; |
|
} |
|
|
|
// Modifier that only allows the current owner to execute |
|
modifier onlyOwner { |
|
require(msg.sender == owner); |
|
_; |
|
} |
|
|
|
|
|
/** |
|
* Constructor function |
|
* |
|
* First time setup |
|
* |
|
* @param _token location of the MoonCatRescue contract |
|
* @param _democracyAddress location of the LiquidCatDemocracy contract |
|
* @param _minimumQuorum proposal can conclude only if the sum of shares held by all voters exceed this number |
|
* @param _debatingPeriodInMinutes the minimum amount of delay between when a proposal is made and when it can be executed |
|
*/ |
|
function CatUnitedNations(address _token, address _democracyAddress, uint _minimumQuorum, uint _debatingPeriodInMinutes) payable { |
|
changeVotingRules(_token, _democracyAddress, _minimumQuorum, _debatingPeriodInMinutes); |
|
} |
|
|
|
/** |
|
* Set new owner for the contract |
|
* |
|
* @param _newOwner address of new owner |
|
*/ |
|
function transferOwnership(address _newOwner) onlyOwner { |
|
owner = _newOwner; |
|
} |
|
|
|
/** |
|
* Change voting rules |
|
* |
|
* Make so that proposals need tobe discussed for at least `minutesForDebate/60` hours |
|
* and all voters combined must own more than `minimumSharesToPassAVote` shares of token `sharesAddress` to be executed |
|
* |
|
* @param _token location of the MoonCatRescue contract |
|
* @param _democracyAddress location of the LiquidCatDemocracy contract |
|
* @param _minimumQuorum proposal can conclude only if the sum of shares held by all voters exceed this number |
|
* @param _debatingPeriodInMinutes the minimum amount of delay between when a proposal is made and when it can be executed |
|
*/ |
|
function changeVotingRules(address _token, address _democracyAddress, uint _minimumQuorum, uint _debatingPeriodInMinutes) onlyOwner { |
|
moonCats = MoonCatToken(_token); |
|
democracy = CatDemocracy(_democracyAddress); |
|
if (_minimumQuorum == 0 ) _minimumQuorum = 1; |
|
minimumQuorum = _minimumQuorum; |
|
debatingPeriodInMinutes = _debatingPeriodInMinutes; |
|
ChangeOfRules(minimumQuorum, debatingPeriodInMinutes, democracy); |
|
} |
|
|
|
/** |
|
* Add Proposal |
|
* |
|
* Propose to send `weiAmount / 1e18` ether to `beneficiary` for `jobDescription`. `transactionBytecode ? Contains : Does not contain` code. |
|
* |
|
* @param _catID the cat making the proposal |
|
* @param _target who to send the ether to |
|
* @param _weiAmount amount of ether to send, in wei |
|
* @param _description Description of job |
|
* @param _transactionBytecode bytecode of transaction |
|
*/ |
|
function newProposal( |
|
bytes5 _catID, |
|
address _target, |
|
uint _weiAmount, |
|
string _description, |
|
bytes _transactionBytecode |
|
) |
|
returns (uint proposalID) |
|
{ |
|
require(moonCats.catOwners(_catID) == msg.sender); // Must be the owner of that cat |
|
proposalID = proposals.length++; |
|
|
|
Proposal storage p = proposals[proposalID]; |
|
p.target = _target; |
|
p.amount = _weiAmount; |
|
p.description = _description; |
|
p.proposalHash = sha3(_target, _weiAmount, _transactionBytecode); |
|
p.votingDeadline = now + debatingPeriodInMinutes * 1 minutes; |
|
p.executed = false; |
|
p.proposalPassed = false; |
|
p.numberOfVotes = 0; |
|
|
|
ProposalAdded(proposalID, _target, _weiAmount, _description); |
|
numProposals = proposalID+1; |
|
|
|
return proposalID; |
|
} |
|
|
|
/** |
|
* Check if a proposal code matches |
|
* |
|
* @param _proposalNumber ID number of the proposal to query |
|
* @param _target who to send the ether to |
|
* @param _weiAmount amount of ether to send |
|
* @param _transactionBytecode bytecode of transaction |
|
*/ |
|
function checkProposalCode( |
|
uint _proposalNumber, |
|
address _target, |
|
uint _weiAmount, |
|
bytes _transactionBytecode |
|
) |
|
constant |
|
returns (bool codeChecksOut) |
|
{ |
|
Proposal storage p = proposals[_proposalNumber]; |
|
return p.proposalHash == sha3(_target, _weiAmount, _transactionBytecode); |
|
} |
|
|
|
/** |
|
* Log a vote for a proposal |
|
* |
|
* Vote `supportsProposal? in support of : against` proposal #`proposalNumber` |
|
* |
|
* @param _catID The cat submitting the vote |
|
* @param _proposalNumber number of proposal |
|
* @param _supportsProposal either in favor or against it |
|
*/ |
|
function vote( |
|
bytes5 _catID, |
|
uint _proposalNumber, |
|
bool _supportsProposal |
|
) |
|
returns (uint voteID) |
|
{ |
|
require(moonCats.catOwners(_catID) == msg.sender); // Must be the owner of that cat |
|
Proposal storage p = proposals[_proposalNumber]; |
|
require(p.voted[_catID] != true); |
|
|
|
voteID = p.votes.length++; |
|
p.votes[voteID] = Vote({ |
|
inSupport: _supportsProposal, |
|
voter: _catID |
|
}); |
|
p.voted[_catID] = true; |
|
p.numberOfVotes = voteID +1; |
|
Voted(_proposalNumber, _supportsProposal, _catID); |
|
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) { |
|
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.proposalHash == sha3(p.target, 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 = democracy.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.target.call.value(p.amount)(_transactionBytecode)); |
|
|
|
p.proposalPassed = true; |
|
} else { |
|
// Proposal failed |
|
p.proposalPassed = false; |
|
} |
|
|
|
// Fire Events |
|
ProposalTallied(_proposalNumber, yea - nay, quorum, p.proposalPassed); |
|
} |
|
} |