Solidity Example Voting
Solidity Example Voting Main Tips
- This example of a smart contract is much more complex than the ones we previously introduced but introduces many important new concepts.
- The following smart contract is for voting and addresses the main problems of electronic voting – how to prevent manipulation and assign the voting rights to the right people.
Solidity Example Voting
This Solidity example will create a smart contract for voting. The goal is to make vote counting automatic and transparent.
The very idea of the script is that a contract is created for each ballot and providing a short name for every option. The creator of the contract, acting as the chairperson, gives the voting rights to all voter addresses individually.
The people that possess the addresses can then choose to vote or delegate their vote to someone else they choose.
In the end, a function is run to find out which proposal has the most votes and to return the result.
Here’s the code itself with explanations commented:
Example
pragma solidity ^0.4.11; /// @title Voting smart contract with option to delegate. contract Ballot { // This will declare a new complex data type, which we can use to represent individual voters. struct ballotVoter { uint delegateWeight; // delegateWeight is accumulated by delegation bool voteSpent; // if true, that person already used their vote address delegateTo; // the person that the voter chooses to delegate their vote to instead of voting themselves uint voteIndex; // index of the proposal that was voted for } // This is a type for a single proposal. struct Proposal { bytes32 proposalName; // short name for the proposal (up to 32 bytes) uint voteCount; // the number of votes accumulated } address public chairman; // Declare state variable to store a 'ballotVoter' struct for every possible address. mapping(address => ballotVoter) public ballotVoters; // A dynamically-sized array of 'Proposal' structs. Proposal[] public proposalsOption; /// New ballot for choosing one of the 'proposalNames' function Ballot(bytes32[] proposalNames) { chairman = msg.sender; ballotVoters[chairman].delegateWeight = 1; // For every provided proposal names, a new proposal object is created and added to the array's end. for (uint i = 0; i < proposalNames.length; i++) { // 'Proposal({...})' will create a temporary Proposal object // 'proposalsOption.push(...)' will append it to the end of 'proposalsOption'. proposalsOption.push(Proposal({ proposalName: proposalNames[i] , voteCount: 0 })); } } // Give 'ballotVoter' the right to cast a vote on this ballot. // Can only be called by 'chairman'. function giveVotingRights(address ballotVoter) { // In case the argument of 'require' is evaluted to 'false', // it will terminate and revert all // state and Ether balance changes. It is often // a good idea to use this in case the functions are // not called correctly. Keep in mind, however, that this // will currently also consume all of the provided gas // (this is planned to change in the future). require((msg.sender == chairman) && !ballotVoters[ballotvoter].voteSpent && (ballotVoters[ballotvoter].delegateWeight == 0)); ballotVoters[ballotvoter].delegateWeight = 1; } /// Delegate your vote to the voter 'to'. function delegateTo(address to) { // assigns reference ballotVoter storage sender = ballotVoters[msg.sender]; require(!sender.voteSpent); // Self-delegation is not allowed. require(to != msg.sender); // Forward the delegation as long as // 'to' is delegated too. // In general, such loops can be very dangerous, // since if they run for too long, they might // need more gas than available inside a block. // In that scenario, no delegation is made // but in other situations, such loops might // cause a contract to get "stuck" completely. while (ballotVoters[to].delegateTo != address(0)) { to = ballotVoters[to].delegateTo; // We found a loop in the delegation, not allowed. require(to != msg.sender); } // Since 'sender' is a reference, this will modify 'ballotVoters[msg.sender].voteSpent' sender.voteSpent = true; sender.delegateTo = to; ballotVoter storage delegateTo = ballotVoters[to]; if (delegateTo.voteSpent) { // If the delegated person already voted, directly add to the number of votes proposalsOption[delegateTo.voteIndex].voteCount += sender.delegateWeight; } else { // If the delegated did not vote yet, // add to her delegateWeight. delegateTo.delegateWeight += sender.delegateWeight; } } /// Give your vote (including votes delegated to you) to proposal 'proposalsOption[proposal].proposalName'. function voteIndex(uint proposal) { ballotVoter storage sender = ballotVoters[msg.sender]; require(!sender.voteSpent); sender.voteSpent = true; sender.voteIndex = proposal; // If 'proposal' is out of the range of the array, // this will throw automatically and revert all // changes. proposalsOption[proposal].voteCount += sender.delegateWeight; } /// @dev Computes which proposal wins by taking all previous votes into account. function winnerProposal() constant returns (uint winnerProposal) { uint winnerVoteCount = 0; for (uint p = 0; p < proposalsOption.length; p++) { if (proposalsOption[p].voteCount > winnerVoteCount) { winnerVoteCount = proposalsOption[p].voteCount; winnerProposal = p; } } } /// Calls winnerProposal() function in order to acquire the index /// of the winner which the proposalsOption array contains and then /// returns the name of the winning proposal function winner() constant returns (bytes32 winner) { winner = proposalsOption[winnerProposal()].proposalName; } }
Note: The code given here requires a lot of transactions to do its job. Reducing the number of transactions required could make it more efficient. Can you think of a way to improve it?