---
id: 24
title: "Gas Mechanics in Detail: Metering Units, the Fee Market, and Execution Halts"
slug: 0-7-gas-mechanics
date: 2026/09/25
summary: Gas is the EVM's resource metering unit, while its price is set by the fee market; conflating the two leads to misjudging the boundaries of scaling and execution halts. This piece breaks down intrinsic and dynamic costs, explains what out-of-gas and REVERT each give back, and says who receives EIP-1559's base fee and tip.
keywords: gas metering,EIP-1559,intrinsic cost,out-of-gas,EIP-2929
heroImage: /images/articles/photos/0-7-gas-mechanics.jpg
---

The simplest ETH transfer consumes a fixed 21000 gas (Gtransaction in appendix G of the Yellow Paper); what an ERC-20 transfer consumes depends on the contract implementation and on whether slots are cold or warm, and usually lands in the tens of thousands of gas — an order-of-magnitude estimate, not a measured value. These numbers are set by the protocol and change only through hard forks; how much ETH the same transaction actually costs can move by more than a factor of ten within hours. The former is a metering unit, the latter is a price. Keep the two concepts apart, and fee debates, scaling narratives, and the behavior of execution halts all become clear.

The gas mechanism does three jobs at once: it converts computation and state operations into a single unit, it allocates limited block space through a price market, and it provides a definite rollback boundary when resources run out. This piece follows those three lines: what intrinsic and dynamic costs each contain, how cold and warm access are priced, what a transaction gets back under each of the three halts — invalid, REVERT, and out-of-gas — where the boundary between base fee, priority fee, and execution-layer gas falls after EIP-1559, and why gas makes DoS metered without ever making pricing correct.

## Gas is the metering unit for computation and storage; the price of ETH is a separate matter

Gas is an abstract unit of account for the amount of abstract resource consumed by executing one instruction, reading or writing state once, or expanding one word of memory. Its values come from protocol constants — for example, 3 gas for addition (Gverylow), 100 gas for a warm SLOAD after the Berlin upgrade (block 12,244,000, April 15, 2021), and 20000 gas for SSTORE writing non-zero over zero. These constants change only through hard forks and have nothing to do with market supply and demand.

Gas is priced in wei, with gwei the common unit, and 1 gwei equals 10⁹ wei. A transaction's fee equals gas_used times effective_gas_price, where the former is decided by execution and the latter jointly by the fee market and the bids in the transaction's signature. That multiplication is the basis for everything that follows.

Keeping the two layers apart pays off directly. In a debate about “reducing gas”, first pin down whether the goal is to lower gas_used or gas price: the former takes fewer operations, a better storage layout, or a change to the EVM's pricing rules, while the latter depends on current demand and the shape of the fee market — EIP-1559 made it more predictable but changed the price of no instruction. In a debate about “who gets the fee”, the base fee is burned by the protocol and has no recipient, and only the priority fee reaches the validator's account. In a debate about “the transaction limit”, gas_limit is a capacity cap rather than a cost cap; the cost cap is max_fee_per_gas, a different field.

## Intrinsic cost: what is deducted before execution begins

Every transaction first pays an intrinsic cost (intrinsic gas), deducted before the EVM executes its first instruction. Equation (64) of the Yellow Paper writes it as g₀: a flat base price of 21000 (Gtransaction), plus 4 gas for each zero byte and 16 gas for each non-zero byte of the data field (the non-zero byte price was cut from 68 to 16 by EIP-2028 in the Istanbul upgrade, block 9,069,000, December 8, 2019), plus another 32000 for a contract-creating transaction (Gtxcreate); after EIP-2930 (Berlin, block 12,244,000, April 15, 2021), each address in the access list costs 2400 gas and each storage key 1900 gas; after EIP-3860 (Shanghai, block 17,034,870, April 12, 2023), initcode for contract creation costs another 2 gas per 32-byte word.

The meaning of 21000 is easily misread as “the fee for a transfer”. Precisely put, it is the base price of “having a transaction at all”, independent of the amount transferred. The simplest ETH transfer has empty calldata, so the data field adds nothing and the total is exactly 21000.

EIP-2929 (Berlin, block 12,244,000, April 15, 2021) also defines the warm set at the start of a transaction: the sender's address, the recipient's address (for a creating transaction, the address being created), and every precompiled contract address are already in accessed_addresses and incur no cold-access fee. This is why a transfer to an ordinary address is not charged extra for accessing the recipient's account.

Because the intrinsic cost is deducted before execution, three behavioral boundaries follow. A transaction whose gas_limit is below the intrinsic cost is simply invalid: it is not included in a block and no gas is charged. When execution halts mid-way on out-of-gas, the intrinsic cost is not refunded. The 2400 and 1900 of the access list (post-Berlin prices) are prepaid: you walk the addresses and slots you intend to access and mark them warm in advance, but if execution never touches them, the money is not returned.

## Dynamic cost: how memory expansion and cold/warm access accumulate

Execution-phase cost is the sum of a static part and a dynamic part. The static part is the opcode's fixed price, which the fee schedule in appendix G of the Yellow Paper expresses in tiers: Gverylow at 3 gas (ADD, SUB, MLOAD, MSTORE, the PUSH family, and so on), Glow at 5, Gmid at 8, Ghigh at 10, Gbase at 2, Gjumpdest at 1.

The dynamic part depends on operand size and on whether state access is cold or warm. Memory expansion is priced by C_mem(a) = 3a + ⌊a² / 512⌋ (Yellow Paper equation (328), Gmemory = 3), where a is the number of words; the actual charge is the difference between before and after expansion, and this term is roughly linear within 704 bytes (22 words) before the quadratic term takes effect. Copy-type operations add 3 gas per word on top of the base price (Gcopy); KECCAK256 costs 30 gas (Gkeccak256) plus 6 gas per word (Gkeccak256word); LOG costs 375 gas (Glog) plus 375 gas per topic (Glogtopic) plus 8 gas per byte (Glogdata); EXP costs 10 gas (Gexp) plus 50 gas per exponent byte (Gexpbyte).

Cold and warm access are defined by EIP-2929 (Berlin). The first access to a given (address, slot) pair is a cold access, costing 2100 gas for SLOAD; a later access within the same transaction is a warm access, costing 100 gas. The cold price for account-type access is 2600 gas, covering CALL, CALLCODE, DELEGATECALL, STATICCALL, and BALANCE along with EXTCODESIZE, EXTCODECOPY, and EXTCODEHASH. These sets are scoped to the transaction, and they revert along with the scope.

| Operation (post-Berlin rules) | Cold access | Warm access |
|--------------------------|--------|--------|
| SLOAD | 2100 | 100 |
| CALL family, BALANCE, EXT* | 2600 | 100 |
| SSTORE's additional cold-access fee | 2100 | Not charged |

The cold/warm mechanism traces directly to lessons left by two DoS attacks. EIP-150 (Tangerine Whistle, block 2,463,000, October 18, 2016), after the attacks of September and October 2016, had already raised SLOAD from 50 gas to 200, CALL from 40 to 700, and SELFDESTRUCT from 0 to 5000. EIP-2929's motivation cites a measurement from a 2019 paper, arXiv:1909.07220: replaying Ethereum's history on the authors' hardware, the same batch of malicious transactions took 20 to 80 seconds while ordinary transactions took only milliseconds, showing that state-reading opcodes were still underpriced. That is a one-off measurement representing only 2019 hardware and workloads; it is not a basis for continuous tracking.

## Three outcomes of an execution halt: invalid transaction, REVERT, and out-of-gas

A transaction can end at three different stages, with different rollback scopes and fee treatment.

The first is an invalid transaction, covering cases such as a nonce mismatch, a bad signature, a balance too small to cover the gas prepayment, or a gas_limit below the intrinsic cost. Such a transaction never enters a block and consumes no gas; it is an off-chain rejection, a different thing from an execution halt.

The second is REVERT, triggered by a revert statement, a failed require, or an inline-assembly revert. All state changes in the current frame roll back, the remaining gas returns to the layer above, and whatever was consumed is paid as usual; the refund counter also rolls back to its state before the frame. REVERT is a “failure with a reason”: it can return error data and suits situations where the caller needs to distinguish why something failed.

The third is an exceptional halt, covering out-of-gas, an illegal opcode, stack overflow, a state write inside a static call, initcode over the limit, and so on. State rolls back just the same, but all gas remaining in the current frame is destroyed. If the exception happens in the top-level frame, the transaction's gas_used equals gas_limit, the full amount is charged, and there is nothing to return. That is the precise meaning of “after OOG, all the gas is consumed”: the remaining gas of the current frame goes to zero, with no relation to the chain's overall balance.

```solidity
// REVERT: state rolls back, remaining gas is returned, and what was consumed is paid
function guarded(uint256 x) external pure returns (uint256) {
    require(x != 0, "zero");
    return 1e18 / x;
}

// Exceptional halt: an infinite loop triggers out-of-gas and destroys all gas left in the current frame
function oog() external pure {
    while (true) {}
}
```

Whether a child call's failure drags down the parent frame is decided by the “keep 1/64” rule introduced in EIP-150 (Tangerine Whistle). When calling a child frame, the parent can forward at most 63/64 of its remaining gas (that is, gas - gas // 64) and keeps at least 1/64; CREATE and CREATE2 likewise offer only 63/64. A child frame's OOG therefore does not drag the parent into OOG, and the parent can use the reserved gas to catch the failure and keep executing. This rule replaced the old call-depth limit in 2016, downgrading the “depth bomb” from a structural attack to a pure gas problem.

```solidity
// Forward at most 63/64 of gasleft() to the child call; the parent keeps 1/64
(bool ok, ) = target.call{gas: gasleft()}("");
```

A CALL carrying value has two further charges (appendix G of the Yellow Paper): a non-zero transfer costs Gcallvalue 9000 gas, and if the target account does not exist, another Gnewaccount 25000 gas; the callee receives a 2300 gas call stipend (Gcallstipend) for the simplest receive logic. EIP-2200 (Istanbul, block 9,069,000, December 8, 2019) makes SSTORE fail outright with OOG when gasleft is at or below 2300, precisely to stop that stipend path from being used to write state.

Refunds settle after execution ends and are invisible during execution. EIP-3529 (London, block 12,965,000, August 5, 2021) cut the refund for rewriting non-zero to zero from 15000 to 4800, removed the SELFDESTRUCT refund, and capped a single transaction's total refund at gas_used // 5. Because refunds are unavailable during execution, a contract cannot rely on them to front the gas it needs mid-transaction.

## EIP-1559: base fee, tip, and the execution-layer boundary

EIP-1559 (London, block 12,965,000, August 5, 2021) splits a transaction's fee into two segments. The base fee is computed by the protocol from the parent block's gas_used and a target (half the gas_limit), can move at most 12.5% up or down per block, and is burned. The priority fee (tip) is the extra unit price a user offers the validator to get included earlier. The transaction signature carries two caps, max_fee_per_gas and max_priority_fee_per_gas, and the effective unit price works out as: the priority fee is the smaller of max_priority_fee_per_gas and (max_fee_per_gas - base fee), and effective_gas_price equals the priority fee plus the base fee. Unused gas is returned at this unit price.

The execution-layer boundary needs stating too. After EIP-1559, the GASPRICE opcode returns effective_gas_price — the unit price the sender actually pays — and the validator's actual take cannot be read directly from the execution environment. That also means any old logic that used GASPRICE to judge “miner revenue” no longer holds.

The two fee segments answer different questions: the base fee sets the minimum bar for entering a block, and the tip sets priority within the same block. Total block space has not changed, and when sustained usage exceeds the target, the base fee rises block by block and squeezes demand out — a queueing mechanism, unrelated to capacity expansion. Reading EIP-1559 as a way to reduce gas_used is a common mistake; it only changes how the price forms.

One more boundary that is easy to conflate: blob fees are not execution-layer gas. EIP-4844 (Cancun, block 19,426,587, March 13, 2024) introduced a separate base fee and unit of account (blob gas) for blob data, and EIP-7516 added an opcode that returns the blob base fee. What a rollup discusses as data-posting cost is the blob market; it is a different ledger from the gas consumed by contract execution.

## DoS being “metered” is not the same as pricing being correct

Gas's core security property is converting resource usage into cost. To fill a block with junk transactions, an attacker must pay the fee corresponding to the block's entire gas — that is what “metered” means. Compared with a network that charges nothing, the marginal cost of an attack goes from zero to something proportional to the space it occupies.

But that only works when pricing matches real cost. The DoS attacks of September and October 2016 exploited precisely the underpriced state-reading and call-family opcodes and directly provoked the Tangerine Whistle hard fork; after that, EIP-1884 (Istanbul, block 9,069,000, December 8, 2019) raised SLOAD from 200 to 800 and BALANCE and EXTCODEHASH from 400 to 700, and EIP-2929 split state access into cold and warm tiers and raised the cold price sharply, again on the grounds that the same pattern could stretch single-block processing time to tens of seconds. EIP-3860 governs jump-target analysis for initcode, work that had carried no metering at all. A structural lag sits between pricing rules and implementation cost: client database layouts and execution engines keep getting faster, while the gas constants wait for the next hard fork to be re-estimated.

The refund mechanism offers the counterexample. It was meant to encourage contracts to clean up state they no longer use, but in practice it spawned schemes like GasToken that treat state slots as batteries — hoarding gas when rates are low and releasing it when they are high — producing state bloat and extra variance in per-block gas usage. EIP-3529 cut and capped refunds precisely to close that channel.

A few conditions under which the mechanism fails deserve separate mention. Pricing is a product of governance, and constants are usually chosen against the worst case across implementations (EIP-3860 states that its 2 gas per word comes from worst-case baselines in different implementations), while different clients take different amounts of time on the same batch of operations, so the real resource cost behind one gas price is not uniform. That is a qualitative judgment; EIP-2929's quantitative evidence for raising prices is a one-off 2019 measurement on one test hardware setup, not a basis for continuous tracking. The value of the ordering market is not carried entirely by the tip: MEV auctions and client strategies both affect inclusion order, and a high tip is only one of the incentives that make a transaction more likely to land in the next block. The block gas limit is itself a trade-off between throughput and validation cost, and raising it also raises the hardware bar for full nodes.

The point of this section is that gas is a mechanism needing periodic calibration: both “gas pricing is reliable” and “gas pricing is unreliable” are conclusions that claim too much. EIP-150, EIP-1884, EIP-2929, and EIP-3860 are all calibration moves. As for what triggers the next calibration, only an inference is available, not a conclusion: all four calibrations were prompted directly by publicly observed resource-amplification attacks, so this piece infers that the next one is most likely an event of the same kind. That inference has no public basis, and it does not rule out an earlier re-estimate driven by state growth, block capacity, or other considerations.

## Boundaries and open questions

The bases cited are the fee schedule in appendix G of the Yellow Paper, equations (64) and (328) (the downloaded version's footer labels it the Shanghai version), and the specification texts of EIP-150, EIP-1884, EIP-2028, EIP-2200, EIP-1559, EIP-2929, EIP-2930, EIP-3529, and EIP-3860. The numbers change with forks, and the forks and block heights marked in this piece are: Istanbul (December 8, 2019, block 9,069,000), Berlin (April 15, 2021, block 12,244,000), London (August 5, 2021, block 12,965,000), Shanghai (April 12, 2023, block 17,034,870), and Cancun (March 13, 2024, block 19,426,587). Before discussing gas on an EVM-compatible chain, confirm which forks it has enabled; its pricing constants may differ from Ethereum mainnet's. Tangerine Whistle's block 2,463,000 and October 18, 2016 likewise come from ethereum.org's upgrade history.

On how severe the “pricing lag” is, only circumstantial evidence is available and no quantitative conclusion: no public material keeps a time series tracking the ratio of protocol gas constants to actual execution time, so there is no way to tell whether the lag is widening or closing. This should be kept as an open item, not used to infer back to conclusions such as “it is getting worse” or “it has converged”.

In addition, fee-market behavior is affected by MEV and client strategies; this piece does not go into the mechanical details of the ordering market, nor does it compare the blob fee market and execution-layer gas in one framework, since after EIP-4844 they are separate pricing systems. When citing these numbers for a cost model, confirm the basis and version first.

## Sources

- EIP-1559: Fee market change for ETH 1.0 chain, https://eips.ethereum.org/EIPS/eip-1559
- EIP-2929: Gas cost increases for state access opcodes, https://eips.ethereum.org/EIPS/eip-2929
- EIP-3529: Reduction in refunds, https://eips.ethereum.org/EIPS/eip-3529
- EIP-150: Gas cost changes for IO-heavy operations, https://eips.ethereum.org/EIPS/eip-150
- EIP-1884: Repricing for trie-size-dependent opcodes, https://eips.ethereum.org/EIPS/eip-1884
- EIP-2028: Transaction data gas cost reduction, https://eips.ethereum.org/EIPS/eip-2028
- EIP-2200: Structured Definitions for Net Gas Metering, https://eips.ethereum.org/EIPS/eip-2200
- EIP-2930: Optional access lists, https://eips.ethereum.org/EIPS/eip-2930
- EIP-3860: Limit and meter initcode, https://eips.ethereum.org/EIPS/eip-3860
- EIP-4844: Shard Blob Transactions, https://eips.ethereum.org/EIPS/eip-4844
- EIP-7516: BLOBBASEFEE opcode, https://eips.ethereum.org/EIPS/eip-7516
- Ethereum Yellow Paper, appendix G fee schedule, equation (64) for the intrinsic cost, and equation (328) for the memory pricing function, https://ethereum.github.io/yellowpaper/paper.pdf
- ethereum.org developer docs: Gas and fees, https://ethereum.org/en/developers/docs/gas/
- ethereum.org opcode reference, https://ethereum.org/en/developers/docs/evm/opcodes/
- ethereum.org network upgrade history (block heights and dates for each fork), https://ethereum.org/en/history/
- arXiv:1909.07220, the state-access timing measurement cited by EIP-2929, https://arxiv.org/abs/1909.07220

## Further reading

- ["A Glossary of Performance Metrics: TPS, BPS, Confirmation Latency, Finality, and Conflict Rate"](/en/blog/performance-metrics-glossary)
- ["Mapping Blockchain Scaling: What L1 Parallelism, L2s, Sharding, and DA Each Solve"](/en/blog/blockchain-scaling-map)
- ["The Tension Between Decentralization and Performance: Validator Requirements, Hardware, and Geographic Distribution"](/en/blog/decentralization-performance-tradeoff)

This piece belongs to the EVM fundamentals series. Its prerequisite is 0.5 “Three Kinds of Storage, Don't Mix Them Up: Memory, Storage, and Transient Storage”.
