Raisebox Faucet

First Flight #50
Beginner FriendlySolidity
100 EXP
View results
Submission Details
Impact: medium
Likelihood: low
Invalid

Missing “rescue funds” mechanism can permanently lock tokens accidentally sent to the faucet (e.g., USDT)

Root + Impact

Description

The faucet has no way to recover arbitrary ERC-20 tokens (or ETH) that are accidentally sent to the contract address. Users (or integrations) may mistakenly transfer tokens like USDT directly to the faucet. Since the contract exposes no withdrawal function for unknown assets, these tokens become permanently stuck, reducing user funds and creating operational overhead.

There is no rescue/recover function for non-faucet assets:
- No method to transfer out arbitrary ERC-20s.

Risk

Likelihood:

  • Faucet addresses are public; users could mis-send tokens to well-known contracts.


Impact:

  • Any ERC-20 mistakenly sent (e.g., USDT) is locked forever.

  • Team cannot assist users by returning funds. Reputational damage and support burden.

Proof of Concept


Conceptual POC:

1. User calls USDT.transfer(faucet, 100e6).
2. Funds appear in the faucet’s USDT balance.
3. There is no function to move USDT outfunds stuck indefinitely.

Recommended Mitigation


Add a rescue function restricted to the owner, using SafeERC20 to support non-standard tokens (e.g., USDT).

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
contract RaiseBoxFaucet {
using SafeERC20 for IERC20;
address public immutable FAUCET_TOKEN; // address of the faucet’s own ERC20
event ERC20Rescued(address indexed token, address indexed to, uint256 amount);
event ETHRescued(address indexed to, uint256 amount);
// ... existing code ...
/// @notice Rescue arbitrary ERC20 tokens accidentally sent to this contract.
/// @dev Does NOT allow rescuing the faucet’s own token to avoid draining claim liquidity.
function rescueERC20(address token, address to, uint256 amount) external onlyOwner {
require(to != address(0), "rescue: zero to");
require(token != FAUCET_TOKEN, "rescue: faucet token not allowed");
IERC20(token).safeTransfer(to, amount);
emit ERC20Rescued(token, to, amount);
}
/// @notice Rescue stray ETH (not part of faucet accounting).
function rescueETH(address to, uint256 amount) external onlyOwner {
require(to != address(0), "rescue: zero to");
(bool ok, ) = to.call{value: amount}("");
require(ok, "rescue: eth transfer failed");
emit ETHRescued(to, amount);
}
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 6 days ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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