2019-02-19 01:12:14 -03:00
|
|
|
pragma solidity >=0.5.0 <0.6.0;
|
|
|
|
|
2019-02-21 01:43:41 -03:00
|
|
|
import "../../common/MessageSigned.sol";
|
|
|
|
import "../../common/MerkleProof.sol";
|
|
|
|
import "./ProposalAbstract.sol";
|
2019-02-19 01:12:14 -03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @title Proposal
|
|
|
|
* @author Ricardo Guilherme Schmidt (Status Research & Development GmbH)
|
|
|
|
* Store votes and tabulate results for Democracy
|
|
|
|
*/
|
2019-02-21 01:43:41 -03:00
|
|
|
contract ProposalBase is ProposalAbstract, MessageSigned {
|
2019-02-19 01:12:14 -03:00
|
|
|
|
|
|
|
|
2019-02-21 01:43:41 -03:00
|
|
|
constructor()
|
|
|
|
public
|
2019-02-19 01:12:14 -03:00
|
|
|
{
|
2019-02-21 01:43:41 -03:00
|
|
|
blockStart = uint256(-1);
|
|
|
|
voteBlockEnd = uint256(-1);
|
2019-02-19 01:12:14 -03:00
|
|
|
}
|
|
|
|
|
2019-02-20 04:54:42 -03:00
|
|
|
function voteSigned(bytes32 _signatures)
|
2019-02-19 01:12:14 -03:00
|
|
|
external
|
|
|
|
{
|
|
|
|
require(block.number >= blockStart, "Voting not started");
|
|
|
|
require(block.number <= voteBlockEnd, "Voting ended");
|
|
|
|
signatures.push(_signatures);
|
|
|
|
}
|
|
|
|
|
2019-02-20 04:54:42 -03:00
|
|
|
function voteDirect(Vote _vote)
|
|
|
|
external
|
|
|
|
{
|
|
|
|
require(block.number >= blockStart, "Voting not started");
|
|
|
|
require(block.number <= voteBlockEnd, "Voting ended");
|
|
|
|
voteMap[msg.sender] = _vote;
|
|
|
|
}
|
|
|
|
|
|
|
|
function tabulateDirect(address _voter)
|
|
|
|
external
|
|
|
|
{
|
|
|
|
require(block.number > voteBlockEnd, "Voting running");
|
|
|
|
Vote vote = voteMap[_voter];
|
|
|
|
require(vote != Vote.Null, "Not voted");
|
|
|
|
setTabulation(_voter, _voter, vote );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-02-19 02:24:06 -03:00
|
|
|
function tabulateSigned(Vote _vote, uint256 _position, bytes32[] calldata _proof, bytes calldata _signature)
|
2019-02-19 01:12:14 -03:00
|
|
|
external
|
|
|
|
{
|
2019-02-19 02:24:06 -03:00
|
|
|
require(block.number > voteBlockEnd, "Voting running");
|
2019-02-19 01:12:14 -03:00
|
|
|
require(MerkleProof.verifyProof(_proof, signatures[_position], keccak256(_signature)), "Invalid proof");
|
|
|
|
address _voter = recoverAddress(keccak256(abi.encodePacked(address(this), _vote)), _signature);
|
2019-02-20 01:16:42 -03:00
|
|
|
require(voteMap[_voter] == Vote.Null, "Already tabulated");
|
2019-02-19 01:12:14 -03:00
|
|
|
voteMap[_voter] = _vote;
|
2019-02-19 02:24:06 -03:00
|
|
|
setTabulation(_voter, _voter, _vote);
|
2019-02-19 01:12:14 -03:00
|
|
|
}
|
|
|
|
|
2019-02-19 02:24:06 -03:00
|
|
|
function tabulateDelegated(address _voter)
|
2019-02-19 01:12:14 -03:00
|
|
|
external
|
|
|
|
{
|
|
|
|
require(block.number > voteBlockEnd, "Voting running");
|
2019-02-20 02:45:21 -03:00
|
|
|
(address _claimer, Vote _vote) = findNearestDelegatable(_voter); // try finding first delegate from chain which voted
|
2019-02-19 02:24:06 -03:00
|
|
|
setTabulation(_voter, _claimer, _vote);
|
|
|
|
}
|
2019-02-19 01:12:14 -03:00
|
|
|
|
2019-02-20 02:47:15 -03:00
|
|
|
function precomputeDelegation(address _start, bool _clean) external {
|
|
|
|
require(block.number > voteBlockEnd, "Voting running");
|
2019-02-20 02:45:21 -03:00
|
|
|
cacheDelegation(_start,_clean);
|
|
|
|
}
|
2019-02-19 01:12:14 -03:00
|
|
|
|
|
|
|
function finalize()
|
|
|
|
external
|
|
|
|
{
|
|
|
|
require(block.number > voteBlockEnd, "Voting running");
|
|
|
|
require(lastTabulationBlock + tabulationBlockDelay > block.number, "Tabulation running");
|
|
|
|
require(result == Vote.Null, "Already finalized");
|
|
|
|
uint256 totalTokens = token.totalSupplyAt(voteBlockEnd);
|
|
|
|
uint256 approvals = results[uint8(Vote.Approve)];
|
|
|
|
uint256 rejects = results[uint8(Vote.Reject)];
|
|
|
|
uint256 approvalQuorum = (totalTokens / 2);
|
|
|
|
if(approvals-rejects >= approvalQuorum) {
|
|
|
|
result = Vote.Approve;
|
|
|
|
} else {
|
|
|
|
result = Vote.Reject;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function clear()
|
|
|
|
external
|
|
|
|
onlyController
|
|
|
|
{
|
|
|
|
require(result != Vote.Null, "Not finalized");
|
|
|
|
selfdestruct(controller);
|
|
|
|
}
|
|
|
|
|
2019-02-19 02:24:06 -03:00
|
|
|
|
|
|
|
function setTabulation(address _voter, address _claimer, Vote _vote) internal {
|
|
|
|
require(_vote != Vote.Null, "Cannot be Vote.Null");
|
|
|
|
uint256 voterBalance = token.balanceOfAt(_voter, voteBlockEnd);
|
|
|
|
address currentClaimer = tabulated[_voter];
|
|
|
|
if(currentClaimer != address(0))
|
|
|
|
{
|
|
|
|
require(currentClaimer != _voter, "Voter already tabulated");
|
|
|
|
require(currentClaimer != _claimer, "Claimer already tabulated");
|
|
|
|
Vote oldVote = voteMap[currentClaimer];
|
|
|
|
require(oldVote != _vote, "Doesn't changes tabulation");
|
|
|
|
results[uint8(oldVote)] -= voterBalance;
|
|
|
|
}
|
|
|
|
tabulated[_voter] = _claimer;
|
|
|
|
results[uint8(_vote)] += voterBalance;
|
|
|
|
lastTabulationBlock = block.number;
|
|
|
|
}
|
|
|
|
|
|
|
|
function findNearestDelegatable(address _voter) internal view returns (address claimer, Vote vote){
|
2019-02-20 02:45:21 -03:00
|
|
|
vote = voteMap[_voter];
|
|
|
|
require(vote == Vote.Null, "Not delegatable");
|
2019-02-19 02:24:06 -03:00
|
|
|
claimer = _voter; // try finding first delegate from chain which voted
|
|
|
|
while(vote == Vote.Null) {
|
2019-02-20 02:45:21 -03:00
|
|
|
claimer = delegationOf[claimer];
|
2019-02-19 02:24:06 -03:00
|
|
|
if(claimer == address(0)){
|
2019-02-20 02:45:21 -03:00
|
|
|
claimer = delegation.delegatedToAt(claimer, voteBlockEnd);
|
2019-02-19 02:24:06 -03:00
|
|
|
}
|
2019-02-20 02:45:21 -03:00
|
|
|
require(claimer != address(0), "No delegate vote found");
|
2019-02-19 02:24:06 -03:00
|
|
|
vote = voteMap[claimer]; //loads delegate vote.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-21 01:43:41 -03:00
|
|
|
function cacheDelegation(address _delegator, bool _clean) private returns (address delegate) {
|
|
|
|
delegate = _delegator;
|
|
|
|
if(voteMap[_delegator] == Vote.Null) {
|
|
|
|
if(!_clean) {
|
|
|
|
delegate = delegationOf[delegate];
|
|
|
|
}
|
|
|
|
if(delegate == address(0)){
|
|
|
|
delegate = delegation.delegatedToAt(_delegator, voteBlockEnd); //get delegate chain tail
|
|
|
|
}
|
2019-02-20 02:45:21 -03:00
|
|
|
}
|
2019-02-21 01:43:41 -03:00
|
|
|
|
2019-02-20 02:45:21 -03:00
|
|
|
require(delegate != address(0), "No delegate vote found");
|
|
|
|
if(voteMap[delegate] == Vote.Null) {
|
|
|
|
delegate = cacheDelegation(delegate, _clean);
|
|
|
|
}
|
|
|
|
delegationOf[_delegator] = delegate;
|
|
|
|
return delegate;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2019-02-19 01:12:14 -03:00
|
|
|
}
|