Solidity immutable: Cut Read Gas for Deployment-Fixed Config
Use Solidity immutable for configuration values fixed at deployment. They live in bytecode, not storage, so every read avoids an SLOAD gas cost.
10 May 2026, 00:12 UTC

The Problem: Paying Storage Gas for Values That Never Change
Many smart contracts need configuration data that is set once at deployment and never touched again—think of a treasury address, a fee recipient, or a fixed basis-point rate. If you declare these as ordinary state variables, Solidity reserves a storage slot for each one. Every time a function reads the value, the EVM executes an SLOAD operation. On mainnet, repeated storage reads across many transactions add noticeable gas costs for users.
The Takeaway: Use immutable for Values Fixed at Deployment
The immutable keyword, available since Solidity 0.6.5, tells the compiler to embed the value directly in the contract’s bytecode instead of allocating a storage slot. The variable is assigned exactly once, inside the constructor. After deployment, the EVM reads it as a constant embedded in the code, which avoids the SLOAD cost on every call.
Storage vs. Immutable: What Changes at Runtime
A standard uint256 state variable lives in contract storage. Reading it requires looking up a storage slot by key. An immutable variable does not occupy a storage slot. The compiler replaces reads with a reference to the value baked into the deployed bytecode. This is why immutable reads are cheaper, but it also means the value cannot be updated after the contract is deployed.
Worked Example: Fixed Withdrawal Fee Vault
Imagine a vault that deducts a platform fee on every withdrawal. The fee recipient and fee rate are decided when the contract is deployed and should not change afterward.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract WithdrawalVault {
address public immutable feeRecipient;
uint16 public immutable feeBasisPoints; // e.g., 100 = 1%
uint256 private totalWithdrawn;
constructor(address _feeRecipient, uint16 _feeBasisPoints) {
feeRecipient = _feeRecipient;
feeBasisPoints = _feeBasisPoints;
}
function withdraw(address payable to, uint256 amount) external {
uint256 fee = (amount * feeBasisPoints) / 10000;
totalWithdrawn += amount;
(bool sent, ) = feeRecipient.call{value: fee}("");
require(sent, "Fee transfer failed");
to.transfer(amount - fee);
}
}
Here, feeRecipient and feeBasisPoints are read during every withdrawal but never modified. Declaring them immutable removes two storage reads from the transaction path.
Verification and Limitations
Compile the contract with Solidity 0.6.5 or newer and compare the gas reported for a read-only call against an equivalent contract using standard public storage variables. You can inspect the deployed bytecode: immutable values appear as embedded constants, while storage variables generate SLOAD opcodes in the runtime code.
One subtlety: because an immutable is not stored in a storage slot, it cannot be used as an indexed event topic. Indexed event parameters must reference storage locations or be explicitly provided as topics, so plan your event design accordingly.
Limitation: An immutable value is permanent. If your protocol might need to change the fee rate or recipient through governance, do not use immutable. Use standard storage with an access-controlled setter, or a proxy pattern, instead.
Actionable check: Before declaring a variable immutable, ask whether the value could ever need a post-deployment update. If the answer is no, and your compiler is at least 0.6.5, use immutable. If the answer is yes, keep it in storage.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.