Beatland Festival

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

[L-6] - State Variable Should Be Immutable

State Variable Should Be Immutable + Gas Inefficiency and Intent Ambiguity

Description

  • Normal Behavior:
    In Solidity, the immutable keyword can be used for state variables that are assigned once during contract construction and never changed thereafter. Marking such variables as immutable saves gas on every read and makes the contract’s intent clearer to auditors and developers, as it guarantees the value cannot be changed after deployment.

  • Issue:
    In the FestivalPass contract, the beatToken address is set in the constructor and never changes, but it is not marked as immutable. This results in unnecessary gas costs for every read and makes it less clear to reviewers that the address is intended to be permanent.

  • Relevant Code Example:

// Current implementation
contract FestivalPass {
@> address public beatToken;
// ...
constructor(address _beatToken, address _organizer) {
beatToken = _beatToken;
organizer = _organizer;
}
}

Risk

Likelihood:

  • This will occur in every deployment and every read of the beatToken variable, as the lack of immutable is a code quality and gas optimization issue.

  • The risk is present in all contracts where constructor-assigned addresses are not marked as immutable.

Impact:

  • Gas Inefficiency: Every read of beatToken costs more gas than necessary, especially in functions that are called frequently or in loops.

  • Intent Ambiguity: Auditors and developers may not immediately realize that beatToken is meant to be permanent, increasing the risk of misunderstanding or accidental modification in future upgrades.

Proof of Concept

To observe the gas inefficiency, copy and paste the following test code into your test file (e.g., test/contract.t.sol). This test compares the gas usage of reading a regular storage variable versus an immutable variable:

contract FestivalPassImmutableTest {
address public beatToken;
address public immutable beatTokenImmutable;
constructor(address _beatToken) {
beatToken = _beatToken;
beatTokenImmutable = _beatToken;
}
function readBeatToken() public view returns (address) {
return beatToken;
}
function readBeatTokenImmutable() public view returns (address) {
return beatTokenImmutable;
}
}
function test_ImmutableGasSavings() public {
address testAddr = address(0xBEEF);
FestivalPassImmutableTest testContract = new FestivalPassImmutableTest(testAddr);
uint256 gasStart = gasleft();
testContract.readBeatToken();
uint256 gasUsedStorage = gasStart - gasleft();
gasStart = gasleft();
testContract.readBeatTokenImmutable();
uint256 gasUsedImmutable = gasStart - gasleft();
// The immutable read should use less gas
assertLt(gasUsedImmutable, gasUsedStorage, "Immutable variable should be cheaper to read");
}

Explanation:

  • This test deploys a contract with both a regular and an immutable address variable.

  • It measures the gas used to read each variable.

  • The read from the immutable variable is cheaper, demonstrating the benefit.

Recommended Mitigation

Mark the beatToken variable as immutable to save gas and clarify intent.

- address public beatToken;
+ address public immutable beatToken;

Summary:
Using immutable for constructor-assigned variables is a best practice for gas optimization and code clarity. It is a low-effort, high-impact improvement that should be applied to all relevant variables.

Updates

Lead Judging Commences

inallhonesty Lead Judge about 2 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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