From e2362ad9baafeb73e64d069139f3f9ea31c8b0f6 Mon Sep 17 00:00:00 2001 From: Protinam Date: Mon, 11 Dec 2017 01:13:26 -0800 Subject: [PATCH] Convenience view functions on DAO; index logs --- contracts/common/TokenRecipient.sol | 8 +- .../dao/DelegatedShareholderAssociation.sol | 94 ++++++++----------- 2 files changed, 48 insertions(+), 54 deletions(-) diff --git a/contracts/common/TokenRecipient.sol b/contracts/common/TokenRecipient.sol index eb62b7a..a09ca21 100644 --- a/contracts/common/TokenRecipient.sol +++ b/contracts/common/TokenRecipient.sol @@ -2,9 +2,15 @@ pragma solidity ^0.4.18; import "zeppelin-solidity/contracts/token/ERC20.sol"; +/** + * @title TokenRecipient + * @author Project Wyvern Developers + * + * + */ contract TokenRecipient { event ReceivedEther(address sender, uint amount); - event ReceivedTokens(address _from, uint256 _value, address _token, bytes _extraData); + event ReceivedTokens(address indexed _from, uint256 _value, address _token, bytes _extraData); /** * @dev Receive tokens and generate a log event diff --git a/contracts/dao/DelegatedShareholderAssociation.sol b/contracts/dao/DelegatedShareholderAssociation.sol index 1b975e0..176849f 100644 --- a/contracts/dao/DelegatedShareholderAssociation.sol +++ b/contracts/dao/DelegatedShareholderAssociation.sol @@ -24,11 +24,6 @@ Only the DAO itself can change its own voting rules (not an owning address). - TODO - - - Convenience constant function for vote tallying - - Clarify some documentation - */ pragma solidity ^0.4.18; @@ -36,6 +31,12 @@ pragma solidity ^0.4.18; import "zeppelin-solidity/contracts/token/ERC20.sol"; import "../common/TokenRecipient.sol"; +/** + * @title DelegatedShareholderAssociation + * @author Project Wyvern Developers + * + * + */ contract DelegatedShareholderAssociation is TokenRecipient { uint public minimumQuorum; @@ -59,13 +60,12 @@ contract DelegatedShareholderAssociation is TokenRecipient { /* Threshold for the ability to create proposals. */ uint public requiredSharesToBeBoardMember; - /* Events for all state changes. TODO: Add for execution. */ + /* Events for all state changes. */ event ProposalAdded(uint proposalID, address recipient, uint amount, bytes metadataHash); 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); - event TokensDelegated(address indexed delegator, uint numberOfTokens, address indexed delegate); event TokensUndelegated(address indexed delegator, uint numberOfTokens, address indexed delegate); @@ -105,6 +105,7 @@ contract DelegatedShareholderAssociation is TokenRecipient { _; } + /* Only boardmembers (shareholders above a certain threshold) can execute a function with this modifier. */ modifier onlyBoardMembers { require(ERC20(sharesTokenAddress).balanceOf(msg.sender) >= requiredSharesToBeBoardMember); _; @@ -116,7 +117,9 @@ contract DelegatedShareholderAssociation is TokenRecipient { _; } - /** + /** + * Delegate an amount of tokens + * * @notice Set the delegate address for a specified number of tokens belonging to the sending address, locking the tokens. * @dev An address holding tokens (shares) may only delegate some portion of their vote to one delegate at any one time * @param tokensToLock number of tokens to be locked (sending address must have at least this many tokens) @@ -132,6 +135,8 @@ contract DelegatedShareholderAssociation is TokenRecipient { } /** + * Undelegate all delegated tokens + * * @notice Clear the delegate address for all tokens delegated by the sending address, unlocking the locked tokens. * @dev Can only be called by a sending address currently delegating tokens, will transfer all locked tokens back to the sender * @return The number of tokens previously locked, now released @@ -202,30 +207,6 @@ contract DelegatedShareholderAssociation is TokenRecipient { return proposalID; } - /** - * Add proposal in Ether - * - * Propose to send `etherAmount` ether to `beneficiary` for `jobMetadataHash`. `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 jobMetadataHash Hash of job metadata (IPFS) - * @param transactionBytecode bytecode of transaction - */ - function newProposalInEther( - address beneficiary, - uint etherAmount, - bytes jobMetadataHash, - bytes transactionBytecode - ) - public - onlyBoardMembers - returns (uint proposalID) - { - return newProposal(beneficiary, etherAmount * 1 ether, jobMetadataHash, transactionBytecode); - } - /** * Check if a proposal code matches * @@ -266,34 +247,24 @@ contract DelegatedShareholderAssociation is TokenRecipient { { 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); + Voted(proposalNumber, supportsProposal, msg.sender); return voteID; } /** - * Finish vote - * - * Count the votes proposal #`proposalNumber` and execute it if approved - * + * Count the votes, including delegated votes, in support of, against, and in total for a particular proposal * @param proposalNumber proposal number - * @param transactionBytecode optional: if the transaction contained a bytecode, you need to send it + * @return yea votes, nay votes, quorum (total votes) */ - function executeProposal(uint proposalNumber, bytes transactionBytecode) public { + function countVotes(uint proposalNumber) public view returns (uint yea, uint nay, uint quorum) { Proposal storage p = proposals[proposalNumber]; - - /* If past deadline, not already executed, and code is correct, keep going. */ - require((now > p.votingDeadline) && !p.executed && p.proposalHash == keccak256(p.recipient, p.amount, transactionBytecode)); - - // ...then tally the results - uint quorum = 0; - uint yea = 0; - uint nay = 0; - + yea = 0; + nay = 0; + quorum = 0; for (uint i = 0; i < p.votes.length; ++i) { Vote storage v = p.votes[i]; uint voteWeight = sharesTokenAddress.balanceOf(v.voter) + delegatedAmountsByDelegate[v.voter]; @@ -304,13 +275,30 @@ contract DelegatedShareholderAssociation is TokenRecipient { nay += voteWeight; } } + } + + /** + * 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]; + + /* If past deadline, not already executed, and code is correct, keep going. */ + require((now > p.votingDeadline) && !p.executed && p.proposalHash == keccak256(p.recipient, p.amount, transactionBytecode)); + + /* Count the votes. */ + var ( yea, nay, quorum ) = countVotes(proposalNumber); /* Assert that a minimum quorum has been reached. */ require(quorum >= minimumQuorum); if (yea > nay) { - // Proposal passed; execute the transaction - + /* Proposal passed; execute the transaction. */ p.executed = true; require(p.recipient.call.value(p.amount)(transactionBytecode)); @@ -319,11 +307,11 @@ contract DelegatedShareholderAssociation is TokenRecipient { p.proposalPassed = true; } else { - // Proposal failed + /* Proposal failed. */ p.proposalPassed = false; } - // Fire Events + /* Log event. */ ProposalTallied(proposalNumber, yea - nay, quorum, p.proposalPassed); } }