|
|
@ -7,7 +7,9 @@ pragma solidity >=0.7.0 <0.9.0; |
|
|
|
* @dev Implements voting process along with vote delegation |
|
|
|
* @dev Implements voting process along with vote delegation |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
contract Ballot { |
|
|
|
contract Ballot { |
|
|
|
|
|
|
|
// This declares a new complex type which will |
|
|
|
|
|
|
|
// be used for variables later. |
|
|
|
|
|
|
|
// It will represent a single voter. |
|
|
|
struct Voter { |
|
|
|
struct Voter { |
|
|
|
uint weight; // weight is accumulated by delegation |
|
|
|
uint weight; // weight is accumulated by delegation |
|
|
|
bool voted; // if true, that person already voted |
|
|
|
bool voted; // if true, that person already voted |
|
|
@ -15,6 +17,7 @@ contract Ballot { |
|
|
|
uint vote; // index of the voted proposal |
|
|
|
uint vote; // index of the voted proposal |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// This is a type for a single proposal. |
|
|
|
struct Proposal { |
|
|
|
struct Proposal { |
|
|
|
// If you can limit the length to a certain number of bytes, |
|
|
|
// If you can limit the length to a certain number of bytes, |
|
|
|
// always use one of bytes1 to bytes32 because they are much cheaper |
|
|
|
// always use one of bytes1 to bytes32 because they are much cheaper |
|
|
@ -24,8 +27,11 @@ contract Ballot { |
|
|
|
|
|
|
|
|
|
|
|
address public chairperson; |
|
|
|
address public chairperson; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// This declares a state variable that |
|
|
|
|
|
|
|
// stores a 'Voter' struct for each possible address. |
|
|
|
mapping(address => Voter) public voters; |
|
|
|
mapping(address => Voter) public voters; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// A dynamically-sized array of 'Proposal' structs. |
|
|
|
Proposal[] public proposals; |
|
|
|
Proposal[] public proposals; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
@ -36,6 +42,9 @@ contract Ballot { |
|
|
|
chairperson = msg.sender; |
|
|
|
chairperson = msg.sender; |
|
|
|
voters[chairperson].weight = 1; |
|
|
|
voters[chairperson].weight = 1; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// For each of the provided proposal names, |
|
|
|
|
|
|
|
// create a new proposal object and add it |
|
|
|
|
|
|
|
// to the end of the array. |
|
|
|
for (uint i = 0; i < proposalNames.length; i++) { |
|
|
|
for (uint i = 0; i < proposalNames.length; i++) { |
|
|
|
// 'Proposal({...})' creates a temporary |
|
|
|
// 'Proposal({...})' creates a temporary |
|
|
|
// Proposal object and 'proposals.push(...)' |
|
|
|
// Proposal object and 'proposals.push(...)' |
|
|
@ -47,11 +56,21 @@ contract Ballot { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* @dev Give 'voter' the right to vote on this ballot. May only be called by 'chairperson'. |
|
|
|
* @dev Give 'voter' the right to vote on this ballot. May only be called by 'chairperson'. |
|
|
|
* @param voter address of voter |
|
|
|
* @param voter address of voter |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
function giveRightToVote(address voter) public { |
|
|
|
function giveRightToVote(address voter) external { |
|
|
|
|
|
|
|
// If the first argument of `require` evaluates |
|
|
|
|
|
|
|
// to 'false', execution terminates and all |
|
|
|
|
|
|
|
// changes to the state and to Ether balances |
|
|
|
|
|
|
|
// are reverted. |
|
|
|
|
|
|
|
// This used to consume all gas in old EVM versions, but |
|
|
|
|
|
|
|
// not anymore. |
|
|
|
|
|
|
|
// It is often a good idea to use 'require' to check if |
|
|
|
|
|
|
|
// functions are called correctly. |
|
|
|
|
|
|
|
// As a second argument, you can also provide an |
|
|
|
|
|
|
|
// explanation about what went wrong. |
|
|
|
require( |
|
|
|
require( |
|
|
|
msg.sender == chairperson, |
|
|
|
msg.sender == chairperson, |
|
|
|
"Only chairperson can give right to vote." |
|
|
|
"Only chairperson can give right to vote." |
|
|
@ -68,20 +87,39 @@ contract Ballot { |
|
|
|
* @dev Delegate your vote to the voter 'to'. |
|
|
|
* @dev Delegate your vote to the voter 'to'. |
|
|
|
* @param to address to which vote is delegated |
|
|
|
* @param to address to which vote is delegated |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
function delegate(address to) public { |
|
|
|
function delegate(address to) external { |
|
|
|
|
|
|
|
// assigns reference |
|
|
|
Voter storage sender = voters[msg.sender]; |
|
|
|
Voter storage sender = voters[msg.sender]; |
|
|
|
|
|
|
|
require(sender.weight != 0, "You have no right to vote"); |
|
|
|
require(!sender.voted, "You already voted."); |
|
|
|
require(!sender.voted, "You already voted."); |
|
|
|
|
|
|
|
|
|
|
|
require(to != msg.sender, "Self-delegation is disallowed."); |
|
|
|
require(to != msg.sender, "Self-delegation is disallowed."); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Forward the delegation as long as |
|
|
|
|
|
|
|
// 'to' also delegated. |
|
|
|
|
|
|
|
// In general, such loops are very dangerous, |
|
|
|
|
|
|
|
// because if they run too long, they might |
|
|
|
|
|
|
|
// need more gas than is available in a block. |
|
|
|
|
|
|
|
// In this case, the delegation will not be executed, |
|
|
|
|
|
|
|
// but in other situations, such loops might |
|
|
|
|
|
|
|
// cause a contract to get "stuck" completely. |
|
|
|
while (voters[to].delegate != address(0)) { |
|
|
|
while (voters[to].delegate != address(0)) { |
|
|
|
to = voters[to].delegate; |
|
|
|
to = voters[to].delegate; |
|
|
|
|
|
|
|
|
|
|
|
// We found a loop in the delegation, not allowed. |
|
|
|
// We found a loop in the delegation, not allowed. |
|
|
|
require(to != msg.sender, "Found loop in delegation."); |
|
|
|
require(to != msg.sender, "Found loop in delegation."); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Voter storage delegate_ = voters[to]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Voters cannot delegate to accounts that cannot vote. |
|
|
|
|
|
|
|
require(delegate_.weight >= 1); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Since 'sender' is a reference, this |
|
|
|
|
|
|
|
// modifies 'voters[msg.sender]'. |
|
|
|
sender.voted = true; |
|
|
|
sender.voted = true; |
|
|
|
sender.delegate = to; |
|
|
|
sender.delegate = to; |
|
|
|
Voter storage delegate_ = voters[to]; |
|
|
|
|
|
|
|
if (delegate_.voted) { |
|
|
|
if (delegate_.voted) { |
|
|
|
// If the delegate already voted, |
|
|
|
// If the delegate already voted, |
|
|
|
// directly add to the number of votes |
|
|
|
// directly add to the number of votes |
|
|
@ -97,11 +135,10 @@ contract Ballot { |
|
|
|
* @dev Give your vote (including votes delegated to you) to proposal 'proposals[proposal].name'. |
|
|
|
* @dev Give your vote (including votes delegated to you) to proposal 'proposals[proposal].name'. |
|
|
|
* @param proposal index of proposal in the proposals array |
|
|
|
* @param proposal index of proposal in the proposals array |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
function vote(uint proposal) public { |
|
|
|
function vote(uint proposal) external { |
|
|
|
Voter storage sender = voters[msg.sender]; |
|
|
|
Voter storage sender = voters[msg.sender]; |
|
|
|
require(sender.weight != 0, "Has no right to vote"); |
|
|
|
require(sender.weight != 0, "Has no right to vote"); |
|
|
|
require(!sender.voted, "Already voted."); |
|
|
|
require(!sender.voted, "Already voted."); |
|
|
|
require(proposal < proposals.length, "Invalid proposal index."); |
|
|
|
|
|
|
|
sender.voted = true; |
|
|
|
sender.voted = true; |
|
|
|
sender.vote = proposal; |
|
|
|
sender.vote = proposal; |
|
|
|
|
|
|
|
|
|
|
@ -131,9 +168,9 @@ contract Ballot { |
|
|
|
* @dev Calls winningProposal() function to get the index of the winner contained in the proposals array and then |
|
|
|
* @dev Calls winningProposal() function to get the index of the winner contained in the proposals array and then |
|
|
|
* @return winnerName_ the name of the winner |
|
|
|
* @return winnerName_ the name of the winner |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
function winnerName() public view |
|
|
|
function winnerName() external view |
|
|
|
returns (bytes32 winnerName_) |
|
|
|
returns (bytes32 winnerName_) |
|
|
|
{ |
|
|
|
{ |
|
|
|
winnerName_ = proposals[winningProposal()].name; |
|
|
|
winnerName_ = proposals[winningProposal()].name; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |