Rock Paper Scissors

First Flight #38
Beginner FriendlySolidity
100 EXP
View results
Submission Details
Severity: high
Invalid

Missing else block in _handleTie() __RockPaperScis

Summary

The _handleTie function has else block missing.

Vulnerability Details

The function assumes that:

  • If game.bet > 0, it's an ETH game, and it refunds ETH (minus protocol fee).

  • If game.bet == 0, it's a token game, and it mints tokens.

    If game.bet has some weird unexpected value (e.g., corrupted storage) then neither condition runs, and:

    • No ETH refund

    • No tokens minted

    • No error thrown

    • Players get nothing

function _handleTie(uint256 _gameId) internal {
Game storage game = games[_gameId];
game.state = GameState.Finished;
// Return ETH bets to both players, minus protocol fee
if (game.bet > 0) {
// Calculate protocol fee (10% of total pot)
uint256 totalPot = game.bet * 2;
uint256 fee = (totalPot * PROTOCOL_FEE_PERCENT) / 100;
uint256 refundPerPlayer = (totalPot - fee) / 2;
// Accumulate fees for admin
accumulatedFees += fee;
emit FeeCollected(_gameId, fee);
// Refund both players
(bool successA,) = game.playerA.call{value: refundPerPlayer}("");
(bool successB,) = game.playerB.call{value: refundPerPlayer}("");
require(successA && successB, "Transfer failed");
}
// Return tokens for token games
if (game.bet == 0) {
winningToken.mint(game.playerA, 1);
winningToken.mint(game.playerB, 1);
}
// Since in a tie scenario, the total prize is split equally
emit GameFinished(_gameId, address(0), 0);
}

Impact

Funds loss

Tools Used

Manual review

Recommendations

Add else block after the if (game.bet == 0).

Fixed Code:

function _handleTie(uint256 _gameId) internal {
Game storage game = games[_gameId];
game.state = GameState.Finished;
// Return ETH bets to both players, minus protocol fee
if (game.bet > 0) {
// Calculate protocol fee (10% of total pot)
uint256 totalPot = game.bet * 2;
uint256 fee = (totalPot * PROTOCOL_FEE_PERCENT) / 100;
uint256 refundPerPlayer = (totalPot - fee) / 2;
// Accumulate fees for admin
accumulatedFees += fee;
emit FeeCollected(_gameId, fee);
// Refund both players
(bool successA,) = game.playerA.call{value: refundPerPlayer}("");
(bool successB,) = game.playerB.call{value: refundPerPlayer}("");
require(successA && successB, "Transfer failed");
}
// Return tokens for token games
if (game.bet == 0) {
winningToken.mint(game.playerA, 1);
winningToken.mint(game.playerB, 1);
} else {
revert("Invalid game state");
}
// Since in a tie scenario, the total prize is split equally
emit GameFinished(_gameId, address(0), 0);
}
Updates

Appeal created

m3dython Lead Judge 7 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

Informational

Code suggestions or observations that do not pose a direct security risk.

m3dython Lead Judge 7 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

Informational

Code suggestions or observations that do not pose a direct security risk.

Support

FAQs

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