Last Man Standing

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

Redundant `owner()` Calls in `withdrawPlatformFees()` Waste Gas

Root + Impact

Root Cause: Multiple storage reads for the same owner() value -> Impact: Unnecessary SLOADs increase transaction gas cost on every withdrawal.

Description

  • The withdrawPlatformFees() function invokes the owner() view function twice—once to determine the payout recipient and again when emitting the event. Each call to owner() performs an SLOAD to read the owner’s address from storage.

  • These redundant reads add extra gas overhead (~800 gas per read) every time the function executes. Although individually small, in high-frequency withdrawal scenarios or gas-sensitive environments, these inefficiencies accumulate and increase users’ costs.

function withdrawPlatformFees() external onlyOwner nonReentrant {
uint256 amount = platformFeesBalance;
require(amount > 0, "Game: No platform fees to withdraw.");
platformFeesBalance = 0;
// ❌ First SLOAD: owner()
@> (bool success,) = payable(owner()).call{value: amount}("");
require(success, "Game: Failed to withdraw platform fees.");
// ❌ Second SLOAD: owner()
@> emit PlatformFeesWithdrawn(owner(), amount);
}

Risk

Likelihood: High

  • First owner() call: inside the .call{value: amount} to determine the recipient.

  • Second owner() call: in the emit statement to log the event.

  • Issue: Both calls retrieve the same storage slot; one local variable could capture owner() once and reuse it, saving a storage read.

Impact: Gas

  • Increased Transaction Cost: Each withdrawal pays +800 gas for the extra SLOAD.

  • Cumulative Waste: Over many withdrawals, the protocol (or users) spend unnecessary ETH.

  • Performance Degradation: In gas-sensitive contracts, eliminating even small inefficiencies matters.

Tools Used:

  • Foundry Test Suite

  • Chat-GPT AI Assistance (Report Grammar Check & Improvements)

  • Manual Review

Proof of Concept

// no poc required
uint256 public constant NOTHING_REQUIRED = TRUE;

Scenario:

Every time withdrawPlatformFees() is called, it performs two independent storage reads for owner(). By caching the owner’s address in a local variable, you eliminate one SLOAD, saving roughly 800 gas per call.

Recommended Mitigation

Cache owner() Once

function withdrawPlatformFees() external onlyOwner nonReentrant {
uint256 amount = platformFeesBalance;
require(amount > 0, "Game: No platform fees to withdraw.");
platformFeesBalance = 0;
+ address payable payee = payable(owner()); // one SLOAD
- (bool success,) = payable(owner()).call{value: amount}("");
(bool success,) = payee.call{value: amount}("");
require(success, "Game: Failed to withdraw platform fees.");
- emit PlatformFeesWithdrawn(owner(), amount);
+ emit PlatformFeesWithdrawn(payee, amount); // reuse cached value
}
Updates

Appeal created

inallhonesty Lead Judge about 1 month ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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