Raisebox Faucet

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

Public Functions Not Used Internally

Public Functions Not Used Internally

Description

Functions that are only called externally should be declared as external instead of public to optimize gas usage and improve code clarity about their intended usage patterns.

Multiple functions are declared public but are never called internally within the contract, causing unnecessary gas overhead for internal call optimizations that will never be used and misleading developers about the functions' intended usage.

@> function getBalance(address user) public view returns (uint256) {
@> function getClaimer() public view returns (address) {
@> function getHasClaimedEth(address user) public view returns (bool) {
@> function getUserLastClaimTime(address user) public view returns (uint256) {
@> function getFaucetTotalSupply() public view returns (uint256) {
@> function getContractSepEthBalance() public view returns (uint256) {
@> function getOwner() public view returns (address) {
@> function mintFaucetTokens(address to, uint256 amount) public onlyOwner {
@> function burnFaucetTokens(uint256 amountToBurn) public onlyOwner {
@> function adjustDailyClaimLimit(uint256 by, bool increaseClaimLimit) public onlyOwner {
@> function claimFaucetTokens() public {
// All only called externally, never internally

Risk

Likelihood: High

  • Every call to these functions incurs additional gas costs for public function call setup and internal call preparation

  • Gas overhead applies to every external caller of these functions throughout contract lifetime

  • Overhead accumulates across all function calls from users and integrations

Impact: Low

  • Increased gas costs for users calling these functions, reducing user experience

  • Reduced efficiency in contract interactions affects adoption and usage

  • Misleading code that suggests internal usage possibilities when none exist

  • Suboptimal gas usage compounds over high-frequency faucet operations

Proof of Concept

// Demonstrate gas difference between public and external
contract VisibilityGasTest {
uint256 public value = 100;
// Public function (higher gas cost)
function getValuePublic() public view returns (uint256) {
return value;
}
// External function (lower gas cost)
function getValueExternal() external view returns (uint256) {
return value;
}
// Measure gas difference
function measureGasDifference() external view returns (
uint256 publicGasCost,
uint256 externalGasCost,
uint256 savingsPerCall,
uint256 annualSavings
) {
// Approximate gas costs (can vary by compiler version)
publicGasCost = 2100 + 24; // Storage read + public overhead
externalGasCost = 2100; // Storage read only
savingsPerCall = 24; // Gas saved per call
// Calculate annual savings for high-usage faucet
uint256 dailyCalls = 10000; // 10k calls per day across all functions
uint256 functionsCount = 11; // 11 public functions that should be external
annualSavings = savingsPerCall * dailyCalls * functionsCount * 365;
return (publicGasCost, externalGasCost, savingsPerCall, annualSavings);
}
// Show internal call analysis
function analyzeInternalUsage() external pure returns (
string[] memory functions,
bool[] memory calledInternally
) {
functions = new string[](5);
calledInternally = new bool[](5);
functions[0] = "getBalance(address)";
calledInternally[0] = false; // Never called internally
functions[1] = "claimFaucetTokens()";
calledInternally[1] = false; // Never called internally
functions[2] = "mintFaucetTokens(address,uint256)";
calledInternally[2] = false; // Never called internally
functions[3] = "adjustDailyClaimLimit(uint256,bool)";
calledInternally[3] = false; // Never called internally
functions[4] = "getFaucetTotalSupply()";
calledInternally[4] = false; // Never called internally
return (functions, calledInternally);
}
// Demonstrate misleading code implications
function showMisleadingImplications() external pure returns (
string memory implication1,
string memory implication2,
string memory implication3
) {
implication1 = "Public visibility suggests functions might be called internally";
implication2 = "Developers might write internal calls thinking they're allowed";
implication3 = "Code review becomes more complex checking for internal usage";
return (implication1, implication2, implication3);
}
}

Real gas impact scenario:

  1. Faucet serves 10,000 users daily

  2. Each user calls claimFaucetTokens() + 2 getter functions

  3. 30,000 total function calls daily with unnecessary public overhead

  4. 24 gas wasted per call = 720,000 gas wasted daily

  5. Annual waste: ~260 million gas

  6. At 20 gwei gas price: ~5.2 ETH wasted annually

  7. Adds unnecessary cost burden on faucet users

Recommended Mitigation

The mitigation changes function visibility from public to external for all functions that are never called internally, providing immediate gas savings and clearer code semantics.

// Change all getter functions to external
- function getBalance(address user) public view returns (uint256) {
+ function getBalance(address user) external view returns (uint256) {
return balanceOf(user);
}
- function getClaimer() public view returns (address) {
+ function getClaimer() external view returns (address) {
return faucetClaimer;
}
- function getHasClaimedEth(address user) public view returns (bool) {
+ function getHasClaimedEth(address user) external view returns (bool) {
return hasClaimedEth[user];
}
- function getUserLastClaimTime(address user) public view returns (uint256) {
+ function getUserLastClaimTime(address user) external view returns (uint256) {
return lastClaimTime[user];
}
- function getFaucetTotalSupply() public view returns (uint256) {
+ function getFaucetTotalSupply() external view returns (uint256) {
return balanceOf(address(this));
}
- function getContractSepEthBalance() public view returns (uint256) {
+ function getContractSepEthBalance() external view returns (uint256) {
return address(this).balance;
}
- function getOwner() public view returns (address) {
+ function getOwner() external view returns (address) {
return Ownable.owner();
}
// Change administrative functions to external
- function mintFaucetTokens(address to, uint256 amount) public onlyOwner {
+ function mintFaucetTokens(address to, uint256 amount) external onlyOwner {
// Function logic remains the same
}
- function burnFaucetTokens(uint256 amountToBurn) public onlyOwner {
+ function burnFaucetTokens(uint256 amountToBurn) external onlyOwner {
// Function logic remains the same
}
- function adjustDailyClaimLimit(uint256 by, bool increaseClaimLimit) public onlyOwner {
+ function adjustDailyClaimLimit(uint256 by, bool increaseClaimLimit) external onlyOwner {
// Function logic remains the same
}
// Main function should remain public if it might be called internally in future
// or change to external if definitely only called externally
- function claimFaucetTokens() public nonReentrant whenNotPaused {
+ function claimFaucetTokens() external nonReentrant whenNotPaused {
// Function logic remains the same
}
// Add documentation about visibility choices
+ /**
+ * @dev Function Visibility Guidelines:
+ * - external: Functions only called from outside the contract (most cases)
+ * - public: Functions that might be called internally (rare in this contract)
+ * - internal/private: Helper functions for contract logic
+ *
+ * Gas Optimization Impact:
+ * - external functions save ~24 gas per call vs public
+ * - Estimated annual savings: ~260M gas for high-usage faucet
+ */
Updates

Lead Judging Commences

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