The selectWinner function relies on the concatenation of block.timestamp, block.difficulty, and msg.sender to generate a random number for purposes of selecting a winner. These variables are predictable and manipulable, so there is no true randomness. Also, anyone can call selectWinner which means they are in control of these variables, so they can wait until a block.timestamp and block.difficulty that is favorable to them winning the drawing (together with their address, which they know) and then call selectWinner. This could also be used to break the intended rarity distribution.
Players can wait until a block.timestamp and block.difficulty where they will win the drawing (ideally one where they will also get a legendary NFT). Then they can call selectWinner. Players can try to predict in advance whether they will win and request a refund of their entry if they won't win and enter the next drawing instead. You don't have a truly random drawing if you use numbers that can be known in advance.
Use Chainlink's VRF contracts to generate a verifiably random number and then use the mod of players.length to pick a winner with true randomness.
Root cause: bad RNG Impact: manipulate winner
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.