.transfer() can cause permanent fund lock if withdrawal target is a smart contract walletDescription
The withdraw() function uses the deprecated .transfer() method to send ETH to the target address:
The .transfer() method has a hardcoded gas stipend of 2,300 gas, which is insufficient for:
Multi-signature wallets (Gnosis Safe, etc.)
Smart contract wallets with custom logic in their receive() or fallback() functions
Any contract with fallback/receive functions that write to storage
Proxy contracts that forward calls
Contracts that emit events on ETH receipt
If the owner attempts to withdraw to a smart contract wallet (increasingly common for security best practices), the withdrawal will always fail, causing permanent fund lock as there is no alternative withdrawal mechanism.
Impact
MEDIUM-HIGH - Can result in permanent loss of all festival revenue:
Permanent Fund Lock: If the withdrawal target is a smart contract wallet requiring >2,300 gas, all ETH from pass sales becomes permanently stuck in the contract
No Recovery Mechanism: The contract has no alternative withdrawal method or emergency function
Common in Production: Many security-conscious projects use multi-sig wallets (Gnosis Safe, multi-owner wallets) for fund management
Future-Proofing Risk: EVM gas cost changes (like EIP-1884 in 2019) can break .transfer() even for previously working contracts
Lost Revenue: Festival organizers lose all ticket sales revenue with no way to recover funds
Real-world scenarios:
Owner deploys with intention to withdraw to a Gnosis Safe (security best practice)
Festival sells thousands of passes, collecting substantial ETH (e.g., 100 ETH)
Owner calls withdraw(gnosisSafeAddress)
Transaction fails due to out-of-gas
Funds permanently locked with no recovery possible
Proof of Concept
Gas Analysis:
Historical Context:
EIP-1884 (Istanbul hard fork, 2019) increased the gas cost of the SLOAD opcode from 200 to 800 gas, breaking many contracts that relied on .transfer() and .send(). This demonstrates that even if a contract works today, future EVM changes can break it.
Recommended Mitigation
Replace .transfer() with the modern .call() pattern with proper error handling:
Why this is better:
Forwards all available gas instead of just 2,300
Compatible with smart contract wallets (Gnosis Safe, Argent, etc.)
Compatible with multi-signature wallets
Future-proof against EVM gas cost changes
Still safe due to onlyOwner modifier (no reentrancy risk since owner is trusted)
Follows current Solidity best practices (post-2019)
Additional Improvements:
Add event emission for transparency and off-chain tracking:
Note on Reentrancy:
Since this function is onlyOwner, reentrancy is not a concern here. The owner is trusted and would not deploy a malicious contract to attack their own funds. However, if paranoid, you could add a nonReentrant modifier from OpenZeppelin's ReentrancyGuard.
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.