AirDropper

AI First Flight #5
Beginner FriendlyDeFiFoundry
EXP
View results
Submission Details
Impact: low
Likelihood: low
Invalid

claimFees() bricks permanently if owner ever becomes a contract that cannot receive ETH

Root + Impact

Description

  • Normal behavior: the owner should always be able to withdraw accumulated claim fees (ETH) via claimFees().

  • The issue: claimFees() pushes the full ETH balance via a low-level call and reverts the whole transaction (TransferFailed) if the recipient cannot receive. If ownership is ever transferred to a contract without receive()/fallback(), every claimFees call reverts and accumulated fees are stuck — there is no pull-based fallback:

    @> function claimFees() external onlyOwner {
    (bool success,) = owner().call{value: address(this).balance}("");
    if (!success) { revert MerkleAirdrop__TransferFailed(); }
    }

Risk

  • Reason 1 // claimFees() uses a push pattern to owner() with no fallback path.

  • Reason 2 // Ownership can be transferred to arbitrary addresses, including contracts that cannot receive ETH.

  • Impact 1 // Fee revenue locked permanently; likelihood is low (owner starts as the deployer EOA), hence Low.

  • Impact 2 // No impact on the airdrop distribution itself.

Proof of Concept

PoC verified by a passing Foundry test (test_PoC_L2_ClaimFeesBrickedWhenOwnerCannotReceiveETH): a contract deployed by a receiver-less contract becomes owner; claimFees reverts TransferFailed; fees stay locked.

Recommended Mitigation

Add an owner-gated escape hatch (e.g. sweepFeesTo(address)) or restrict ownership to EOAs / document the constraint.

Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 3 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!