Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 2

Solidity Variable Types

Recommended resources

Hello! Welcome back to your second lesson.

In our first session, we established the fundamental data building blocks in Solidity—the value types—and touched upon how they are stored in the EVM. We saw that writing to contract storage is an expensive operation, a crucial insight for any aspiring smart contract developer.

Introduction

Today, we will build directly on that foundation. This lesson addresses the learning outcome: Declare and differentiate between state variables, local variables, constants, and immutables.

Understanding the distinction between these variable types is not just a matter of syntax; it is fundamental to managing your contract's data, security, and, critically, its gas consumption. For every piece of data your contract needs, you must decide:

  • Does it need to persist permanently on the blockchain?
  • When is its value determined?
  • Can its value ever change?

The answers to these questions will determine which type of variable you should use, directly impacting the efficiency and cost of your contract—a topic I know is of particular interest to you.


Scopes of Operation: State vs. Local Variables

Let's begin by distinguishing between variables that define a contract's persistent state and those that are temporary.

Learn Solidity – A Handbook for Smart Contract Development

To start, let's get a clear definition of the different variable scopes. This section from the freeCodeCamp 'Learn Solidity' handbook provides an excellent, straightforward explanation.

Please read the section titled Variable Scope in Smart Contracts. It clearly defines state variables and local variables and tells you how to identify them based on where they are declared.

As the article explains:

  • State Variables are declared at the contract level (outside of any function). Their values are stored permanently on the blockchain in the contract's storage. They represent the contract's persistent state and can be thought of as the fields in a class or the columns in a database table. Modifying a state variable involves a write to storage, which is a high-gas operation (typically using the SSTORE opcode).

  • Local Variables are declared inside a function. Their scope is limited to that function's execution. They are not persisted on the blockchain and are therefore much cheaper to work with. They are typically held on the stack, a temporary data area for execution.

Here is a visual example to make this distinction crystal clear.

Solidity State and Local Variables in Remix IDE
This image shows a Solidity contract in the Remix IDE. `myStoredData` is a **state variable**, as it's declared at the contract level. `x`, `y`, and `myResult` are **local variables**, as they are declared within the `getNum` function and exist only during its execution.

Optimization with Unchanging Values: constant and immutable

What if you have a value that's integral to your contract's logic but will never change? For example, a contract might have a fixed fee, a maximum supply for a token, or the address of a related contract. It would be extremely inefficient to use a standard state variable for this, as you'd pay the gas cost to read from storage (SLOAD opcode) every single time you access it, even though the value is fixed.

Solidity provides two special keywords for this scenario: constant and immutable. These allow you to declare variables whose values, once set, can never be changed. The key difference between them lies in when their value is set.

Solidity Constant and Immutable Variables Example
This code snippet clearly demonstrates the syntax for `constant` and `immutable` variables. `MAX_VALUE` is known at compile-time, while the `owner` is set at deployment-time.

Let's break down the difference.

constant

A constant variable's value must be a literal value that is known at compile time. The compiler replaces every occurrence of the constant variable's name directly with its value in the contract's bytecode.

  • Use Case: Mathematical constants (SECONDS_IN_A_DAY = 86400;), configuration values known before deployment (MAX_TOKEN_SUPPLY = 1000000;).
  • Declaration: uint256 constant MY_CONSTANT = 100;

immutable

An immutable variable's value can be set once, either at the point of declaration or, more commonly, within the contract's constructor. The constructor is a special function that runs only once when the contract is deployed. After the contract is deployed, the immutable's value is locked in and stored in the contract's bytecode, just like a constant.

  • Use Case: The address of the contract creator (owner), the address of another contract that is required for this one to function, or a configuration parameter decided at the moment of deployment.
  • Declaration:
    address public immutable owner;
    
    constructor() {
        owner = msg.sender;
    }
    

The Gas Savings: Bypassing Storage

Now for the crucial part: why are constant and immutable so much more efficient? Because their values are not stored in contract storage. Instead, they are embedded directly into the deployed bytecode of the contract.

When you access a constant or immutable variable, the EVM performs a simple PUSH operation to place the value onto the stack, which costs a mere 3 gas. In contrast, reading a state variable from storage requires an SLOAD operation, which costs 2100 gas (in a "cold" read where the slot hasn't been accessed yet in the transaction). This is a dramatic difference.

Solidity Gas Optimization Part-1

This article provides an excellent breakdown of the gas implications. It directly compares normal state variables with constants and immutables, explaining the 'storage tax' you pay unnecessarily for fixed values.

Please read the section titled 6. Constants and immutables: Not in Storage. Focus on the explanation of how these values are stored in bytecode rather than storage and the resulting gas cost difference.

To see this in action, the following short video demonstrates the gas difference when calling a function that reads from a regular state variable versus an immutable one.

Immutable | Solidity 0.8

This video from Smart Contract Programmer offers a practical demonstration of the gas savings achieved by using immutable.

Watch the entire video. It first explains the concept, then shows a side-by-side gas comparison in Remix, and finally demonstrates the common pattern of initializing an immutable in the constructor.


Summary of Variable Types

Let's consolidate everything into a single table for easy reference.

Variable Type Scope Mutability Value Set At EVM Location Gas Cost (Read)
State Variable Contract-level Mutable Deployment or runtime Storage High (SLOAD)
Local Variable Function-level Mutable Runtime (in-function) Stack / Memory Very Low
Constant Contract-level Not mutable Compile time Bytecode Extremely Low (PUSH)
Immutable Contract-level Not mutable Deployment time Bytecode Extremely Low (PUSH)

Conclusion

In this lesson, you've learned to differentiate between Solidity's main variable types based on their scope, persistence, and mutability.

Key Takeaways:

  • State Variables persist on the blockchain in expensive storage, defining the contract's long-term state.
  • Local Variables are temporary and cheap, existing only on the stack or in memory during a function's execution.
  • Constants are compile-time values embedded in the contract's bytecode, offering extreme gas efficiency for fixed values known in advance.
  • Immutables are deployment-time values also embedded in the bytecode, perfect for values like an owner address that are set once and never change.

By consciously choosing the correct variable type, especially using constant and immutable wherever possible, you move from simply writing functional code to writing efficient and optimized code. This is a critical skill for building professional DeFi applications.

In our next lesson, we will explore function visibility specifiers (public, external, internal, private). Now that you understand how data is stored, we will learn how to control who can access and modify it through the contract's functions.