Expert level
The EVM is a stack machine with 256 bit words and five places to keep data: the stack, memory, storage, transient storage and calldata. Almost every gas cost follows from one principle: an operation is priced by the burden it puts on every node that has to repeat it, so reading state from disk is expensive the first time, writing new state is the most expensive thing you can do, and anything that disappears when the transaction ends is cheap.
The machine in one paragraph
The Ethereum Virtual Machine executes bytecode one opcode at a time against a stack of 256 bit words, at most 1,024 deep. Every node that validates a block re-executes every transaction in it and must reach the same result, byte for byte. That single requirement explains the whole design: no floating point, no system clock, no randomness, no unbounded loops, and a price on every operation so that no transaction can make every node on the network do unbounded work for free.
Five places to keep data
| Location | Lifetime | Addressed by | Cost profile |
|---|---|---|---|
| Stack | The current call frame | Position, top 16 reachable | Cheapest. Most arithmetic is 3 to 5 gas |
| Memory | The current call frame | Byte offset | Cheap to use, with a quadratic charge for growing it |
| Calldata | The transaction | Byte offset, read only | Paid once per byte in the intrinsic cost, then cheap to read |
| Transient storage | The transaction | 32 byte slot | 100 gas per TLOAD or TSTORE, gone when the transaction ends |
| Storage | Forever, in the state trie | 32 byte slot, 2256 per contract | The expensive one. Priced by whether a slot is cold, warm, empty or changing |
Transient storage arrived with the Dencun upgrade in March 2024 (EIP-1153). It behaves like storage for the length of one transaction and then vanishes, which makes it ideal for reentrancy locks, callbacks and flash accounting. Uniswap v4 depends on it.
What a transaction costs before it runs
Every transaction pays an intrinsic cost before a single opcode executes:
| Component | Gas |
|---|---|
| Any transaction | 21,000 |
| Contract creation, extra | 32,000, plus 2 per 32 byte word of init code (EIP-3860) |
| Each zero byte of calldata | 4 |
| Each nonzero byte of calldata | 16 |
| Each address in an access list (EIP-2930) | 2,400 |
| Each storage key in an access list | 1,900 |
Since the Pectra upgrade in May 2025, EIP-7623 adds a floor for transactions that are mostly data. Count tokens as zero bytes plus four times nonzero bytes. The transaction pays the greater of its normal cost and 21,000 plus 10 gas per token. Ordinary contract calls rarely hit the floor, because their execution gas dwarfs their calldata; it exists to cut the worst case amount of calldata a block can carry, which made room to raise blob capacity.
Fusaka, in December 2025, capped any single transaction at 224 = 16,777,216 gas (EIP-7825), so no transaction can occupy most of a block by itself.
Warm and cold: the first touch is the expensive one
Since the Berlin upgrade in 2021 (EIP-2929), the EVM tracks which accounts and storage slots a transaction has already touched. The first access pays for the disk read a node may have to do. Every later access in the same transaction is priced as a cache hit.
| Access | Cold (first time) | Warm (again) |
|---|---|---|
| SLOAD, reading a storage slot | 2,100 | 100 |
| Touching an account: BALANCE, EXTCODESIZE, EXTCODEHASH, CALL and friends | 2,600 | 100 |
Some things start warm: the sender, the recipient, every precompile and, since Shanghai (EIP-3651), the block's fee recipient. An access list lets you pre-warm the rest at 2,400 per address and 1,900 per slot, which is slightly cheaper than paying the cold price during execution, but only when you know in advance exactly what will be touched.
SSTORE: the most expensive thing you can do
Writing storage creates or changes state that every full node must keep. The price depends on what the slot held at the start of the transaction and what you are writing:
| Write | Gas (slot already warm) | Add if the slot is cold |
|---|---|---|
| Zero to nonzero (new state) | 20,000 | 2,100 |
| Nonzero to a different nonzero | 2,900 | 2,100 |
| Same value as now | 100 | 2,100 |
| Nonzero to zero | 2,900, then 4,800 refunded at the end | 2,100 |
Refunds are paid back when the transaction finishes, and since London (EIP-3529) they are capped at one fifth of the gas the transaction used, down from one half. The same EIP removed the SELFDESTRUCT refund and cut the clearing refund from 15,000 to 4,800, which together killed the old trick of minting "gas tokens" by filling storage when gas was cheap and clearing it when gas was dear.
How Solidity lays out storage
State variables are assigned slots in declaration order. Values smaller than 32 bytes are packed into the same slot while they fit, which is why the order you declare variables in can change what every function costs. Mappings and dynamic arrays cannot know their size, so their contents are placed at hashed locations derived from the slot they were declared in:
contract Vault {
uint128 public totalShares; // slot 0, low 16 bytes
uint64 public lastUpdate; // slot 0, next 8 bytes (packed)
address public owner; // slot 1 (20 bytes do not fit beside 24 used)
mapping(address => uint256) public shares; // slot 2 holds nothing itself
uint256[] public history; // slot 3 holds the length
}
// shares[user] lives at keccak256(abi.encode(user, uint256(2)))
// history[i] lives at uint256(keccak256(abi.encode(uint256(3)))) + iTwo consequences are worth internalizing. Reading two packed variables costs one cold SLOAD, not two, if they share a slot. And every mapping entry lives at an effectively random slot, so iterating a mapping is impossible onchain and each new key written is a fresh 20,000 gas slot.
Memory: cheap until it is not
Memory starts empty in every call frame and grows in 32 byte words. You pay for its high water mark with a cost that has a linear and a quadratic part:
| Memory used | Words | Total memory cost |
|---|---|---|
| 1 KB | 32 | 98 gas |
| 32 KB | 1,024 | 5,120 gas |
| 1 MB | 32,768 | 2,195,456 gas |
The quadratic term is negligible for normal contracts and prohibitive for anything that tries to hold a large data structure in memory. It is the EVM's way of capping how much RAM a transaction can demand from every node.
Calls, value and the 63/64 rule
A CALL pays the account access cost, plus 9,000 if it sends value and a further 25,000 if that creates a new
account. A value transfer also hands the callee a free stipend of 2,300 gas, historically enough to emit an
event and famously not enough to do much else, which is why transfer() and send()
broke when opcode costs were repriced (EIP-1884 in the Istanbul upgrade of 2019 raised SLOAD from 200 to 800
gas) and why modern code uses call with an explicit check.
Since EIP-150 in 2016 a caller can forward at most 63/64 of its remaining gas to a callee. That bounds call depth in practice and guarantees the caller always keeps enough gas to handle a failed call.
CALL
Runs the callee’s code in the callee’s own storage, with msg.sender set to the caller. The ordinary case.
DELEGATECALL
Runs the callee’s code in the caller’s storage and context. The foundation of every proxy and every upgradeable contract, and of a whole class of storage collision bugs.
STATICCALL
A call that reverts if anything tries to modify state. Used for view functions and for calling code you do not trust to be read only.
Logs: the cheap way to publish data
An ERC-20 Transfer event has three topics (the event signature and the two indexed addresses) and 32 bytes of data, so it costs 1,756 gas. Storing the same 32 bytes in a fresh slot costs 20,000 or more. Contracts cannot read logs, but indexers, explorers and wallets can, so anything only needed offchain belongs in an event.
Worked example: one ERC-20 transfer, two prices
Why does sending a token to a new holder cost so much more than sending it to an existing one? Walk through a plain implementation, sending 1,000 USDC worth of a 6 decimal token:
| Step | To an existing holder | To a new holder |
|---|---|---|
| Intrinsic cost | 21,000 | 21,000 |
| Calldata: 4 byte selector plus two 32 byte words | about 600 | about 600 |
| Read and write the sender’s balance (cold slot, nonzero to nonzero) | 5,000 | 5,000 |
| Read and write the recipient’s balance | 5,000 (nonzero to nonzero) | 22,100 (zero to nonzero) |
| Transfer event, three topics and one word of data | 1,756 | 1,756 |
| Everything else: dispatch, checks, stack work | a couple of thousand | a couple of thousand |
| Roughly | about 35,000 | about 52,000 |
The 17,000 gas difference is almost entirely one thing: creating a storage slot that did not exist. Real tokens add their own overhead, and proxies add a DELEGATECALL, so measured figures run higher, but the shape is always the same. The same reasoning tells you why airdropping to thousands of fresh addresses is expensive and why claim based distributions push that cost onto the claimer.
Deploying code
A new contract's address is determined before it exists, which is what makes counterfactual wallets and deterministic deployments possible:
Deployed code costs 200 gas per byte to store and may not exceed 24,576 bytes (EIP-170), with init code capped at twice that (EIP-3860). The size limit is why large protocols are split into libraries, facets and modules.
Precompiles: native code at fixed addresses
Some operations are far too slow to write in EVM bytecode, so they are built into every client and exposed at fixed addresses, called like contracts and priced like opcodes.
| Address | What it does | Why it matters |
|---|---|---|
| 0x01 | ecrecover: recover a signer from a secp256k1 signature | Every permit, every signed order, every meta transaction. 3,000 gas |
| 0x02 | SHA-256 | Bitcoin interoperability and light clients |
| 0x05 | Modular exponentiation | RSA style verification |
| 0x06 to 0x08 | Addition, multiplication and pairing on the BN254 curve | What makes Groth16 and PLONK proof verification affordable onchain |
| 0x0a | KZG point evaluation (EIP-4844) | Verifying that data really sits in a blob |
| 0x0b to 0x11 | BLS12-381 operations (EIP-2537, Pectra) | Verifying BLS signatures and newer proof systems |
| 0x100 | secp256r1 signature verification (EIP-7951, Fusaka) | Passkeys and device secure enclaves as wallet signers |
What this means when you write code
Treat storage as the budget
Pack related small values into one slot, read a slot once and keep it on the stack, and use
constantandimmutablefor anything fixed, since those live in bytecode and cost no SLOAD at all.Use the cheap locations for temporary things
Locks, flags and values passed between callbacks belong in transient storage. Arguments to external functions can stay in calldata instead of being copied to memory.
Publish, do not store, what only humans read
History, receipts and analytics belong in events. Contracts only need to store what contracts need to read.
Measure, do not guess
Foundry gas reports and snapshots show the cost of every function and catch regressions in review. The numbers above explain the results; the tools tell you the truth.
// A reentrancy lock in transient storage (Solidity 0.8.28 or later).
// TLOAD and TSTORE cost 100 gas each, so the lock costs about 300 gas,
// and the slot clears automatically when the transaction ends:
// no 20,000 gas first write and no refund bookkeeping.
bool transient locked;
modifier nonReentrant() {
require(!locked, "reentrant call");
locked = true;
_;
locked = false;
}
// If you write it in assembly instead, use a namespaced slot (ERC-7201
// style), never slot 0: transient state variables are laid out from 0.What is changing next
Every number in this guide is the mainnet schedule as of September 2026. The next upgrade, Glamsterdam, was heading for its Sepolia testnet fork in early October 2026 with mainnet targeted for later in the year. Its headline changes are enshrined proposer builder separation (EIP-7732) and block level access lists (EIP-7928), which let clients execute non overlapping transactions in parallel. Neither headline change alters the cost model described here, but Glamsterdam’s candidate list has also carried gas repricing proposals, so check the final EIP list before relying on a specific figure.
Common questions
Why does writing to a new storage slot cost 20,000 gas?
Because it permanently grows the state that every full node must store and serve, forever. Changing an existing nonzero value only alters state that already exists, which is why it costs 2,900 instead. The price reflects the long term burden on the network, not the computation.
What is the difference between memory and storage in the EVM?
Memory is a scratch space that exists only for the current call and is wiped when it returns. Storage is the contract's permanent state, kept in the state trie between transactions. Memory is cheap until it grows large; storage is expensive from the first write.
What are warm and cold access in Ethereum?
The first time a transaction touches an account or a storage slot it is cold and pays 2,600 or 2,100 gas, reflecting a possible disk read. After that it is warm for the rest of the transaction and costs 100. Access lists can pre-warm known accounts and slots.
Why is my transaction gas estimate different from the gas actually used?
Estimates simulate against the current state, but the state can change before inclusion: a balance that was zero may now be nonzero, a slot may already be warm, a branch may take a different path. Refunds are also only applied at the end, which is why estimates include a margin.
Does the EVM have a maximum contract size?
Yes. Deployed code is limited to 24,576 bytes by EIP-170, and init code to 49,152 bytes by EIP-3860. Large systems split logic across libraries, proxies and modules to stay under it.
Where to go next
Next: Inside the Solana runtime: accounts, parallelism and fees
Guide 8 of 16 in the Expert level. Your place is kept on this device, so you can pick up exactly here next time.