Recommended resources
The freely available resources this lesson will direct you to.
Hello! Welcome back to your Solidity development course.
In our last lesson, we explored the four function visibility modifiers (public, private, external, internal), which control who can call your functions. This is a critical pillar of smart contract security.
Today, we're building on that foundation to address the next logical question: what is a function allowed to do with the contract's data? The goal of this lesson is to learn how to control function behavior and gas costs with the state mutability keywords view and pure.
As a backend developer, you're used to thinking about read/write operations on a database. view and pure are Solidity's way of explicitly declaring whether a function is a "read" operation or a pure computation, with significant consequences for gas costs and security. This is one of the most important gas optimization techniques in Solidity, and mastering it is essential for building an efficient token.
The State Mutability Spectrum
In Solidity, functions interact with the contract's state in three primary ways:
- They read and write to state. (e.g., updating a user's token balance)
- They only read from state. (e.g., checking a user's token balance)
- They neither read from nor write to state. (e.g., performing a mathematical calculation on its inputs)
Solidity gives us keywords to explicitly declare this behavior.
State-Modifying Functions (No Keyword)
If a function doesn't have a state mutability keyword, the EVM assumes it can modify the state (i.e., write data to the blockchain). These are the most common types of functions you'll write, like transfer or mint.
Crucially, any operation that changes the state of the blockchain requires a transaction and costs gas.
How do Solidity functions work?
Let's first establish the connection between writing to state and gas costs. This section from the Alchemy documentation explains it well.
Read the section titled 'Functions - Writing to Storage'. The key takeaway is that functions that change state variables, like assigning a value to z, always cost gas.
view: Read-Only Functions
A view function promises that it will not modify the state. It can read state variables, but it cannot change them.
The Solidity by Example resource offers a very direct definition and a clear code example for view functions.
Read the definition for view and look at the addToX function in the code sample. Notice how it reads the state variable x but doesn't change its value.
pure: Computation-Only Functions
A pure function makes an even stronger promise: it will neither modify nor read from the state. These functions are self-contained and behave like utility functions; their output depends only on their input parameters.
Now, look at the pure function in the same Solidity by Example resource.
Read the definition for pure and look at the add function. See how it only uses its inputs (i and j) and has no interaction with the contract's state variable x.
This flowchart provides a great visual summary of the distinction:

The "Why": Gas Costs and External Calls
So, why go to the trouble of adding these keywords? The primary reason is gas optimization.
When you call a function from outside the blockchain (e.g., from your dApp's frontend, a script, or a block explorer like Etherscan), you are making an "external call".
- Calls to state-modifying functions must be broadcast as a transaction, which gets mined into a block and costs gas (
eth_sendTransaction). - Calls to
viewandpurefunctions are simply read operations on your local Ethereum node. They don't need to be mined and are therefore completely free (eth_call).
This is a fundamental concept. It means you can let users check your token's total supply or their own balance as many times as they want without paying any gas fees.
Solidity Tutorial: Function modifier keywords (view, pure, constant)
This video clearly explains the difference in how these functions are called and why it matters for gas.
Watch from 00:30 to 01:05 to see the visual distinction in the Remix IDE, and then watch from 03:01 to 03:37 to understand the difference between eth_call (for view/pure) and eth_sendTransaction.
Important Note: If a view or pure function is called internally by another function that does modify state, it will consume gas as part of that larger transaction. The optimization still helps, as the compiler knows it doesn't need to account for state changes from that part of the code, but it is not free in this context.
Compiler Enforcement
These keywords aren't just suggestions; they are rules enforced by the Solidity compiler. If you declare a function as view and then try to modify a state variable inside it, your code will not compile.
Learn Solidity (0.5) - View and Pure Functions
This video provides a detailed walkthrough of what is and isn't allowed inside view and pure functions, showing the compiler errors you'll get if you break the rules.
Watch the section on view functions (00:16 - 03:53) and pure functions (03:53 - 06:24). Pay close attention to the examples of invalid code and the compiler errors they produce. This will build your intuition for what's allowed.
Furthermore, the compiler is smart. If you write a function that could be declared as view or pure but you forget the keyword, it will give you a warning, encouraging you to be as specific as possible for gas optimization.
Best Practice: Always apply the most restrictive mutability keyword possible.
- If a function doesn't interact with state at all, make it
pure. - If it only reads state, make it
view. - Only if it modifies state should you leave it without a mutability keyword.
Test your understanding!
You are writing a simple Counter contract. For each function below, decide if it should be view, pure, or have no keyword.
function increment() { counter++; }function get() returns (uint) { return counter; }function add(uint a, uint b) returns (uint) { return a + b; }
Show answer
- No keyword (state-modifying): The
incrementfunction modifies thecounterstate variable by incrementing it. This is a "write" operation. view: Thegetfunction reads the value of thecounterstate variable and returns it. It doesn't modify anything, soviewis appropriate.pure: Theaddfunction takes two numbers and returns their sum. It does not read from or write to thecounterstate variable. Its logic is completely self-contained, making itpure.
Practical Application
Let's apply this in your Hardhat project.
- In VS Code, create a new file in your
contractsdirectory namedMutability.sol. - Add the following code. It contains a state variable and three functions. Your task is to add the correct mutability keyword to
getValueandaddValues.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
contract Mutability {
uint public myValue = 10;
// This function MODIFIES state. It needs no keyword.
function setValue(uint _newValue) public {
myValue = _newValue;
}
// This function only READS state. What keyword should it have?
function getValue() public /* Add keyword here */ returns (uint) {
return myValue;
}
// This function NEITHER reads NOR modifies state. What keyword should it have?
function addValues(uint _a, uint _b) public /* Add keyword here */ returns (uint) {
return _a + _b;
}
}
- After adding the keywords you think are correct (
viewandpure), compile the contract withnpx hardhat compileto check your work. If it compiles without warnings related to mutability, you've done it correctly!
Conclusion
Great job! You have now learned how to declare what a function is allowed to do with a contract's state using view and pure. This concept is fundamental to writing efficient, secure, and readable Solidity code.
Key Takeaways:
- Functions that modify state require gas and are submitted as transactions.
viewfunctions promise to only read state, not modify it.purefunctions promise to neither read nor modify state.- When called externally (off-chain),
viewandpurefunctions are free to execute. - Always use the most restrictive mutability keyword possible (
pure>view> none) for gas efficiency and code clarity.
Preview of the Next Lesson:
We've covered state variables, function visibility, and now function mutability. The next piece of the puzzle is initializing our contract. In the next lesson, we will learn how to implement a constructor to initialize contract state upon deployment. The constructor is a special function that runs only once, when the contract is first created, making it the perfect place to set initial values for our state variables.