Skip to content

Instantly share code, notes, and snippets.

@bitdollarfund
Last active January 28, 2018 17:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bitdollarfund/ed79f53be5e41429fefcbfe89f844340 to your computer and use it in GitHub Desktop.
Save bitdollarfund/ed79f53be5e41429fefcbfe89f844340 to your computer and use it in GitHub Desktop.
pragma solidity ^0.4.18;
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 {
mapping (address => uint256) public balanceOf;
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success);
}
/**
* The shareholder association contract itself
*/
contract Association 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, uint result, uint quorum, bool active);
event ChangeOfRules(uint newMinimumQuorum, uint newDebatingPeriodInMinutes, address newSharesTokenAddress);
struct Proposal {
address recipient;
uint amount;
string description;
uint votingDeadline;
bool executed;
bool proposalPassed;
uint numberOfVotes;
bytes32 proposalHash;
Vote[] votes;
mapping (address => bool) voted;
}
struct Vote {
bool inSupport;
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 Association(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;
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);
}
/**
* 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.voted[msg.sender] != true);
voteID = p.votes.length++;
p.votes[voteID] = Vote({inSupport: supportsProposal, voter: msg.sender});
p.voted[msg.sender] = true;
p.numberOfVotes = voteID +1;
Voted(proposalNumber, supportsProposal, 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.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, yea - nay, quorum, p.proposalPassed);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment