Beginner FriendlyFoundry
100 EXP
View results
Submission Details
Severity: medium
Invalid

`ThePredicter:: makePrediction` has flawed logic, Which requires prediction fees to already paid round in case of updating the choice

Summary

Ask fees for already paid round, if user has to update his choice.

Vulnerability Details

function makePrediction(
uint256 matchNumber,
ScoreBoard.Result prediction
) public payable {
if (msg.value != predictionFee) {
revert ThePredicter__IncorrectPredictionFee();
}
if (block.timestamp > START_TIME + matchNumber * 68400 - 68400) {
revert ThePredicter__PredictionsAreClosed();
}
scoreBoard.confirmPredictionPayment(msg.sender, matchNumber);
scoreBoard.setPrediction(msg.sender, matchNumber, prediction);
}

In above function, there is no check if user has already paid for given round. It will require user to pay fees again and again even if they want to update there choice for the round, they already paid for.
This will be loss of user funds, as they will be paying unnecessarily.

POC

In existing test suite, add following test

function test_FeesNeedsToPaidEveryTimeForPredictionEvenToChangeCurrentRoundChoice() public {
vm.startPrank(stranger);
vm.warp(1);
vm.deal(stranger, 1 ether);
thePredicter.register{value: 0.04 ether}();
vm.stopPrank();
vm.startPrank(organizer);
vm.warp(2);
thePredicter.approvePlayer(stranger);
vm.stopPrank();
vm.startPrank(stranger);
thePredicter.makePrediction{value: 0.0001 ether}(
0,
ScoreBoard.Result.First
);
vm.expectRevert(abi.encodeWithSelector(
ThePredicter__IncorrectPredictionFee.selector
));
thePredicter.makePrediction{value: 0 ether}(
1,
ScoreBoard.Result.Second
);
vm.stopPrank();
}

run the following command in terminal forge run --mt test_FeesNeedsToPaidEveryTimeForPredictionEvenToChangeCurrentRoundChoice and following results will be printed out as output.

[⠊] Compiling...
[⠃] Compiling 1 files with Solc 0.8.20
[⠊] Solc 0.8.20 finished in 1.89s
Compiler run successful!
Ran 1 test for test/ThePredicter.test.sol:ThePredicterTest
[PASS] test_FeesNeedsToPaidEveryTimeForPredictionEvenToChangeCurrentRoundChoice() (gas: 188078)
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 9.03ms (793.21µs CPU time)
Ran 1 test suite in 159.14ms (9.03ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)

Impact

Loss of user funds

Tools Used

Manual Review

Recommendations

Add a check, if user has already paid for particular round, don't ask fees again.

+ mapping (address => mapping(uint256=> bool) isPaid;
function makePrediction(
uint256 matchNumber,
ScoreBoard.Result prediction
) public payable {
+ if(![msg.sender][matchNumber]){
if (msg.value != predictionFee) {
revert ThePredicter__IncorrectPredictionFee();
}
+ [msg.sender][matchNumber] = true;
+ scoreBoard.confirmPredictionPayment(msg.sender, matchNumber);
}
if (block.timestamp > START_TIME + matchNumber * 68400 - 68400) {
revert ThePredicter__PredictionsAreClosed();
}
- scoreBoard.confirmPredictionPayment(msg.sender, matchNumber);
scoreBoard.setPrediction(msg.sender, matchNumber, prediction);
}
Updates

Lead Judging Commences

NightHawK Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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