Choosing Immutable Variables in Vyper: When to Use, Trade‑offs, and How to Validate
Immutable variables in Vyper let you set a value once at deployment, reduce storage gas, and enable compiler inlining. This guide explains when to use them, their trade‑offs, and a step‑by‑step validation workflow.
24 Dec 2025, 11:11 UTC

Why Consider Immutables?
In Vyper, an immutable variable is set once during contract deployment and never changes. This guarantees that the value is fixed, reduces the number of storage slots, and allows the compiler to inline the value, cutting runtime gas.
Decision Matrix
| Option | Supported Types | Gas at Deployment | Gas at Runtime | Use‑case Fit |
|---|---|---|---|---|
| Regular state variable | All Vyper types | High – storage write | High – storage read/write | Values that change after deployment |
| Immutable variable | int128, address, bytes32, bool | Medium – storage write in constructor | Low – inlined at compile time | Constants that must be known at deployment but never change |
| Constant (compile‑time) | All literal types | None – baked into bytecode | None – directly inlined | Hard‑coded values that do not need constructor input |
When Immutables Are the Right Choice
- Fixed configuration – e.g., the owner address, a pre‑determined fee, or a token symbol.
- Gas‑critical paths – any function that reads the value frequently benefits from inlining.
- Security guarantees – once set, the value cannot be altered by a malicious actor.
Limitations to Keep in Mind
- Only
int128,address,bytes32, andboolare allowed. - Dynamic arrays, mappings, and structs cannot be immutable.
- Providing a large value via the constructor can inflate deployment gas; if the value is huge, consider a constant or external storage.
- Misusing immutables for data that needs to change leads to irreversible contract behavior.
Concrete Implementation
Below is a minimal contract that stores an immutable owner address and exposes it via a view function.
# @version ^0.3.9
owner: immutable(address)
@external
def __init__(_owner: address):
# The constructor assigns the immutable once.
owner = _owner
@view
@external
def get_owner() -> address:
return owner
Compile and Inspect
- Run
vyper --versionand confirm you are on 0.3.9 or newer. - Compile with
vyper contract.vy. The output ABI will containget_owneronly; the immutable is not exposed as a storage slot. - Use
vyper --abi contract.vyto view the ABI and verify that noownervariable is listed. - Check the bytecode with
vyper --bytecode contract.vyand look for the address value in the data section – it should be inlined.
Deploy and Verify Runtime Value
- Deploy the contract via a script or Remix, passing the desired owner address as the constructor argument.
- Call
get_owner()and confirm it matches the address you supplied. - Deploy a second instance with a different address and repeat the call; each instance will have its own immutable value.
Gas Comparison
Using a tool like eth-gas-reporter or ganache-cli, you can compare the gas used by a function that reads a regular state variable versus one that reads an immutable. Typically, the immutable path saves 30–50% of the gas spent on storage reads.
Practical Checklist Before Adding an Immutable
- Is the value known at deployment time?
- Does it need to change later?
- Is the type one of the supported ones?
- Will the deployment gas cost be acceptable?
- Can the value be inlined as a constant instead?
Rollback Considerations
Because immutable variables cannot be altered after deployment, there is no rollback path. If you anticipate the need to change the value, use a regular state variable or a proxy pattern instead.
Final Takeaway
Use immutable in Vyper when you need a value that is set once, is cheap to read, and must never change. It’s a powerful tool for reducing gas and hardening contracts, but its type restrictions and lack of mutability mean it’s not a silver bullet for all configuration data.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.