Beginner FriendlyFoundry
100 EXP
View results
Submission Details
Severity: high
Valid

The ```rewardPerVoter``` amount of the last voter in the 'for' voters list is incorrect

Summary

The rewardPerVoter amount of the last voter in the 'for' voters list is incorrect. The rewardPerVoter parameter computation within the VotingBooth::_distributeRewards is wrong because uses the totalVotes instead of the totalVotesFor. Due to this miscalculation, the distribution of rewards of the last voter becomes wrong.

Note: also the rewardPerVoter calculation of all "for" voters is incorrect as indicated in the "Incorrect calculation of the rewardPerVoter within the VotingBooth::_distributeRewards computation leads to inaccurate distribution of rewards" findings. Please refer to this finding for more details.

Vulnerability Details

function _distributeRewards() private {
// get number of voters for & against
uint256 totalVotesFor = s_votersFor.length;
uint256 totalVotesAgainst = s_votersAgainst.length;
uint256 totalVotes = totalVotesFor + totalVotesAgainst;
uint256 totalRewards = address(this).balance;
if (totalVotesAgainst >= totalVotesFor) {
_sendEth(s_creator, totalRewards);
}
else {
uint256 rewardPerVoter = totalRewards / totalVotes;
for (uint256 i; i < totalVotesFor; ++i) {
if (i == totalVotesFor - 1) {
@> rewardPerVoter = Math.mulDiv(totalRewards, 1, totalVotes, Math.Rounding.Ceil);
}
_sendEth(s_votersFor[i], rewardPerVoter);
}
}
}

Impact

The rewardPerVoter variable computation of the last voter is wrong. After a vote passes, an equal reward is distributed to the for (less than 1 Wei) and the contract balance should be 0 but it is not.

Proof Of Code
function testVotePassesAndWrongMoneyIsSentToTheLastVoters() public {
console.log("Initial contract balance", address(booth).balance);
vm.prank(address(0x1));
booth.vote(true);
vm.prank(address(0x2));
booth.vote(true);
vm.prank(address(0x3));
booth.vote(false);
console.log("User 1 balance", address(0x1).balance);
console.log("User 2 balance", address(0x2).balance);
assert(!booth.isActive() && address(booth).balance != 0);
console.log("Final contract balance", address(booth).balance);
}
Logs:
Initial contract balance 10000000000000000000
User 1 balance 5000000000000000000
User 2 balance 3333333333333333334
Final contract balance 1666666666666666666

Tools Used

Manual review

Recommendations

Use the totalVotesFor instead of the totalVotes in the rewardPerVoter computation.

function _distributeRewards() private {
// get number of voters for & against
uint256 totalVotesFor = s_votersFor.length;
uint256 totalVotesAgainst = s_votersAgainst.length;
uint256 totalVotes = totalVotesFor + totalVotesAgainst;
uint256 totalRewards = address(this).balance;
if (totalVotesAgainst >= totalVotesFor) {
_sendEth(s_creator, totalRewards);
}
else {
uint256 rewardPerVoter = totalRewards / totalVotes;
for (uint256 i; i < totalVotesFor; ++i) {
if (i == totalVotesFor - 1) {
- rewardPerVoter = Math.mulDiv(totalRewards, 1, totalVotes, Math.Rounding.Ceil);
+ rewardPerVoter = Math.mulDiv(totalRewards, 1, totalVotesFor, Math.Rounding.Ceil);
}
_sendEth(s_votersFor[i], rewardPerVoter);
}
}
}
Updates

Lead Judging Commences

0xnevi Lead Judge almost 2 years ago
Submission Judgement Published
Validated
Assigned finding tags:

VotingBooth._distributeRewards(): Incorrect computation of rewardPerVoter

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.