ballot.sol 5.3 KB

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