Weather Witness

First Flight #40
Beginner FriendlyFoundrySolidityNFT
100 EXP
View results
Submission Details
Severity: high
Valid

Missing ETH Withdrawal Functionality in WeatherNFT Contract Leads to Fund Locking Risk

Summary

The WeatherNFT contract receives ETH payments for NFT minting but lacks any withdrawal functionality. This design flaw could lead to permanent locking of ETH funds in the contract with no recovery mechanism.

Root Cause

The contract implements payable functions that accept ETH (through requestMintWeatherNFT with msg.value) but provides no method to withdraw accumulated ETH. This violates the basic principle that any contract receiving funds should have a controlled withdrawal mechanism.

Internal pre-conditions

  1. Contract accepts ETH payments through requestMintWeatherNFT function

  2. No withdrawal function implemented despite inheriting from ConfirmedOwner

  3. No emergency recovery mechanism for stuck funds

  4. ETH balance tracking is implicit rather than explicit

External pre-conditions

  1. Users send ETH to contract for NFT minting

  2. Contract owner has no way to access accumulated funds

  3. No fallback or recovery mechanism exists

Attack Path

  1. Users mint NFTs by sending ETH to contract

  2. ETH accumulates in contract balance

  3. Contract owner cannot access funds for:

    • Project development

    • Revenue sharing

    • Operational expenses

  4. Funds remain permanently locked in contract

Impact

  1. Permanent loss of project funds

  2. Inability to utilize revenue for project maintenance

  3. Violation of financial transparency expectations

  4. Potential legal/compliance issues regarding unaccessible funds

  5. Loss of trust from investors and users

PoC

  1. Deploy WeatherNFT contract

  2. Mint multiple NFTs by sending ETH

  3. Verify ETH balance grows in contract

  4. Attempt to withdraw ETH as owner (will fail)

  5. Confirm funds are permanently locked

Mitigation

  1. Implement owner-controlled withdrawal function:

function withdrawETH(address payable recipient) external onlyOwner {
uint256 balance = address(this).balance;
require(balance > 0, "No ETH balance");
(bool success, ) = recipient.call{value: balance}("");
require(success, "Transfer failed");
}
  1. Add withdrawal event logging:

event ETHWithdrawn(address indexed recipient, uint256 amount);
  1. Consider implementing:

    • Withdrawal limits

    • Multi-signature requirements

    • Timelock for large withdrawals

    • Emergency pause functionality

  2. Follow best practices:

    • Explicit ETH balance tracking

    • Regular fund sweeping

    • Transparent withdrawal policies

Updates

Appeal created

bube Lead Judge 4 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Lack of `withdraw` function

The contract collects funds for minting a WeatherNFT, but there is no function that allows the owner to withdraw these funds.

Support

FAQs

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