forked from ethereum/ethereum-org
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdao-liquid-democracy.sol
139 lines (119 loc) · 4.72 KB
/
dao-liquid-democracy.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
pragma solidity >=0.4.22 <0.6.0;
contract token {
mapping (address => uint256) public balanceOf;
}
contract LiquidDemocracy {
token public votingToken;
bool underExecution;
address public appointee;
mapping (address => uint) public voterId;
mapping (address => uint256) public voteWeight;
uint public delegatedPercent;
uint public lastWeightCalculation;
uint public numberOfDelegationRounds;
uint public numberOfVotes;
DelegatedVote[] public delegatedVotes;
string public forbiddenFunction;
event NewAppointee(address newAppointee, bool changed);
struct DelegatedVote {
address nominee;
address voter;
}
/**
* Constructor
*/
constructor(
address votingWeightToken,
string memory forbiddenFunctionCall,
uint percentLossInEachRound
) public {
votingToken = token(votingWeightToken);
delegatedVotes.length++;
delegatedVotes[0] = DelegatedVote({nominee: address(0), voter: address(0)});
forbiddenFunction = forbiddenFunctionCall;
delegatedPercent = 100 - percentLossInEachRound;
if (delegatedPercent > 100) delegatedPercent = 100;
}
/**
* Vote for an address
*
* Send your vote weight to another address
*
* @param nominatedAddress the destination address receiving the sender's vote
*/
function vote(address nominatedAddress) public returns (uint voteIndex) {
if (voterId[msg.sender]== 0) {
voterId[msg.sender] = delegatedVotes.length;
numberOfVotes++;
voteIndex = delegatedVotes.length++;
numberOfVotes = voteIndex;
}
else {
voteIndex = voterId[msg.sender];
}
delegatedVotes[voteIndex] = DelegatedVote({nominee: nominatedAddress, voter: msg.sender});
return voteIndex;
}
/**
* Perform Executive Action
*
* @param target the destination address to interact with
* @param valueInWei the amount of ether to send along with the transaction
* @param bytecode the data bytecode for the transaction
*/
function execute(address target, uint valueInWei, bytes32 bytecode) public {
require(msg.sender == appointee // If caller is the current appointee,
&& !underExecution // if the call is being executed,
&& bytes4(bytecode) != bytes4(keccak256(abi.encodePacked(forbiddenFunction))) // and it's not trying to do the forbidden function
&& numberOfDelegationRounds >= 4); // and delegation has been calculated enough
underExecution = true;
(bool success, ) = target.call.value(valueInWei)(abi.encode(bytecode)); // Then execute the command.
require(success);
underExecution = false;
}
/**
* Calculate Votes
*
* Go thruogh all the delegated vote logs and tally up each address's total rank
*/
function calculateVotes() public returns (address winner) {
address currentWinner = appointee;
uint currentMax = 0;
uint weight = 0;
DelegatedVote storage v = delegatedVotes[0];
if (now > lastWeightCalculation + 90 minutes) {
numberOfDelegationRounds = 0;
lastWeightCalculation = now;
// Distribute the initial weight
for (uint i=1; i< delegatedVotes.length; i++) {
voteWeight[delegatedVotes[i].nominee] = 0;
}
for (uint i=1; i< delegatedVotes.length; i++) {
voteWeight[delegatedVotes[i].voter] = votingToken.balanceOf(delegatedVotes[i].voter);
}
}
else {
numberOfDelegationRounds++;
uint lossRatio = 100 * delegatedPercent ** numberOfDelegationRounds / 100 ** numberOfDelegationRounds;
if (lossRatio > 0) {
for (uint i=1; i< delegatedVotes.length; i++){
v = delegatedVotes[i];
if (v.nominee != v.voter && voteWeight[v.voter] > 0) {
weight = voteWeight[v.voter] * lossRatio / 100;
voteWeight[v.voter] -= weight;
voteWeight[v.nominee] += weight;
}
if (numberOfDelegationRounds>3 && voteWeight[v.nominee] > currentMax) {
currentWinner = v.nominee;
currentMax = voteWeight[v.nominee];
}
}
}
}
if (numberOfDelegationRounds > 3) {
emit NewAppointee(currentWinner, appointee == currentWinner);
appointee = currentWinner;
}
return currentWinner;
}
}