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

[H-3] Players don't have enough time to make their predictions.

Summary

Due to a time miscalculation in ScoreBoard::setPrediction() and ThePredicter::makePrediction(), players are not able to make predictions within the correct time frame.

Vulnerability Details

As specified in the protocol's specification, players should set their predictions till 19:00 of the match day. However, the implementation of ScorBoard::setPrediction() and ThePredicter::makePrediction() implies a mistake in the calculation for the time limit, effectively reducing the deadline for setting predictions.

function setPrediction(
address player,
uint256 matchNumber,
Result result
) public {
@> if (block.timestamp <= START_TIME + matchNumber * 68400 - 68400)
playersPredictions[player].predictions[matchNumber] = result;
playersPredictions[player].predictionsCount = 0;
for (uint256 i = 0; i < NUM_MATCHES; ++i) {
if (
playersPredictions[player].predictions[i] != Result.Pending &&
playersPredictions[player].isPaid[i]
) ++playersPredictions[player].predictionsCount;
}
}
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);
}

PoC:

function test_NotEnoughPredictionTime() public {
// 1. Give stranger some funds
vm.startPrank(stranger);
vm.deal(stranger, 1 ether);
thePredicter.register{value: 0.04 ether}();
vm.stopPrank();
// 2. The organizer approves the player and sets result
vm.startPrank(organizer);
thePredicter.approvePlayer(stranger);
scoreBoard.setResult(0, ScoreBoard.Result.First);
vm.stopPrank();
// 3. "stranger" makes a prediction at 18:00 (1 hour before deadline)
vm.expectRevert(
abi.encodeWithSelector(
ThePredicter__PredictionsAreClosed.selector
)
);
vm.warp(1723752000 - 7200);
vm.startPrank(stranger);
thePredicter.makePrediction{value: 0.0001 ether}(
0,
ScoreBoard.Result.First
);
vm.stopPrank();
// 4. "stranger" makes a prediction at 01:00
vm.warp(1723752000 - 68400);
vm.startPrank(stranger);
thePredicter.makePrediction{value: 0.0001 ether}(
0,
ScoreBoard.Result.First
);
vm.stopPrank();
// 5. check that the prediction is made
assertEq(scoreBoard.getPlayerScore(stranger), 2);
}

The test above proves that players can't submit their predictions at 18:00 (i.e, one hour before the deadline), but they can submit it at 01:00 (i.e, 19 hours before the deadline).

Impact

  1. Player Dissatisfaction: Players who cannot make predictions within the expected time frame may become frustrated and dissatisfied with the game, potentially leading to a loss of engagement and trust in the platform.

  2. Unfair Competitive Advantage: Players who are aware of the exact time limitations might gain an unfair advantage over others, leading to an unbalanced and unfair gaming experience.

  3. Financial Implications: If the platform relies on prediction fees or other monetary inputs from players, limiting the time frame for predictions might reduce the total number of predictions made, impacting revenue.

Tools Used

  • Manual review.

  • Foundry (to run test cases)

Recommendations

Fix the time calculation in ScorBoard::setPrediction() and ThePredicter::makePrediction() as follows:

function setPrediction(
address player,
uint256 matchNumber,
Result result
) public {
- if (block.timestamp <= START_TIME + matchNumber * 68400 - 68400)
+ if (block.timestamp <= START_TIME + matchNumber * 86400 - 3600)
playersPredictions[player].predictions[matchNumber] = result;
playersPredictions[player].predictionsCount = 0;
for (uint256 i = 0; i < NUM_MATCHES; ++i) {
if (
playersPredictions[player].predictions[i] != Result.Pending &&
playersPredictions[player].isPaid[i]
) ++playersPredictions[player].predictionsCount;
}
}
function makePrediction(
uint256 matchNumber,
ScoreBoard.Result prediction
) public payable {
if (msg.value != predictionFee) {
revert ThePredicter__IncorrectPredictionFee();
}
- if (block.timestamp > START_TIME + matchNumber * 68400 - 68400) {
+ if (block.timestamp > START_TIME + matchNumber * 86400 - 3600) {
revert ThePredicter__PredictionsAreClosed();
}
scoreBoard.confirmPredictionPayment(msg.sender, matchNumber);
scoreBoard.setPrediction(msg.sender, matchNumber, prediction);
}
Updates

Lead Judging Commences

NightHawK Lead Judge 11 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Match timestamps are incorrect

In both contracts there is a similar error in the computation of the timestamps of the matches.

Support

FAQs

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