|
| 1 | +<h2>Full example</h2> |
| 2 | +<pre><code>pragma solidity >=0.4.22 <0.7.0; |
| 3 | + |
| 4 | +/// @title Voting with delegation. |
| 5 | +contract Ballot { |
| 6 | + // This declares a new complex type which will |
| 7 | + // be used for variables later. |
| 8 | + // It will represent a single voter. |
| 9 | + struct Voter { |
| 10 | + uint weight; // weight is accumulated by delegation |
| 11 | + bool voted; // if true, that person already voted |
| 12 | + address delegate; // person delegated to |
| 13 | + uint vote; // index of the voted proposal |
| 14 | + } |
| 15 | + |
| 16 | + // This is a type for a single proposal. |
| 17 | + struct Proposal { |
| 18 | + bytes32 name; // short name (up to 32 bytes) |
| 19 | + uint voteCount; // number of accumulated votes |
| 20 | + } |
| 21 | + |
| 22 | + address public chairperson; |
| 23 | + |
| 24 | + // This declares a state variable that |
| 25 | + // stores a `Voter` struct for each possible address. |
| 26 | + mapping(address => Voter) public voters; |
| 27 | + |
| 28 | + // A dynamically-sized array of `Proposal` structs. |
| 29 | + Proposal[] public proposals; |
| 30 | + |
| 31 | + /// Create a new ballot to choose one of `proposalNames`. |
| 32 | + constructor(bytes32[] memory proposalNames) public { |
| 33 | + chairperson = msg.sender; |
| 34 | + voters[chairperson].weight = 1; |
| 35 | + |
| 36 | + // For each of the provided proposal names, |
| 37 | + // create a new proposal object and add it |
| 38 | + // to the end of the array. |
| 39 | + for (uint i = 0; i < proposalNames.length; i++) { |
| 40 | + // `Proposal({...})` creates a temporary |
| 41 | + // Proposal object and `proposals.push(...)` |
| 42 | + // appends it to the end of `proposals`. |
| 43 | + proposals.push(Proposal({ |
| 44 | + name: proposalNames[i], |
| 45 | + voteCount: 0 |
| 46 | + })); |
| 47 | + } |
| 48 | + } |
| 49 | + |
| 50 | + // Give `voter` the right to vote on this ballot. |
| 51 | + // May only be called by `chairperson`. |
| 52 | + function giveRightToVote(address voter) public { |
| 53 | + // If the first argument of `require` evaluates |
| 54 | + // to `false`, execution terminates and all |
| 55 | + // changes to the state and to Ether balances |
| 56 | + // are reverted. |
| 57 | + // This used to consume all gas in old EVM versions, but |
| 58 | + // not anymore. |
| 59 | + // It is often a good idea to use `require` to check if |
| 60 | + // functions are called correctly. |
| 61 | + // As a second argument, you can also provide an |
| 62 | + // explanation about what went wrong. |
| 63 | + require( |
| 64 | + msg.sender == chairperson, |
| 65 | + "Only chairperson can give right to vote." |
| 66 | + ); |
| 67 | + require( |
| 68 | + !voters[voter].voted, |
| 69 | + "The voter already voted." |
| 70 | + ); |
| 71 | + require(voters[voter].weight == 0); |
| 72 | + voters[voter].weight = 1; |
| 73 | + } |
| 74 | + |
| 75 | + /// Delegate your vote to the voter `to`. |
| 76 | + function delegate(address to) public { |
| 77 | + // assigns reference |
| 78 | + Voter storage sender = voters[msg.sender]; |
| 79 | + require(!sender.voted, "You already voted."); |
| 80 | + |
| 81 | + require(to != msg.sender, "Self-delegation is disallowed."); |
| 82 | + |
| 83 | + // Forward the delegation as long as |
| 84 | + // `to` also delegated. |
| 85 | + // In general, such loops are very dangerous, |
| 86 | + // because if they run too long, they might |
| 87 | + // need more gas than is available in a block. |
| 88 | + // In this case, the delegation will not be executed, |
| 89 | + // but in other situations, such loops might |
| 90 | + // cause a contract to get "stuck" completely. |
| 91 | + while (voters[to].delegate != address(0)) { |
| 92 | + to = voters[to].delegate; |
| 93 | + |
| 94 | + // We found a loop in the delegation, not allowed. |
| 95 | + require(to != msg.sender, "Found loop in delegation."); |
| 96 | + } |
| 97 | + |
| 98 | + // Since `sender` is a reference, this |
| 99 | + // modifies `voters[msg.sender].voted` |
| 100 | + sender.voted = true; |
| 101 | + sender.delegate = to; |
| 102 | + Voter storage delegate_ = voters[to]; |
| 103 | + if (delegate_.voted) { |
| 104 | + // If the delegate already voted, |
| 105 | + // directly add to the number of votes |
| 106 | + proposals[delegate_.vote].voteCount += sender.weight; |
| 107 | + } else { |
| 108 | + // If the delegate did not vote yet, |
| 109 | + // add to her weight. |
| 110 | + delegate_.weight += sender.weight; |
| 111 | + } |
| 112 | + } |
| 113 | + |
| 114 | + /// Give your vote (including votes delegated to you) |
| 115 | + /// to proposal `proposals[proposal].name`. |
| 116 | + function vote(uint proposal) public { |
| 117 | + Voter storage sender = voters[msg.sender]; |
| 118 | + require(sender.weight != 0, "Has no right to vote"); |
| 119 | + require(!sender.voted, "Already voted."); |
| 120 | + sender.voted = true; |
| 121 | + sender.vote = proposal; |
| 122 | + |
| 123 | + // If `proposal` is out of the range of the array, |
| 124 | + // this will throw automatically and revert all |
| 125 | + // changes. |
| 126 | + proposals[proposal].voteCount += sender.weight; |
| 127 | + } |
| 128 | + |
| 129 | + /// @dev Computes the winning proposal taking all |
| 130 | + /// previous votes into account. |
| 131 | + function winningProposal() public view |
| 132 | + returns (uint winningProposal_) |
| 133 | + { |
| 134 | + uint winningVoteCount = 0; |
| 135 | + for (uint p = 0; p < proposals.length; p++) { |
| 136 | + if (proposals[p].voteCount > winningVoteCount) { |
| 137 | + winningVoteCount = proposals[p].voteCount; |
| 138 | + winningProposal_ = p; |
| 139 | + } |
| 140 | + } |
| 141 | + } |
| 142 | + |
| 143 | + // Calls winningProposal() function to get the index |
| 144 | + // of the winner contained in the proposals array and then |
| 145 | + // returns the name of the winner |
| 146 | + function winnerName() public view |
| 147 | + returns (bytes32 winnerName_) |
| 148 | + { |
| 149 | + winnerName_ = proposals[winningProposal()].name; |
| 150 | + } |
| 151 | +}</code></pre> |
0 commit comments