User still can refund the raffle fee even if the raffles has ended.
Description
Normally user can't get refund if the Raffle has ended. In this case, user still can refund the raffle fee even if the raffles has ended, if the raffle players is less than 4. This is because missing check on Raffle timing on refund()
function refund(uint256 playerIndex) public {
address playerAddress = players[playerIndex];
require(playerAddress == msg.sender, "PuppyRaffle: Only the player can refund");
require(playerAddress != address(0), "PuppyRaffle: Player already refunded, or is not active");
payable(msg.sender).sendValue(entranceFee);
players[playerIndex] = address(0);
emit RaffleRefunded(playerAddress);
}
Risk
Likelihood:
Event will occured when the Raffle has ended but the players is less than 4
Impact:
User able to refund the fee where it supposed to be accrued to protocol
Proof of Concept
Add this function to existing test suite
Run with forge test --match-test testCanGetRefundAfterEnd -vvvv
function testCanGetRefundAfterEnd() public playersEntered {
uint256 balanceBefore = address(playerOne).balance;
uint256 indexOfPlayer = puppyRaffle.getActivePlayerIndex(playerOne);
vm.warp(block.timestamp + duration + 1);
vm.roll(block.number + 1);
vm.prank(playerOne);
puppyRaffle.refund(indexOfPlayer);
assertEq(address(playerOne).balance, balanceBefore + entranceFee);
}
Recommended Mitigation
Add Raffle timing check on refund()
function refund(uint256 playerIndex) public {
address playerAddress = players[playerIndex];
require(playerAddress == msg.sender, "PuppyRaffle: Only the player can refund");
require(playerAddress != address(0), "PuppyRaffle: Player already refunded, or is not active");
+ require(block.timestamp < raffleStartTime + raffleDuration, "PuppyRaffle: Raffle is already over");
payable(msg.sender).sendValue(entranceFee);
players[playerIndex] = address(0);
emit RaffleRefunded(playerAddress);
}