Beatland Festival

AI First Flight #4
Beginner FriendlyFoundrySolidityNFT
EXP
View results
Submission Details
Impact: low
Likelihood: low
Invalid

withdraw uses .transfer (2300 gas) and reverts for contract recipients or post-EIP gas changes, risking locked festival funds

withdraw uses transfer() and can permanently strand festival ETH in a contract target

Description

withdraw sends the contract's entire ETH balance with payable(target).transfer(...) (FestivalPass.sol:148). Solidity's transfer forwards a fixed 2300 gas stipend, which is insufficient for any non-trivial receive/fallback. If the organizer's withdrawal target is a contract (a multisig, timelock, or splitter), the transfer reverts and the accumulated pass-sale ETH cannot be withdrawn.

function withdraw(address target) external onlyOwner {
payable(target).transfer(address(this).balance); // @> 2300-gas stipend reverts on contract targets
}

Risk

Likelihood:

Low. Only the owner calls withdraw, and an EOA target works fine. But routing festival revenue to a multisig or timelock is a common and recommended operational practice, so a contract target is a realistic configuration.

Impact:

Low to Medium. No attacker is involved, but all proceeds from pass sales can become permanently inaccessible if the chosen withdrawal destination is a contract with a gas-consuming receiver. Because transfer reverts rather than failing gracefully, there is no fallback path and the ETH is effectively stuck until/unless an EOA target is used.

Proof of Concept

Withdrawing to a contract whose receive does any bookkeeping reverts on the 2300-gas limit.

contract Safe { receive() external payable { total += msg.value; } uint256 total; }
// ...
Safe safe = new Safe();
vm.prank(owner);
vm.expectRevert(); // transfer's 2300 gas is too little for the SSTORE in receive
pass.withdraw(address(safe));

Recommended Mitigation

Use a gas-flexible call and check the result.

- payable(target).transfer(address(this).balance);
+ (bool ok, ) = payable(target).call{value: address(this).balance}("");
+ require(ok, "ETH transfer failed");
Updates

Lead Judging Commences

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