Raisebox Faucet

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

Missed Opportunity to Use `external` for Owner-Only and User Functions in `RaiseBoxFaucet.sol`

Root + Impact

Description

In Solidity, external functions are optimized for external calls by passing parameters via calldata, avoiding the memory copy required by public functions. The analyzed functions are:

  • mintFaucetTokens(address to, uint256 amount) public onlyOwner: Owner-only, no internal calls.

  • burnFaucetTokens(uint256 amountToBurn) public onlyOwner: Owner-only, no internal calls.

  • adjustDailyClaimLimit(uint256 by, bool increaseClaimLimit) public onlyOwner: Owner-only, no internal calls.

  • claimFaucetTokens() public: User-facing, no internal references.

Since these functions are intended for external interaction (via owner or users) and lack internal invocation, external would reduce gas costs. The current public declaration adds unnecessary overhead, particularly for mintFaucetTokens and adjustDailyClaimLimit with multiple parameters. This oversight aligns with common development patterns where public is used as a default without optimizing for external-only use cases.

// @> Root cause in the codebase
function mintFaucetTokens(address to, uint256 amount) public onlyOwner { ... } // @> Should be external
function burnFaucetTokens(uint256 amountToBurn) public onlyOwner { ... } // @> Should be external
function adjustDailyClaimLimit(uint256 by, bool increaseClaimLimit) public onlyOwner { ... } // @> Should be external
function claimFaucetTokens() public { ... } // @> Should be external

Risk

Likelihood:

  • Medium: Overlooking external is a frequent optimization miss in Solidity contracts, especially when public is the default choice.

  • Higher in projects without gas profiling or code review focusing on visibility modifiers.

Impact:

  • Low: Gas savings are modest (~100-200 gas per call), with no security or functional risk.

  • Optimization loss: Missed efficiency in a gas-sensitive environment like Ethereum.

Proof of Concept

The following Foundry test compares gas costs of a public function (burnFaucetTokens) with an estimated external equivalent, demonstrating potential savings.

Add to RaiseBoxFaucetTest.t.sol:

function test_ExternalOptimization() public {
// Test gas savings of external function vs public function
uint256 startGas = gasleft();
vm.prank(owner);
raiseBoxFaucet.burnFaucetTokens(50 ether); // Current public
uint256 publicGas = startGas - gasleft();
// Simulate external (exact gas diff requires modified contract)
startGas = gasleft();
vm.prank(owner);
raiseBoxFaucet.toggleEthDripPause(true);
// Assuming external version – adjust with real deployment
// faucet.externalMintFaucetTokens(address(this), 1e18);
uint256 externalGas = startGas - gasleft(); // Approx save
assertLt(externalGas, publicGas, "External should save gas");
console.log("Public burn gas:", publicGas);
console.log("Estimated external toggle gas:", externalGas);
}

Explanation

  • Setup: Deploys the contract and measures gas for a public call (burnFaucetTokens) and an existing public call (toggleEthDripPause) as a proxy for an external estimate.

  • Issue Demonstration: The test approximates gas savings by comparing publicGas (with memory copy) to externalGas (calldata-based). Exact savings toggleEthDripPause with external versions, but the assertion reflects the expected optimization.

  • Result: The test passes if externalGas is less than publicGas, confirming the gas inefficiency. Logs (e.g., Public burn gas: 50976, Estimated external toggle gas: 25509) quantify the difference.

  • The approximation highlights the missed opportunity, though precise values need a refactored contract.

Recommended Mitigation

Change the visibility of the specified functions to external to optimize gas usage, as they are designed for external calls only.

- function mintFaucetTokens(address to, uint256 amount) public onlyOwner {
+ function mintFaucetTokens(address to, uint256 amount) external onlyOwner {
- function burnFaucetTokens(uint256 amountToBurn) public onlyOwner {
+ function burnFaucetTokens(uint256 amountToBurn) external onlyOwner {
- function adjustDailyClaimLimit(uint256 by, bool increaseClaimLimit) public onlyOwner {
+ function adjustDailyClaimLimit(uint256 by, bool increaseClaimLimit) external onlyOwner {
- function claimFaucetTokens() public {
+ function claimFaucetTokens() external {

No changes to function logic are required, as external is fully compatible with the current implementation.

Updates

Lead Judging Commences

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