Summary
The comments specify that if at the last voter round up to avoid leaving dust; this means that the last voter can get 1 wei more than the rest
. This implies that there shouldn't be any dust left in the contract. This is false.
Vulnerability Details
Rounding up at the last voter is not sufficient to prevent dust remaining stuck in the contract.
Please copy the code below in a new test file:
pragma solidity ^0.8.23;
import {VotingBooth} from "../src/VotingBooth.sol";
import {Test} from "forge-std/Test.sol";
contract MyFuzzFoundry is Test {
VotingBooth booth;
function setUp() public virtual {}
function testHappyCase(uint256 x, uint256 amount) public {
vm.assume(x <= 9);
vm.assume(x >= 3);
vm.assume(x % 2 == 1);
vm.assume(amount > 1 ether);
address[] memory participants = new address[](x);
address participant;
for (uint160 i = 1; i < x + 1; i++) {
participant = address(i);
participants[i - 1] = participant;
}
uint256 catchedLenght = participants.length;
assertEq(participants.length, x);
vm.assume(amount >= 1 ether);
deal(address(this), amount);
booth = new VotingBooth{value: amount}(participants);
uint256 requiredToPass = catchedLenght / 2 + 1;
for (uint j; j < requiredToPass; ++j) {
vm.prank(participants[j]);
booth.vote(true);
}
assertEq(booth.isActive(), false);
assertEq(address(booth).balance, 0);
}
}
Run it using the following command:
forge test --mt testHappyCase -vvv
Relevant part of result:
├─ [292] VotingBooth::isActive() [staticcall]
│ └─ ← false
├─ emit log(val: "Error: a == b not satisfied [uint]")
├─ emit log_named_uint(key: " Left", val: 1)
├─ emit log_named_uint(key: " Right", val: 0)
├─ [0] VM::store(VM: [0x7109709ECfa91a80626fF3989D68f67F5b1DD12D], 0x6661696c65640000000000000000000000000000000000000000000000000000, 0x0000000000000000000000000000000000000000000000000000000000000001)
│ └─ ← ()
└─ ← ()
Test result: FAILED. 0 passed; 1 failed; 0 skipped; finished in 68.91ms
Ran 1 test suites: 0 tests passed, 1 failed, 0 skipped (1 total tests)
Failing tests:
Encountered 1 failing test in test/MyFuzzFoundry.t.sol:MyFuzzFoundry
[FAIL. Reason: assertion failed; counterexample: calldata=0x7e6cbe250000000000000000000000000000000000000000000000000000000000000005bfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff args=[5, 86844066927987146567678238756515930889952488499230423029593188005934847229951 [8.684e76]]] testHappyCase(uint256,uint256) (runs: 270, μ: 777433, ~: 783174)
Impact
Code doesn't work as intended. There's dust after the reward distribution.
Tools Used
Manual review + Forge testing
Recommendations
Update the code to send all the remaining balance in the last transfer of the for loop inside the _distributeRewards
function.