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 {
vm.startPrank(stranger);
vm.deal(stranger, 1 ether);
thePredicter.register{value: 0.04 ether}();
vm.stopPrank();
vm.startPrank(organizer);
thePredicter.approvePlayer(stranger);
scoreBoard.setResult(0, ScoreBoard.Result.First);
vm.stopPrank();
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();
vm.warp(1723752000 - 68400);
vm.startPrank(stranger);
thePredicter.makePrediction{value: 0.0001 ether}(
0,
ScoreBoard.Result.First
);
vm.stopPrank();
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
-
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.
-
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.
-
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
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);
}