State Variables Should Be Immutable/Constant
Description
State variables that are only set during contract deployment should be declared as immutable to reduce gas costs and improve code clarity about their unchangeable nature.
Variables faucetDrip, sepEthAmountToDrip, dailySepEthCap, and blockTime are only set in the constructor or are constant values, but are not marked as immutable or constant, causing unnecessary storage costs for every read operation throughout the contract's lifetime.
@> uint256 public faucetDrip;
@> uint256 public sepEthAmountToDrip;
@> uint256 public dailySepEthCap;
@> uint256 public blockTime = block.timestamp;
Risk
Likelihood: High
-
Every storage read operation (SLOAD) costs additional gas for these variables in every function call
-
Gas costs accumulate across all function calls that access these variables throughout contract lifetime
-
Storage slots are unnecessarily allocated and maintained in contract state
Impact: Low
-
Significantly increased gas costs for contract deployment and every function call that reads these values
-
Reduced gas efficiency affects user experience and increases operational costs
-
Code clarity is reduced regarding which values are truly constant versus changeable
-
Higher cumulative operational costs for frequent faucet usage over time
Proof of Concept
contract GasImpactAnalysis {
uint256 public faucetDrip;
uint256 public sepEthAmountToDrip;
uint256 public dailySepEthCap;
uint256 public blockTime = block.timestamp;
uint256 public immutable faucetDripImmutable;
uint256 public immutable sepEthAmountToDripImmutable;
uint256 public immutable dailySepEthCapImmutable;
uint256 public immutable deploymentTime;
constructor() {
faucetDrip = 1000 * 10**18;
sepEthAmountToDrip = 0.005 ether;
dailySepEthCap = 1 ether;
faucetDripImmutable = 1000 * 10**18;
sepEthAmountToDripImmutable = 0.005 ether;
dailySepEthCapImmutable = 1 ether;
deploymentTime = block.timestamp;
}
function compareGasCosts() external view returns (
uint256 storageReadCost,
uint256 immutableReadCost,
uint256 savingsPerRead,
uint256 projectedSavings
) {
storageReadCost = 2100;
immutableReadCost = 3;
savingsPerRead = storageReadCost - immutableReadCost;
projectedSavings = savingsPerRead * 4 * 10000;
return (storageReadCost, immutableReadCost, savingsPerRead, projectedSavings);
}
function calculateLifetimeSavings() external pure returns (
uint256 dailyUsers,
uint256 yearlyGasSavings,
uint256 etherSavingsAt50Gwei
) {
dailyUsers = 1000;
uint256 dailySavings = dailyUsers * 4 * 2097;
yearlyGasSavings = dailySavings * 365;
etherSavingsAt50Gwei = (yearlyGasSavings * 50e9) / 1e18;
return (dailyUsers, yearlyGasSavings, etherSavingsAt50Gwei);
}
function showStorageWaste() external pure returns (
uint256 wastedSlots,
uint256 slotsStorageCost,
string memory optimization
) {
wastedSlots = 4;
slotsStorageCost = wastedSlots * 20000;
optimization = "Move to immutable to eliminate storage costs";
return (wastedSlots, slotsStorageCost, optimization);
}
}
Real gas impact calculation:
Current: 4 variables * 2,100 gas per read = 8,400 gas per claim
Optimized: 4 variables * 3 gas per read = 12 gas per claim
Savings: 8,388 gas per claim
With 1000 daily users: 8,388,000 gas saved per day
Annual savings: ~3 billion gas (worth ~$150-300 at typical gas prices)
Recommended Mitigation
The mitigation converts constructor-set variables to immutable and constant values to constant, dramatically reducing gas costs while improving code clarity about which values can never change.
// Convert constructor-set variables to immutable
- uint256 public faucetDrip;
+ uint256 public immutable faucetDrip;
- uint256 public sepEthAmountToDrip;
+ uint256 public immutable sepEthAmountToDrip;
- uint256 public dailySepEthCap;
+ uint256 public immutable dailySepEthCap;
- uint256 public blockTime = block.timestamp;
+ uint256 public immutable deploymentTime;
// Keep truly constant values as constants
uint256 public constant CLAIM_COOLDOWN = 3 days;
uint256 public constant INITIAL_SUPPLY = 1e27;
+ uint256 public constant DECIMALS = 18;
constructor(
string memory name_,
string memory symbol_,
uint256 faucetDrip_,
uint256 sepEthDrip_,
uint256 dailySepEthCap_
) ERC20(name_, symbol_) Ownable(msg.sender) {
+ // Set immutable values in constructor
faucetDrip = faucetDrip_;
sepEthAmountToDrip = sepEthDrip_;
dailySepEthCap = dailySepEthCap_;
+ deploymentTime = block.timestamp;
_mint(address(this), INITIAL_SUPPLY);
}
// Update functions to use immutable values (no code changes needed - compiler handles)
// Gas savings are automatic when reading these values
// Add getter functions for consistency (optional - immutable variables are already public)
+ function getDeploymentTime() external view returns (uint256) {
+ return deploymentTime;
+ }
+
+ function getFaucetParameters() external view returns (
+ uint256 drip,
+ uint256 ethAmount,
+ uint256 dailyEthCap,
+ uint256 deployed
+ ) {
+ return (faucetDrip, sepEthAmountToDrip, dailySepEthCap, deploymentTime);
+ }
// Document the optimization in comments
+ /**
+ * @dev Gas Optimization Notes:
+ * - faucetDrip, sepEthAmountToDrip, dailySepEthCap: immutable (set in constructor)
+ * - deploymentTime: immutable (deployment timestamp)
+ * - CLAIM_COOLDOWN, INITIAL_SUPPLY: constant (compile-time values)
+ * - Total gas savings: ~8,388 gas per claim transaction
+ */