Puppy Raffle

AI First Flight #1
Beginner FriendlyFoundrySolidityNFT
EXP
View results
Submission Details
Impact: medium
Likelihood: high
Invalid

withdrawFees() bricked permanently by force-sent ETH (selfdestruct), locking all protocol fees

Root + Impact

Description

withdrawFees() pays accrued protocol fees to feeAddress, gated by a strict equality between the contract balance and totalFees.

Anyone can increase address(this).balance by force-sending ETH via selfdestruct of a funded contract (or a pre-funded create2 deployment), bypassing any receive() guard. Once balance > totalFees, the strict equality can never hold again, so withdrawFees() reverts forever and the fees are locked.

@> require(address(this).balance == uint256(totalFees), "PuppyRaffle: There are currently players active!");

Risk

Likelihood: High

  • A single 1 wei forced transfer permanently bricks withdrawals. Trivial, permissionless, irreversible.

Impact: Medium

  • All accrued protocol fees are permanently locked (frozen funds). Pure griefing — no attacker gain, but the protocol loses its fees.

Proof of Concept

contract ForceFeeder {
constructor(address payable target) payable {
selfdestruct(target); // forces ETH into PuppyRaffle, bypassing receive()
}
}
function test_bricksWithdrawFees() public {
// fees have accrued so that balance == totalFees and withdraw would normally succeed
new ForceFeeder{value: 1 wei}(payable(address(raffle)));
// now address(raffle).balance == totalFees + 1
vm.expectRevert(); // "There are currently players active!"
raffle.withdrawFees();
}

Recommended Mitigation

Do not gate withdrawal on an exact balance equality; track and send totalFees regardless of any stray balance:

- require(address(this).balance == uint256(totalFees), "PuppyRaffle: There are currently players active!");
uint256 feesToWithdraw = totalFees;
totalFees = 0;
(bool success,) = feeAddress.call{value: feesToWithdraw}("");
require(success, "PuppyRaffle: Failed to withdraw fees");
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 22 hours ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!