Gas: Metering Computation and Storage
The part overview framed the question this whole part answers: running a shared world computer costs real resources, and someone has to pay for them in a way that untrusting strangers can agree on. This page builds the foundation for everything that follows — the unit in which that cost is measured. Before we can talk about limits, fee formulas, auctions, or EIP-1559, we need to answer one prior question: what exactly are we charging for?
The answer is gas. Gas is Ethereum’s unit of work. Every operation the EVM can perform is assigned a fixed gas cost, and a transaction’s total cost is the sum of the gas of every operation it executes. This page explains why that unit has to be work and not bytes, how the cost of each operation is set, and why writing to storage is the most expensive thing you can do on the chain.
Why bytes cannot meter this chain
Section titled “Why bytes cannot meter this chain”Bitcoin, which this series covers in a sibling book, can get remarkably far charging by size. A Bitcoin transaction is, to a first approximation, data: some inputs, some outputs, some signatures. The cost a transaction imposes on the network is dominated by the bytes every node must download, verify, and store, so a fee-per-byte captures the burden reasonably well.
Ethereum cannot do this, because an Ethereum transaction is not data — it is a program. Recall from the EVM part that a transaction can invoke a contract, and that contract is bytecode the stack machine executes: it can loop, hash, read storage, write storage, and call other contracts. The bytes of the transaction tell you almost nothing about the work it triggers.
Consider two transactions that are identical in size:
Same byte size, wildly different work ------------------------------------- Tx A: call contract X ──▶ ADD; ADD; RETURN (a few cheap ops) Tx B: call contract Y ──▶ loop 10,000 times, writing storage each pass (enormous work)Both are a short “call this address” transaction on the wire. But Tx A finishes in microseconds and touches nothing permanent, while Tx B pins every full node’s CPU for far longer and grows the state they must all keep forever. If we charged by size, both would cost the same — and an attacker would pay the price of Tx A to inflict the cost of Tx B on the entire network.
That gap is not an edge case; it is the defining property of an account-model, Turing-complete chain. Because a small input can command an unbounded amount of computation, the fee has to track the computation itself, not the bytes that requested it. So Ethereum meters transactions by the work they do. That unit of work is gas.
A meter, not just a price
Section titled “A meter, not just a price”Gas plays a second role beyond charging fees: it is the termination guarantee for a Turing-complete machine. A contract can contain an infinite loop, and no analysis can decide in advance whether an arbitrary program halts — that is the halting problem. Ethereum sidesteps it: every transaction carries a gas budget, every operation spends from that budget, and when it hits zero the machine halts and throws. An infinite loop does not hang the network; it simply runs out of gas.
You can see this directly in this book’s companion crate, ethmini — a working mini-EVM in Rust. Its interpreter charges gas before each operation acts, so the meter can never go negative:
// From ethmini's VM loop: decode one instruction, pay for it, then run it.let (op, next) = Op::decode(code, pc)?;charge(&mut gas, op.gas_cost())?; // OutOfGas if the budget can't cover itAn ethmini test makes the guarantee concrete: a program that jumps back to its own start forever does not spin — it halts as OutOfGas the instant its budget is exhausted:
// JUMPDEST ; PUSH 0 ; JUMP -> jumps to pc 0 forever.let code = assemble(&[Op::JumpDest, Op::Push(0), Op::Jump]);let err = run(&code, &empty(), 1000).unwrap_err();assert!(matches!(err, VmError::OutOfGas { limit: 1000, .. }));So gas is doing two jobs at once: it prices the work a transaction does, and it bounds that work so a public machine cannot be forced to compute forever. Both jobs need the same thing — a fixed cost attached to each operation.
Gas as a per-operation cost
Section titled “Gas as a per-operation cost”Here is the core definition. Gas is a fixed cost assigned to each EVM operation. Executing an opcode spends that many gas from the transaction’s budget. The transaction’s total gas is simply the sum over every operation it runs — every arithmetic step, every storage access, every jump.
The costs are not arbitrary. Each opcode’s gas is meant to reflect the real burden that operation puts on the nodes that must execute it: cheap arithmetic that happens inside the CPU costs little; touching persistent storage costs far more. ethmini’s gas schedule is deliberately EVM-inspired and, crucially, ordered the way real Ethereum orders things:
// ethmini's gas schedule — simplified numbers, real EVM ordering.pub fn gas_cost(&self) -> u64 { match self { Op::Stop | Op::Return => 0, Op::JumpDest => 1, Op::Push(_) | Op::Pop | Op::Add | Op::Sub => 3, // arithmetic: cheap Op::Mul => 5, Op::Jump => 8, Op::JumpI => 10, Op::SLoad => 100, // read storage: much dearer Op::SStore => 200, // write storage: dearest of all }}Read the ordering, not the exact numbers. Arithmetic — the work a CPU does natively in a nanosecond — sits at the bottom at ~3 gas. A storage read (SLOAD) is far dearer at ~100. A storage write (SSTORE) is dearest of all at ~200. That shape mirrors real Ethereum’s gas schedule exactly, even though real EVM’s numbers are larger and, for SSTORE, much more elaborate (it depends on whether a slot goes from zero to non-zero, is set back to zero, and more). The lesson to carry forward is the ranking: compute is cheap, reading state is expensive, writing state is the most expensive thing you can do.
Summing gas over a transaction
Section titled “Summing gas over a transaction”To find what a transaction costs in gas, you walk its execution and add up the cost of each op. Take a tiny program that stores a value and reads it back — the classic SSTORE / SLOAD round-trip from ethmini’s tests:
PUSH 99 PUSH 0 SSTORE PUSH 0 SLOAD RETURN 3 + 3 + 200 + 3 + 100 + 0 = 309 gasThe two storage operations dominate the total — 300 of the 309 gas — even though there are more arithmetic-style pushes than storage ops. That is the schedule doing its job: the expensive work shows up as the expensive part of the bill.
Why storage writes are the dearest operation
Section titled “Why storage writes are the dearest operation”Why is SSTORE the most expensive thing in the schedule? Because it is the only operation whose cost falls on every future node, forever — not just the node running the transaction now.
Recall the account model: the world state is a map from address to account, and each contract account carries a storage map of its own. When a contract executes SSTORE, it adds or changes an entry in that persistent map. That change is not scratch work that evaporates when the transaction ends — it becomes part of the shared state that every full node on Earth must store, forever, and must include when it computes the state root.
Op cost Who pays, and for how long ------- -------------------------------------------------- ADD the executing node, for a nanosecond SLOAD the executing node, one lookup into existing state SSTORE EVERY full node, FOREVER — new bytes of permanent state, carried by the whole network in perpetuityThis is the key economic idea: gas prices an externality. An SSTORE imposes a cost on parties who are not part of the transaction — the thousands of node operators who will store that slot for the life of the chain. Left unpriced, contracts would bloat the state with impunity (“state bloat”), and running a full node would grow ever more expensive until only a few could afford to, eroding the very decentralization that makes the chain trustworthy. Charging heavily for storage internalizes that cost: the person creating permanent state pays up front for the burden they hand to everyone else.
ethmini’s own source states the principle plainly in its comment on the schedule — a storage write is dearest “because it grows state every full node must keep forever.” That is not a simplification of Ethereum; it is Ethereum’s reasoning, distilled.
Under the hood — a storage write moves the whole state root
Section titled “Under the hood — a storage write moves the whole state root”There is a second, structural reason SSTORE is expensive: it changes the state root. In ethmini, the state root is a hash over every account’s balance, nonce, and storage, in a fixed order. A single storage write changes the bytes that get hashed, so the root changes — which is exactly the point of a commitment: the root moves if and only if the state moved.
Real Ethereum gets this same property from a Merkle-Patricia trie rather than a flat sort-and-hash, and a trie write is not free: it re-hashes a path from the changed leaf up to the root. So an SSTORE triggers real cryptographic work on top of the storage burden — another slice of the cost the gas price accounts for. Reads (SLOAD) walk the trie but change nothing, which is why they are dear but not as dear as writes.
Gas is a unit of work, not a price in ether
Section titled “Gas is a unit of work, not a price in ether”One more distinction to nail down before the next page, because it trips up nearly everyone at first: gas is not money. Gas is a count of work units. A plain transfer is 21,000 gas whether the ether price is $10 or $10,000; a heavy contract call is a few hundred thousand gas regardless of markets. The number of gas a transaction consumes is a fact about the computation — fixed by the opcode schedule — and nothing else.
What turns gas into a fee is a separate quantity: the price per unit of gas, denominated in ether (specifically in gwei, a billionth of an ether). Total fee = gas used × price per gas. This page has defined only the left factor — how much work a transaction is. The right factor — how the price per gas is set, and why that price became the whole story of Ethereum’s fee market — is what the next page and the rest of this part are about, culminating in EIP-1559.
Keeping the two factors separate is what lets the protocol change fee policy — auctions, base fees, burning — without ever touching the metering. The gas cost of SSTORE is a statement about physics and storage; the price of gas is a statement about demand for blockspace. Confusing them is the root of most gas confusion.
A concrete anchor: 21,000 gas
Section titled “A concrete anchor: 21,000 gas”The number to memorize is 21,000. That is the fixed gas cost of the simplest possible transaction — a plain ether transfer from one account to another, with no contract code executed. It is a flat floor charged for the unavoidable work any transaction imposes: recovering the sender from the signature, checking and bumping the nonce, and updating two balances.
Transaction Gas (approx, real Ethereum) ----------- --------------------------- Plain ETH transfer 21,000 (fixed floor) ERC-20 token transfer ~45,000–65,000 (a storage write or two) Uniswap-style token swap ~100,000–200,000+ (many ops + writes)The plain transfer is a fixed 21,000 because it always does the same small amount of work. A token transfer costs more because it runs contract code that writes at least one storage slot. A swap costs far more still because it runs a lot of code and touches a lot of state. The bill tracks the work, not the size: all three transactions are comparable in bytes, and their costs differ by an order of magnitude because their computations do.
(These figures are approximate and depend on the specific contract; treat them as the ranking, not exact quotes. The 21,000 floor for a plain transfer, however, is a fixed protocol constant, stable as of 2024.)
The architect’s lens
Section titled “The architect’s lens”- Why does it exist? Because an account-model, Turing-complete chain lets a tiny input command unbounded computation and permanent state growth. A byte-based fee cannot see that, so Ethereum needs a unit that measures the work a transaction actually does. Gas is that unit.
- What problem does it solve? Two at once: it prices the real burden each operation imposes on every node (so the network is compensated and not spammed), and it bounds execution (so a Turing-complete machine cannot be forced into an infinite loop) by giving every transaction a spendable budget.
- What are the trade-offs? A fixed per-opcode schedule is simple and consensus-friendly — every client charges identically — but it is a coarse approximation of true cost. Mispriced opcodes have been exploited (see the 2016 Shanghai attacks), and the schedule needs periodic hard-fork repricings to stay honest as hardware and state size change.
- When should I avoid it? You never avoid gas on Ethereum — it is load-bearing. But you avoid paying too much of it: designers minimize
SSTOREs, pack storage slots, and push heavy computation off-chain precisely because gas makes on-chain state the scarce, expensive resource. - What breaks if I remove it? Everything. Without metering, one infinite-loop contract halts the entire network, and unpriced storage lets anyone bloat the state until running a node is unaffordable — collapsing the decentralization that makes the shared world computer trustworthy in the first place.
Check your understanding
Section titled “Check your understanding”- Bitcoin can charge fees by transaction size, but Ethereum cannot. What property of Ethereum transactions makes a byte-based fee unworkable?
- Two transactions have identical byte sizes. Explain how one can impose thousands of times more work on the network than the other, and what unit captures that difference.
- Using
ethmini’s schedule (Push/Add = 3, SLOAD = 100, SSTORE = 200), compute the gas for:PUSH 5 PUSH 0 SSTORE PUSH 0 SLOAD RETURN. Which single operation dominates, and why? - Why is
SSTOREthe most expensive operation in the schedule? Frame your answer in terms of who pays the cost and for how long. - A friend says “gas went up, so my transaction now uses more gas.” Correct them: distinguish the two separate quantities and say which one actually changed.
Show answers
- Ethereum transactions are programs, not just data. An account-model, Turing-complete chain lets a small input trigger unbounded computation (loops, hashing) and permanent state changes. The bytes on the wire reveal almost nothing about the work executed, so a fee-per-byte would let an attacker pay a tiny-transaction price to inflict an enormous-computation cost on every node.
- One transaction might run a few cheap arithmetic ops and return; the other might loop thousands of times, writing storage on each pass. Same size on the wire, vastly different CPU time and permanent state growth. Gas — a fixed cost summed over every operation actually executed — captures the difference; bytes cannot.
PUSH 5(3) +PUSH 0(3) +SSTORE(200) +PUSH 0(3) +SLOAD(100) +RETURN(0) = 309 gas.SSTOREdominates at 200 of the 309, because a storage write creates permanent state that every full node must keep forever — the dearest kind of work.SSTOREwrites a slot into persistent state, so the cost falls on every full node, forever: they must all store the new data and carry it in the state root in perpetuity. Arithmetic costs only the executing node for a nanosecond; a storage read touches only existing state on one node. A write is an externality imposed on the whole network for the life of the chain, so gas prices it highest.- Two separate quantities: (a) gas used — the count of work units, fixed by the opcode schedule for a given computation, and (b) price per gas — the ether (gwei) paid per unit, set by the fee market. “Gas went up” almost always means the price per gas rose; the gas used by the same transaction is unchanged, because it does the same work. Fee = gas used × price per gas.