Solidity Voting Smart Contract
In our voting contract we have the following features:
- Everyone can cast vote.
- Anybody can check its vote.
- Admin can whitelist (allow) voters
- Admin can blacklist (disallow) voters
- Blacklist voters can't cast vote.
- A single address can cast one vote only.
- Everyone can check the total number of votes of a party.
For creating such a contract, first I am making a contract named VotingContract.
I have a mapping of parties. Parties mapping's keys are string (party name) and values are uint (no. of votes of that party). After that, I declare a variable owner and inside a constructor, I am setting the owner to msg.sender. Inside the constructor parameter, I take the Array of parties. I iterate the parameter Array and assign each iterated value to the parties' mapping with the value of 1.
Value 1 is for represents that the party is valid, further, it becomes the total votes for that party.
Now, I have to define a Struct for voting with some information. Struct Vote has three properties:
- string: party (Here comes the party's name)
- power: uint (this is voting power initially 1)
- isVoted: bool (check for already voted or not)
Struct can be dfined outside of contract
Well, we have now the mapping of parties, owner, and struct of Vote. Now, for total valid votes, I have a votes mapping, where the keys are addresses of voters and values are the votes (struct).
Now, I am creating a function castVote where everyone has a right to vote.
In the first check, I am checking whether the party is valid or not by checking not to 0, because at the constructor, I am assigning value 1 to each party indicating that the party is valid.
The second check is simple. It simply checks whether the caller's address was already voted on or not.
After passing two checks, I am incrementing the party value that we get from the parameter _party. Also, I am passing the caller's address to votes mapping with the new Struct of Vote.
Now, I have a function checkMyvote, this simply returns the Vote struct for the caller struct from the votes mapping.
There is another function status, which accepts the name of the party and returns the total number of votes of that party.
parties[_party]-1 means removing the default value of votes.
Additional functionalities
Now we have two additional functionalities:
- The owner can whitelist voters ( disallow )
- The owner can blacklist voters ( allow )
By default, all addresses (voters) are whitelisted.
We have a mapping isblacklist, that holds the addresses of voters and bool values for allowed and disallowed.
Because by default the bool values are false resulting in everyone being whitelisted ( allowed ).
Now, I have a modifier _onlyowner, this modifier simply checks whether the caller of the function is the owner or not.
Then I put this modifier's check in my BlacklistVoter function. In this function, the first require statement is checking that the target address shouldn't already be blacklisted by passing the targeted address to the isBlacklist mapping. Then I am passing the target address to votes mapping to get its previous vote.
- If isBlacklist mapping returns true for the target address, this means that the target address is already blacklisted so ! statement toggle value to throw the error.
- I am getting the name of the party ( the target address voted for ) and passing that name into parties mapping and decrement the value by the (target address voting) power. By default, mapping returns the default value, so the voting power is 0 if the target address doesn't cast the vote.
- After I am cleaning the target address vote from the votes and save that vote to the blacklistVotes mapping.
- Finally, I am assigning true for the target address in isBlacklist mapping, ensuring that the address is blacklisted.
Same code for WhitelistVoter function with some changes:
- The first require checks if the address is already whitelisted by checking isblacklist, if true then our code runs and if false it throws an error.
- I (get target) vote from blacklistVotes and save it to votes mapping, if he didn't cast yet, then it saves the default vote struct.
- Then I am incrementing that party votes as I decrement in BlacklistVoter function.
- Next, I cleared the (target address) vote from blacklistVotes mapping.
- Finally, I am removing that address form isBlacklist mapping.
So, our entire code looks like this, if want to get the source code, check out my GitHub repo: Repository
Feel free to star 🙏.
About Anas Aqeel
I’m currently working Frontend Development having an experience of building Web applications with JavaScript / React-JS / Node-JS and some other cool libraries and frameworks. I’m currently learning web3 technologies such as Solidity, Ether-JS and web3.