diff --git a/libs/remix-ws-templates/src/templates/remixDefault/contracts/3_Ballot.sol b/libs/remix-ws-templates/src/templates/remixDefault/contracts/3_Ballot.sol index ffcc6c3609..56b2159551 100644 --- a/libs/remix-ws-templates/src/templates/remixDefault/contracts/3_Ballot.sol +++ b/libs/remix-ws-templates/src/templates/remixDefault/contracts/3_Ballot.sol @@ -7,7 +7,9 @@ pragma solidity >=0.7.0 <0.9.0; * @dev Implements voting process along with vote delegation */ contract Ballot { - + // This declares a new complex type which will + // be used for variables later. + // It will represent a single voter. struct Voter { uint weight; // weight is accumulated by delegation bool voted; // if true, that person already voted @@ -15,6 +17,7 @@ contract Ballot { uint vote; // index of the voted proposal } + // This is a type for a single proposal. struct Proposal { // If you can limit the length to a certain number of bytes, // always use one of bytes1 to bytes32 because they are much cheaper @@ -24,8 +27,11 @@ contract Ballot { address public chairperson; + // This declares a state variable that + // stores a 'Voter' struct for each possible address. mapping(address => Voter) public voters; + // A dynamically-sized array of 'Proposal' structs. Proposal[] public proposals; /** @@ -36,6 +42,9 @@ contract Ballot { chairperson = msg.sender; 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++) { // 'Proposal({...})' creates a temporary // 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'. * @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( msg.sender == chairperson, "Only chairperson can give right to vote." @@ -60,7 +79,7 @@ contract Ballot { !voters[voter].voted, "The voter already voted." ); - require(voters[voter].weight == 0); + require(voters[voter].weight == 0, "Voter already has the right to vote."); voters[voter].weight = 1; } @@ -68,20 +87,39 @@ contract Ballot { * @dev Delegate your vote to the voter 'to'. * @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]; + require(sender.weight != 0, "You have no right to vote"); require(!sender.voted, "You already voted."); + 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)) { to = voters[to].delegate; // We found a loop in the delegation, not allowed. 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.delegate = to; - Voter storage delegate_ = voters[to]; + if (delegate_.voted) { // If the delegate already voted, // directly add to the number of votes @@ -97,7 +135,7 @@ contract Ballot { * @dev Give your vote (including votes delegated to you) to proposal 'proposals[proposal].name'. * @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]; require(sender.weight != 0, "Has no right to vote"); require(!sender.voted, "Already voted."); @@ -130,7 +168,7 @@ contract Ballot { * @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 */ - function winnerName() public view + function winnerName() external view returns (bytes32 winnerName_) { winnerName_ = proposals[winningProposal()].name;