The RockPaperScissors.sol
triggers minting of the Winning Token on token games that are cancelled, won or end in a tie instead of transferring tokens out of the contract resulting in an accumulation of winning tokens within RockPaperScissors
that are locked.
The RockPaperScissors.sol
allows users to play the game using either Ether or the Winning Token. The Winning Token is only obtainable after winning a game.
Note: token games = games created with createGameWithToken
The Code Issue
When Winning Tokens are used to play, each player must have 1 token, which is transferred into the game. If a game ends by resolution of:
cancelled (joining period expired)
finished (a winner is found)
draw (no winner)
The RockPaperScissors.sol
mints players Winning tokens instead of transferring the existing tokens out of the contract. The number of Winning Token minted is dependent on the game resolution.
Tokens Minted according to Game resolution
For token games that are cancelled by creator due to no player joining within the alloted join time, then 1 token is minted in RockPaperScissors::_cancelGame#564
to the creator (PlayerA)...and the Winning Token transferred is left in the contract.
For token games that end in a winner, the Winner is minted 2 tokens. The code area is RockPaperScissors::_finishGame#496
. The 2 tokens used by PlayerA and PlayerB are left in the contract.
For token games that end in a tie, the players are minted 1 token each. The tokens used to join, remain in the contract. The affected area is RockPaperScissors::_handleTie#534
Comparison: Correct Logic
For games that are created with Eth, the expected and correct logic applied, is to transfer assets/funds out of the contract to the respective recipient.
Ether Game that ends in cancelled (RockPaperScissors::_cancelGame#553
) game creator is refunded
Ether Game that ends in tie (RockPaperScissors::_handleTie#517
) both players are refunded minus the fee.
Ether Games that end with a winner (RockPaperScissors::_finishGame#480
) the winner gets both bets minus the fee.
The impact of this bug is
Locked Winning tokens: As more people participate in the game, there will be an accumulation of locked Winning Token in RockPaperScissors.sol
which no one can use. This may affect the value of the Winning Token overtime.
Dilute Value: As more tokens are minted this may dilute the value of the Winning Token as there is a high total supply.
Reputation damage: User trust in the protocol is lowered due to the view in failed development as the contract logic that is accidentally locking tokens.
Unpredictable total supply: As the tokens locked in the contract cannot be circulated, they cannot effectively be included in the total supply therefore making total supply calculations difficult.
This bug can be classed as Medium severity due to no funds being directly lost but as the vulnerability will be triggered on every game created using the Winning Token, it has a high chance of happening. This issue is logical and econmic and cannot be directly exploited.
Manual Review
Foundry
The below Proof-of-concept contains 3 test functions
Description
There are 3 players - Player1, Player2, Player3 - that play different games inorder to obtain the Winning Token.
Player2 and Player3 obtain the Winning token after winning games against Player1
Player2 and Player3 are pitted together in token games to demonstrate
Tokens are locked after winning [Test function: testTokensLockedInGameViaWin
]
tokens are locked after a tie. [Test function: testTokensLockedInGameViaTie
]
In testTokensLockedInGameViaCancelling
, player2 creates and cancels games (after the join timeout) 100 times. After 100 games, the RockPaperScissors
contract has 100 locked winning tokens
All 3 test functions can be run using: forge test --mt testTokensLockedInGame -vvv
Note:
There are additional internal functions to simulate playerMoves and allow code to be reused across test functions.
console.log is used to provide information on the results
Code
To mitigate this vulnerability, the contract should
use safeTransferFrom
to move the tokens out of the RockPaperScissors
contract instead of using mint()
.
Alternatively, have a system to burn tokens in the contract, either using burn
or burnFrom
.
Mints new tokens upon game completion or cancellation for token-based games
Mints new tokens upon game completion or cancellation for token-based games
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.