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

Small amount of funds can remain stuck in contract due to precision loss

Summary

Small amount of funds can remain stuck in the ThePredicter contract due to precision loss.

Vulnerability Details

The method ThePredicter::withdraw is responsible for players to withdraw their prize pool. It contains the following calculation (shares * players.length * entranceFee) / totalShares but since Solidity does not support floating point numbers it could lead to precision loss. If there was no precision loss calling the method ThePredicter::withdrawPredictionFees by the organizer and ThePredicter::withdraw by all players eligable for rewards, it would leave the contract with 0 balance. However, small amounds of funds can remain as shown in the proof of code due to the precision loss.

Impact

Small amount of funds remain stucked in the contract.

Tools Used

Manual Review, Foundry

Proof Of Code

  1. Add the following test case ThePredicter.test.sol:

function test_remainingFundsAreStuckedInContract() public {
vm.startPrank(organizer);
uint256 registrationFee = 0.001 ether;
uint256 predictionFee = 0.003 ether;
scoreBoard = new ScoreBoard();
thePredicter = new ThePredicter(
address(scoreBoard),
registrationFee,
predictionFee
);
scoreBoard.setThePredicter(address(thePredicter));
vm.stopPrank();
address stranger2 = makeAddr("stranger2");
address stranger3 = makeAddr("stranger3");
address stranger4 = makeAddr("stranger4");
address stranger5 = makeAddr("stranger5");
address stranger6 = makeAddr("stranger6");
address stranger7 = makeAddr("stranger7");
address[7] memory players = [stranger, stranger2, stranger3, stranger4, stranger5, stranger6, stranger7];
for (uint256 i = 0; i < players.length; i++) {
address player = players[i];
vm.startPrank(player);
vm.deal(player, 1 ether);
thePredicter.register{value: registrationFee}();
vm.stopPrank();
vm.startPrank(organizer);
thePredicter.approvePlayer(player);
vm.stopPrank();
}
for (uint256 i = 0; i < players.length; i++) {
address player = players[i];
vm.startPrank(player);
thePredicter.makePrediction{value: predictionFee}(
0,
ScoreBoard.Result.First
);
thePredicter.makePrediction{value: predictionFee}(
1,
ScoreBoard.Result.First
);
thePredicter.makePrediction{value: predictionFee}(
2,
i % 2 == 0 ? ScoreBoard.Result.First : ScoreBoard.Result.Second
);
vm.stopPrank();
}
vm.startPrank(organizer);
scoreBoard.setResult(0, ScoreBoard.Result.First);
scoreBoard.setResult(1, ScoreBoard.Result.First);
scoreBoard.setResult(2, ScoreBoard.Result.First);
scoreBoard.setResult(3, ScoreBoard.Result.First);
scoreBoard.setResult(4, ScoreBoard.Result.First);
scoreBoard.setResult(5, ScoreBoard.Result.First);
scoreBoard.setResult(6, ScoreBoard.Result.First);
scoreBoard.setResult(7, ScoreBoard.Result.First);
scoreBoard.setResult(8, ScoreBoard.Result.First);
vm.stopPrank();
vm.startPrank(organizer);
thePredicter.withdrawPredictionFees();
vm.stopPrank();
for (uint256 i = 0; i < players.length; i++) {
address player = players[i];
vm.startPrank(player);
thePredicter.withdraw();
vm.stopPrank();
}
uint256 remainingBalance = address(thePredicter).balance;
console.log("Remaining funds:", remainingBalance);
assert(remainingBalance > 0);
}
  1. Execute the following command: forge test --mt test_remainingFundsAreStuckedInContract -vvvvvvv

  2. Verify from the logs that there is small amount of funds remaining: Remaining funds: 4

Recommendations

Implement method which allows for the organizer to withdraw the remaining funds.

Updates

Lead Judging Commences

NightHawK Lead Judge about 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

Dust amount might remain

Small amount of funds can remain stuck in contract due to a precision loss.

Support

FAQs

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