Raisebox Faucet

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

[L-03] - Unused state variable in `claimFaucetTokens` wastes gas

Root + Impact

Description

The claimFaucetTokens function updates a state variable faucetClaimer that serves no functional purpose in the contract. This variable is set to msg.sender on every claim but is never read or used anywhere in the contract logic.

The specific issue is that updating this unnecessary state variable costs approximately 5,000 gas per transaction through an SSTORE operation, wasting user funds without providing any benefit.

// Root cause in the codebase
address public faucetClaimer; // @> Declared but never used
function claimFaucetTokens() external {
// ... claim logic ...
faucetClaimer = msg.sender; // @> Wastes ~5,000 gas per transaction
emit FaucetClaimed(msg.sender, faucetDrip);
}

Risk

Likelihood:

  • Occurs on every single token claim

  • Affects all users

  • Guaranteed to happen during normal operation

  • No conditions required

Impact:

  • Wastes ~5,000 gas per claim transaction

  • Unnecessary cost to users

  • Pollutes contract state

  • No functional benefit provided

Proof of Concept

This test demonstrates the gas waste from the unnecessary state variable:

  1. Setup: We deploy the contract with the unnecessary state variable

  2. Measurement: We observe that the variable is updated on every claim

  3. Result: ~5,000 gas wasted per transaction with no benefit

The waste occurs because:

  • SSTORE operation costs ~5,000 gas for non-zero to non-zero

  • Variable is updated on every claim

  • Variable is never read or used

  • Provides zero functional value

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {Test} from "forge-std/Test.sol";
import {RaiseBoxFaucet} from "../src/RaiseBoxFaucet.sol";
import {RaiseBoxToken} from "../src/RaiseBoxToken.sol";
contract UnnecessaryStateVariableTest is Test {
RaiseBoxFaucet faucet;
RaiseBoxToken token;
address owner = makeAddr("owner");
address user1 = makeAddr("user1");
address user2 = makeAddr("user2");
function setUp() public {
vm.startPrank(owner);
token = new RaiseBoxToken();
faucet = new RaiseBoxFaucet(address(token));
token.mintFaucetTokens(address(faucet), 1_000_000 * 10**18);
vm.deal(address(faucet), 100 ether);
vm.stopPrank();
}
function testUnnecessaryStateVariableWaste() public {
// First claim by user1
vm.prank(user1);
faucet.claimFaucetTokens();
assertEq(faucet.faucetClaimer(), user1); // Variable is set
// Wait 3 days for cooldown
vm.warp(block.timestamp + 3 days);
// Second claim by user2
vm.prank(user2);
faucet.claimFaucetTokens();
assertEq(faucet.faucetClaimer(), user2); // Variable is updated
// The variable is updated but never used
// This wastes ~5,000 gas per transaction
// Demonstrate the variable serves no purpose:
// 1. It's not used in any require statements
// 2. It's not used in any calculations
// 3. It's not used in any events
// 4. It's only set, never read
// The only "use" is that it's publicly readable,
// but this provides no value to the contract logic
}
function testStateVariableNeverUsed() public {
// Claim multiple times
vm.prank(user1);
faucet.claimFaucetTokens();
vm.warp(block.timestamp + 3 days);
vm.prank(user2);
faucet.claimFaucetTokens();
// The variable is updated each time
assertEq(faucet.faucetClaimer(), user2);
// But it's never used in any contract logic
// Search the entire contract - no reads of this variable
// Only writes, which waste gas
}
}

Recommended Mitigation

Remove the unnecessary state variable:

- address public faucetClaimer;
function claimFaucetTokens() external {
// ... claim logic ...
- faucetClaimer = msg.sender;
emit FaucetClaimed(msg.sender, faucetDrip);
}

If tracking the last claimer is needed for some reason, document why and use it:

// If you need to track last claimer for a specific reason:
address public lastClaimer; // Tracks last user who claimed for [specific purpose]
function claimFaucetTokens() external {
// ... claim logic ...
lastClaimer = msg.sender;
emit FaucetClaimed(msg.sender, faucetDrip);
}
// Then actually USE it somewhere:
function someFunction() external {
require(msg.sender != lastClaimer, "Last claimer cannot do this");
// ... logic ...
}

However, since the current contract has no use for this variable, it should simply be removed to save gas.

Updates

Lead Judging Commences

inallhonesty Lead Judge 4 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.