The smart contract's _distributeRewards()
function exhibits a critical flaw in its reward distribution logic. Specifically, it miscalculates the distribution of funds by dividing the total available rewards not by the count of approving ('for') votes but by the combined tally of all votes, diluting the reward per approving participant. This not only results in a less-than-expected payout for voters who supported the proposal but also leads to a surplus of undistributed funds remaining within the contract.
If everyone who votes are 'for' voters, the rewards calculated would be accurate as the total number of voters are all 'for' voters. However, the problem arises when the 'against' votes are present but there are enough 'for' votes for the proposal to pass.
The reward will be calculated with the assumption that it will be distributed to all the voters, but only distributing to the 'for' voters, leading to a mismatch between the calculated and actual reward distribution.
Imagine a scenario similar to the test provided.
There are in total 5 voters in the allowedList
. Now, in order for it to pass and distribute the rewards, 3 out of 5 needs to vote. The number of 'for' voters need to be more than 'against' voters too. The first voter votes 'against' it, while the second and third voter vote 'for'. It passes the MIN_QUORUM
which will then call _distributeRewards()
, and it will run the else statement which is to distribute the rewards to all the 'for' voters.
However, the flaw lies in line 192, where it calculates how much of the rewards is to be distributed. It calculates the rewardPerVoter
by dividing the totalRewards
by totalVotes
which is inclusive of the number of voters that are against it. This means that it will be divided by 3. Then, it loops through the number of totalVotesFor
which is 2, and distribute accordingly. However, the total distributed will be less than the actual amount since it was calculated for 3 people but only distributed to 2 of the 'for' voters, leading to undistributed funds in the contract.
Say there is 120 ETH in the VotingBooth
contract, where one voter opposes and two support the proposal, the contract will proceed and distribute the rewards to the 'for' voters.
The rewardPerVoter
will be incorrectly calculated as 120 ETH / 3 votes = 40 ETH
per voter, leading to only 80 ETH
being distributed. The proper calculation should reflect 120 ETH / 2 votes = 60 ETH
per voter.
The impact of this vulnerability as it results in 'for' voters receiving less ETH than intended, undermining the trust in the protocol.
Add the test to the file: test/VotingBoothTest.t.sol
.
Run the test:
Manual Review, Foundry
Fix the calculation flaw by changing the variable that totalRewards is divided by.
Change the calculation for the last 'for' voter too
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.