State Variables, Constants & Immutables
Declared at contract level; stored permanently in storage.
12.1 State variables
Declared at contract level; stored permanently in storage.
everywhere; compiler generates an external getter internal (default) this contract and derived contracts this contract only
State variables cannot be external. Nothing on-chain is private — anyone can read any slot with eth_getStorageAt / cast storage.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract StateVars {
uint256 public count; // public → getter count()
uint256 internal secretish; // NOT secret: all storage is publicly readable
address private admin; // private only restricts other contracts' access
mapping(address => uint256) public balances;
uint256[] public history; // getter history(uint256 index)
}12.2 Constants and immutables
regular state var at declaration (compile-time expression) in constructor (or declaration) inlined in bytecode in runtime bytecode 2,100 cold / 100 warm value types and string/bytes value types only
Use constant/immutable whenever a value never changes after deployment — a major gas saving.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract Config {
uint256 public constant FEE_BPS = 30; // fixed at compile time; inlined
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
address public immutable OWNER; // set once in constructor; stored in bytecode
uint256 public immutable DEPLOYED_AT;
constructor() {
OWNER = msg.sender;
DEPLOYED_AT = block.timestamp;
}
}12.3 Getters for complex types
Arrays: history(uint256 i) returns one element (not the whole array).
Mappings: one parameter per key.
Structs: returns members as a tuple, omitting mappings and arrays.
🧪 Part I Exercises
Deploy HelloWorld in Remix VM, call setGreeting from a non-owner account, and read the revert reason in the console.
Using cast, compute the selector of approve(address,uint256) and the keccak256 of the string "MINTER_ROLE".
Write a contract demonstrating that uint8(300) returns 44 and that uint8 x = 255; x++; reverts; then make it wrap with unchecked.
Build a Counter with increment, decrement (reverting below zero with a custom error), and reset callable only by the deployer.
Write a Todo contract with structs and arrays: create, toggle, update text, and delete (swap-and-pop).
Implement a simple address book: mapping(address => string) plus an array of keys so all entries can be enumerated.
Demonstrate the difference between storage and memory struct variables with two functions and explain the results in comments.
Write a function that takes uint256[] calldata and returns the sum, max, and min; compare its gas against a memory version.
Create an enum-driven state machine for an order (Created → Paid → Shipped → Delivered / Cancelled) that rejects invalid transitions.
Compare gas for reading a constant, an immutable, and a regular state variable using Remix or forge test --gas-report.