Summary
The player must pay 1 or more ETH to play the game. When player win the game return reward is fixed to 2 ETH.
Vulnerability Details
The startGame()
function requires player to send 1 or more Ether to start the game as show in line 3 of the following code.
TwentyOne.sol
function startGame() public payable returns (uint256) {
address player = msg.sender;
require(msg.value >= 1 ether, "not enough ether sent");
initializeDeck(player);
uint256 card1 = drawCard(player);
uint256 card2 = drawCard(player);
addCardForPlayer(player, card1);
addCardForPlayer(player, card2);
return playersHand(player);
}
The endGame()
function return fixed 2 Ether amount of reward as show in line 6 of the following code.
TwentyOne.sol
function endGame(address player, bool playerWon) internal {
delete playersDeck[player].playersCards;
delete dealersDeck[player].dealersCards;
delete availableCards[player];
if (playerWon) {
payable(player).transfer(2 ether);
emit FeeWithdrawn(player, 2 ether);
}
}
When user pay more than 1 Ether they will still get the same fixed winning reward.
Impact
Users spend unnecessary ETH when paying more than 1 Ether.
Tools Used
Manual code reading.
Recommendations
I recommend fixing the require playing amount or refunding the amount that exceeding 1 Ether for example:
Case 1: fixing the require playing amount
function startGame() public payable returns (uint256) {
address player = msg.sender;
require(msg.value == 1 ether, "not enough ether sent");
initializeDeck(player);
uint256 card1 = drawCard(player);
uint256 card2 = drawCard(player);
addCardForPlayer(player, card1);
addCardForPlayer(player, card2);
return playersHand(player);
}
Case 2: refunding the amount the exceeding
function startGame() public payable returns (uint256) {
address player = msg.sender;
require(msg.value >= 1 ether, "not enough ether sent");
initializeDeck(player);
uint256 card1 = drawCard(player);
uint256 card2 = drawCard(player);
addCardForPlayer(player, card1);
addCardForPlayer(player, card2);
payable(player).transfer(msg.value-1 ether)
return playersHand(player);
}
In the case 2, please take care about reentrancing. Make sure that the code is in Checks Effects Interactions pattern or adding reentrancy guard.