View and Pure Functions in Solidity
Solidity gives you two function modifiers, view and pure, that let you declare upfront whether a function reads or modifies blockchain state. The compiler enforces these declarations, which means bugs get caught early and other developers can understand your code at a glance.
This post explains what each modifier does, what counts as reading or modifying state, and when to use each one.
What "State" Means in a Smart Contract
State is any data stored permanently on the blockchain. This includes:
- Storage variables declared at the contract level
- Account balances (including
address(this).balance) - Data in other contracts
- Block and transaction context (
block.timestamp,msg.sender, etc.)
Reading or writing state costs gas. Functions that interact with state need to be part of a transaction, and transactions cost gas. Functions that do not touch state can sometimes be executed off-chain for free.
View Functions
A view function promises it will read state but never modify it. The compiler enforces this. If your function tries to write to a storage variable or emit an event, it will not compile as view.
contract Counter {
uint256 public count = 0;
// Reads state, returns it unchanged
function getCount() public view returns (uint256) {
return count;
}
// Reads state and does a calculation
function getDoubleCount() public view returns (uint256) {
return count * 2;
}
}
When you call a view function directly from outside the blockchain (using eth_call), it costs no gas. The node executes it locally and returns the result. However, if a regular state-changing function calls a view function internally during a transaction, that internal call still consumes gas as part of the overall transaction cost.
Pure Functions
A pure function is more restricted than view. It cannot read state and cannot modify state. It only works with its input parameters.
contract MathHelper {
// No state read, no state write
function add(uint256 a, uint256 b) public pure returns (uint256) {
return a + b;
}
function multiply(uint256 x, uint256 y) public pure returns (uint256) {
return x * y;
}
}
pure functions are useful for utility logic: hashing, encoding, arithmetic, or any computation that does not depend on contract data. Because they have no side effects and no dependencies on storage, they behave like mathematical functions. Same input always produces same output.
What Breaks Each Promise
These actions will cause the compiler to reject a view or pure declaration.
Breaks view (modifies state):
- Writing to a storage variable
- Emitting an event
- Creating another contract
- Using
selfdestruct - Sending Ether
- Calling a function that is not
vieworpure - Using low-level calls (
call,delegatecall, etc.) - Writing state via inline assembly
Breaks pure (reads state):
- Reading a storage variable
- Accessing
address(this).balanceor any other address balance - Accessing
block,tx, ormsgproperties (exceptmsg.sigandmsg.data) - Calling a function that is not
pure - Reading state via inline assembly
msg.sigandmsg.dataare allowed insidepurefunctions. They describe the call itself, not the blockchain state.
All Three Types Together
Here is a single contract showing a state-modifying function, a view function, and a pure function side by side:
contract Example {
uint256 public storedNumber = 42;
// Regular function: modifies state
function setNumber(uint256 newNumber) public {
storedNumber = newNumber;
}
// View function: reads state, no modification
function getNumber() public view returns (uint256) {
return storedNumber;
}
// View function: reads state and transforms it
function getNumberPlusTen() public view returns (uint256) {
return storedNumber + 10;
}
// Pure function: no state involved
function add(uint256 a, uint256 b) public pure returns (uint256) {
return a + b;
}
}Why This Matters
Gas savings. External calls to view and pure functions execute off-chain for free when called directly. If your frontend or another service only needs to read data, declaring the function as view lets callers skip the transaction entirely.
Compiler enforcement. The compiler will reject a view function that tries to write state, and reject a pure function that tries to read it. This catches an entire class of accidental side effects before deployment.
Readability. A developer reading your code does not need to trace through the function body to understand whether it modifies anything. The keyword says it upfront.
When to Use Each
Use a regular function when you need to write to storage, emit events, or change contract state in any way.
Use view when you need to read storage variables, balances, or block context without changing anything.
Use pure when your function only needs its input parameters, nothing from storage or the environment.
If you are unsure which to use, start with pure. If the compiler rejects it because you are reading state, upgrade to view. If the compiler rejects that because you are writing state, remove the modifier entirely.