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;
if (game.bet > 0) {
uint256 totalPot = game.bet * 2;
uint256 fee = (totalPot * PROTOCOL_FEE_PERCENT) / 100;
uint256 refundPerPlayer = (totalPot - fee) / 2;
accumulatedFees += fee;
emit FeeCollected(_gameId, fee);
(bool successA,) = game.playerA.call{value: refundPerPlayer}("");
(bool successB,) = game.playerB.call{value: refundPerPlayer}("");
require(successA && successB, "Transfer failed");
}
if (game.bet == 0) {
winningToken.mint(game.playerA, 1);
winningToken.mint(game.playerB, 1);
}
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;
if (game.bet > 0) {
uint256 totalPot = game.bet * 2;
uint256 fee = (totalPot * PROTOCOL_FEE_PERCENT) / 100;
uint256 refundPerPlayer = (totalPot - fee) / 2;
accumulatedFees += fee;
emit FeeCollected(_gameId, fee);
(bool successA,) = game.playerA.call{value: refundPerPlayer}("");
(bool successB,) = game.playerB.call{value: refundPerPlayer}("");
require(successA && successB, "Transfer failed");
}
if (game.bet == 0) {
winningToken.mint(game.playerA, 1);
winningToken.mint(game.playerB, 1);
} else {
revert("Invalid game state");
}
emit GameFinished(_gameId, address(0), 0);
}