withdraw() forwards the contract's entire ETH balance to an owner-chosen target using transfer, which hard-codes a 2300-gas stipend. Any recipient whose receive/fallback (e.g., a multisig such as a Safe, or any treasury contract) needs more than 2300 gas will revert, so proceeds can only ever be withdrawn to an EOA. The declared FundsWithdrawn event is also never emitted, removing on-chain auditability of the protocol's primary money flow.
The function uses the deprecated .transfer and drops the success value by construction (transfer reverts on failure). Correct handling would be call{value: ...} with an explicit success check. The event defined in the interface (IFestivalPass.sol:78) is unused.
Likelihood: Medium. It manifests as soon as the owner routes proceeds to a contract treasury/multisig rather than an EOA — a normal operational choice.
Impact: If the intended target is a contract that requires more than 2300 gas, withdrawal reverts and proceeds are temporarily stuck until the owner switches to an EOA. Owner-controlled, so not a fund loss, but it breaks the expected treasury flow and lacks any event trail for accounting.
A non-payable, non-receiving contract is deployed as the withdrawal target. After a pass sale the contract holds ETH; withdraw to the sink reverts because the 2300-gas transfer cannot complete.
Use call with an explicit success check (or Address.sendValue) and emit FundsWithdrawn. Consider pulling to a configured treasury address instead of an arbitrary per-call target.
The contest is live. Earn rewards by submitting a finding.
Submissions are being reviewed by our AI judge. Results will be available in a few minutes.
View all submissionsThe contest is complete and the rewards are being distributed.