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

Incorrect Reward Distribution in Successful Proposals with Votes Against

Summary

The contract exhibits a flaw in the reward distribution mechanism for successful proposals that receive votes against. The _distributeRewards function divides totalRewards by the sum of s_votersFor.length and s_votersAgainst.length, leading to incorrect reward allocations for voters who supported the proposal. This vulnerability manifests when there are votes against a successful proposal, resulting in funds being left stuck in the contract.

Vulnerability

The vulnerability arises due to the inclusion of number of votes against a successful proposal in the rewardPerVoter calculation. This miscalculation impacts the reward distribution, making it dependent on the total number of votes, rather than the number of voters who supported the proposal.

Impact

The impact of this vulnerability is high, as it compromises the fairness of the reward distribution specifically in successful proposals with votes against. Additionally, funds are left remaining in the contract, creating a risk of loss. Unanimously successful proposals with no votes against exhibit the correct reward calculation.

Tools Used

Foundry

Proof of Concept

As a PoC, include the following test in the VotingBoothTest.t.sol file:

function testIncorrectFundDistributionForMixedVotes() public {
uint256 balanceBefore = address(booth).balance;
console.log("Voting Booth starting balance: ", balanceBefore);
vm.prank(address(0x1));
booth.vote(true);
vm.prank(address(0x2));
booth.vote(false);
vm.prank(address(0x3));
booth.vote(true);
uint256 balanceAfter = address(booth).balance;
console.log("Voting Booth ending balance : ", balanceAfter);
assert(address(booth).balance > 0);
}

Recommendation

Modify the following lines of the _distributeRewards function:

- uint256 rewardPerVoter = totalRewards / totalVotes;
+ uint256 rewardPerVoter = totalRewards / totalVotesFor;
- rewardPerVoter = Math.mulDiv(totalRewards, 1, totalVotes, Math.Rounding.Ceil);
+ rewardPerVoter = Math.mulDiv(totalRewards, 1, totalVotesFor, Math.Rounding.Ceil);

This modification ensures that rewards are distributed only to voters who supported the successful proposal in scenarios where there are votes against.

After modifying, you can run the following test and see that all funds are distributed to users who vote in favor of a successful proposal, regardless of the voter mix:

function testCorrectFundDistributionForMixedVotes() public {
uint256 balanceBefore = address(booth).balance;
console.log("Voting Booth starting balance: ", balanceBefore);
vm.prank(address(0x1));
booth.vote(true);
vm.prank(address(0x2));
booth.vote(false);
vm.prank(address(0x3));
booth.vote(true);
uint256 balanceAfter = address(booth).balance;
console.log("Voting Booth ending balance : ", balanceAfter);
assert(address(booth).balance == 0);
}
Updates

Lead Judging Commences

0xnevi Lead Judge over 1 year 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.