Transaction Types: Legacy to Blob
In Anatomy of a Transaction we laid out the fields a transaction carries, and in Signing & Replay Protection we saw how a signature turns those fields into a self-authorizing request — one whose from address is recovered from the signature, not written. That gave us one transaction shape. But Ethereum has shipped several, and each addition changed how a transaction is priced or what it can carry.
This page answers a question that looks cosmetic and is actually load-bearing: how do you add a brand-new transaction format to a live chain that thousands of independent nodes must all agree on — without breaking every node that only knows the old format? Get that wrong and every fee-market or scaling upgrade becomes a chain split. The answer is a small, disciplined idea called the typed-transaction envelope, and once it existed, four distinct formats grew inside it. Each one is best understood not as a random schema change but as the answer to a specific cost or scaling problem.
The problem: you cannot version a format you never versioned
Section titled “The problem: you cannot version a format you never versioned”The original Ethereum transaction was an RLP-encoded list of nine fields — nonce, gasPrice, gasLimit, to, value, data, and the three signature parts v, r, s. RLP (Recursive Length Prefix) is Ethereum’s serialization scheme: it encodes a list, and a decoder reads the list back. There was no version number anywhere. The shape of the list — nine items, in that order — was the format.
That is fine until you want to change it. Suppose you want to add a tenth field. An old node decoding a ten-item list either rejects it (it expected nine) or, worse, silently misreads it. You cannot tell “a new kind of transaction” apart from “a corrupt old transaction,” because there is no field that says which kind this is. The format has no room to grow, and every attempt to grow it is indistinguishable from garbage to software that hasn’t upgraded.
This is the same lesson every long-lived wire protocol learns: reserve a way to say “what version is this” before you need it, or retrofitting one is a hard fork. Ethereum retrofitted it. The retrofit is EIP-2718.
EIP-2718: the typed-transaction envelope
Section titled “EIP-2718: the typed-transaction envelope”EIP-2718 (the “Typed Transaction Envelope,” live since the Berlin upgrade of 2021) defines exactly one new rule: a transaction may be a single type byte followed by a payload whose meaning is defined by that type byte.
Legacy transaction (pre-2718, still valid forever): ┌──────────────────────────────────────────────┐ │ RLP([nonce, gasPrice, gasLimit, to, value, │ ← first byte is ≥ 0xc0 │ data, v, r, s]) │ (an RLP list header) └──────────────────────────────────────────────┘
Typed transaction (EIP-2718): ┌──────┬───────────────────────────────────────┐ │ 0x01 │ payload defined by type 0x01 │ ← first byte is 0x00..0x7f ├──────┼───────────────────────────────────────┤ │ 0x02 │ payload defined by type 0x02 │ ├──────┼───────────────────────────────────────┤ │ 0x03 │ payload defined by type 0x03 │ └──────┴───────────────────────────────────────┘ │ the leading byte says "how to read the rest"The trick that makes this backward-compatible is a byte-range disjointness. A legacy transaction always begins with an RLP list header, which for any real transaction is a byte ≥ 0xc0. EIP-2718 reserves the range 0x00–0x7f for type bytes. Those ranges never overlap, so a decoder can look at the very first byte and know instantly:
- first byte
≥ 0xc0→ this is a legacy transaction, decode the old way; - first byte in
0x00–0x7f→ this is a typed transaction of that type, decode according to that type’s rules.
That is the whole envelope. Its payoff is enormous: new transaction formats can now be added by assigning a new type byte, and any node that doesn’t recognise the byte can cleanly reject it rather than misread it. Every later type on this page — access lists, the fee market, blobs — is just “reserve a byte, define its payload.” The hard part, done once, was making room to grow.
The shape it enforces on every new type
Section titled “The shape it enforces on every new type”Because the type byte gates decoding, each new type gets to define its own field list from scratch. In practice they share most fields with legacy (a nonce, a gas limit, a to, a value, a data, a signature) and add or replace only what the new feature needs. This is why our companion crate ethmini models a transaction as a from, a nonce, and a kind enum: the “kind” is the miniature stand-in for the type byte — the tag that says how to interpret the rest.
/// `ethmini`'s transaction: a sender, a nonce, and a *kind* tag that says how/// to interpret the payload — a teaching stand-in for EIP-2718's type byte.pub struct Transaction { pub from: Address, pub nonce: u64, pub kind: TxKind, // Transfer | Deploy | Call — one tag, several payloads}Real Ethereum’s type byte plays the same role at the encoding layer that TxKind plays at the semantic layer: a single leading discriminant that tells the reader which of several payload shapes follows. Hold that analogy — one tag, several shapes — and the four formats below stop being trivia and become variants of one enum.
Type 0 — Legacy: one price, a blind auction
Section titled “Type 0 — Legacy: one price, a blind auction”The legacy transaction prices gas with a single field: gasPrice, the number of wei you pay per unit of gas consumed. Your total fee is gasUsed × gasPrice, and the entire amount goes to whoever includes your transaction (historically a miner, now a block proposer).
The problem legacy pricing has is that it is a first-price auction under uncertainty. Blockspace each block is scarce; the proposer greedily picks the highest-gasPrice transactions. So to get included you must guess a price high enough to beat the others competing this block — but you cannot see their bids, and the “right” price swings with demand you can’t observe.
First-price sealed-bid auction (legacy gasPrice): you bid 30 gwei, hoping it clears clearing price turns out to be 12 gwei → you overpaid 18 gwei clearing price turns out to be 45 gwei → you don't get in at all
Everyone overbids to be safe, or underbids and gets stuck. The UX is a guess.Wallets papered over this with fee estimators, but the underlying mechanism was hostile: users routinely overpaid to avoid getting stuck, or set a price that was fine yesterday and stranded today. Legacy transactions are still fully valid — the envelope guarantees it — but almost nothing sends them anymore, because a better auction exists.
Type 1 — EIP-2930: access lists
Section titled “Type 1 — EIP-2930: access lists”EIP-2930 (Berlin, 2021) was the first type to use the new envelope — type byte 0x01. It solved a narrow but real problem created by a security change in the same upgrade.
Berlin made accessing “cold” state — an account or storage slot your transaction touches for the first time in that transaction — more expensive than accessing it again (“warm”). This priced in the real cost: the first touch forces a node to go fetch that state from disk; later touches hit a cache. Good for pricing, but it introduced uncertainty — a call that unexpectedly touched new state could cost more gas than estimated and run out.
An access list lets a transaction pre-declare the addresses and storage slots it will touch:
accessList: [ { address: 0xTokenContract, storageKeys: [ 0x00…balances_slot, 0x00…allowance_slot ] }, { address: 0xRouter, storageKeys: [ ] } ]Declaring an access entry does two things. First, the listed items are treated as warm from the start, so the in-transaction access is cheaper. Second — and this is the point — you pay a small, fixed, up-front gas cost for each declared address and slot, in exchange for making the later accesses cheaper and predictable. For a contract call that touches many known slots, pre-warming them can net out cheaper, and it removes the “cold access blew my gas estimate” failure mode.
Access lists are optional and, in practice, only worth it for specific heavy contract interactions. But EIP-2930 mattered more as a proof: it was the first live use of the typed envelope, demonstrating that a new format could ship without disturbing legacy transactions at all.
Type 2 — EIP-1559: a base fee that’s burned, plus a tip
Section titled “Type 2 — EIP-1559: a base fee that’s burned, plus a tip”EIP-1559 (the London upgrade, 5 August 2021, type byte 0x02) replaced the blind first-price auction with a mechanism the protocol computes for you. It is now the default transaction type. Instead of one gasPrice, a type-2 transaction carries two fields:
maxFeePerGas— the most you are willing to pay per gas, all-in.maxPriorityFeePerGas— the most of that you’ll hand to the proposer as a tip, on top of the base fee.
The protocol maintains a base fee per block: a price it sets algorithmically from how full recent blocks were. If the previous block was more than half full, the base fee rises; if less than half full, it falls — a feedback loop that steers demand toward a target of roughly 50% block fullness. The base fee is not a bid you make; it is a posted price the protocol charges everyone in the block.
What you actually pay per gas under EIP-1559:
effectiveGasPrice = baseFee + min(maxPriorityFeePerGas, maxFeePerGas − baseFee)
├─ baseFee ─────────────► BURNED (destroyed, removed from supply) └─ priority tip ────────► goes to the block PROPOSER
Your refund: you set maxFeePerGas high as a cap; you're only charged baseFee + your tip. Overpaying the cap does NOT mean overpaying the price.Two design choices make this work. First, the base fee is burned — permanently destroyed — not paid to the proposer. That removes the proposer’s incentive to secretly inflate the base fee, and it means the fee you must pay reflects genuine demand rather than a counterparty’s bid. Second, you set maxFeePerGas as a cap you’ll tolerate but only ever pay the actual baseFee plus your tip, with the difference refunded. So the “guess a price and pray” of legacy becomes “set a ceiling, pay the posted price.” The auction didn’t get faster — it got replaced by a posted-price market with a tip lane for when you want to jump the queue.
Type 3 — EIP-4844: blob transactions and a second gas market
Section titled “Type 3 — EIP-4844: blob transactions and a second gas market”EIP-4844 (“proto-danksharding,” the Dencun upgrade of 13 March 2024, type byte 0x03) is the most recent type, and it exists to solve a scaling problem, not a pricing-fairness one. It is covered in depth in EIP-4844 — Proto-Danksharding and Blobs; here we place it as a transaction type.
The problem: rollups (see The Rollup Pattern) execute transactions off-chain and post their compressed data back to Ethereum so anyone can verify and reconstruct the rollup’s state. Before 4844, that data went into ordinary transaction calldata — and calldata is stored by every node forever and priced in the same gas market as everything else. Rollups were paying permanent-storage prices for data that only needs to be available for a short window, and competing head-to-head with ordinary transactions for the same blockspace.
A blob transaction attaches one or more blobs — large (~128 KB each), fixed-size chunks of data — that ride alongside the transaction but are not part of the EVM’s accessible state. Three things make blobs cheap where calldata was expensive:
┌─ blob transaction (type 0x03) ────────────────────────────────┐ │ normal fields (nonce, to, value, EIP-1559 fees, signature) │ │ + KZG commitments ── a short cryptographic fingerprint of │ │ each blob, stored on-chain forever │ └────────────────────────────────────────────────────────────────┘ │ references ▼ ┌─ blobs (carried by consensus layer, NOT in EVM state) ────────┐ │ ~128 KB each, priced in a SEPARATE blob-gas market, │ │ PRUNED by nodes after ~18 days (≈4096 epochs) │ └────────────────────────────────────────────────────────────────┘- A separate fee market. Blobs are priced in their own unit — blob gas — with its own base fee that follows the same EIP-1559-style ±up/down feedback, independent of the main gas market. A calldata surge no longer prices out rollups’ data, and vice versa. Two markets, two demand curves, no cross-contention.
- Referenced by KZG commitments, not stored inline. The transaction on-chain carries only a short KZG commitment — a cryptographic fingerprint of each blob — while the blob bytes themselves are carried by the consensus layer. Anyone can later prove a piece of blob data matches its commitment without the chain having to hold the whole blob in EVM-accessible state.
- Dropped after ~18 days. Blobs are pruned by nodes after roughly 18 days (4096 epochs). This is the crux: rollup data only needs to be available long enough for anyone to download it and challenge or reconstruct — not forever. By explicitly making blob storage temporary, 4844 stops charging permanent-storage prices for temporary-availability data.
The result, observed after Dencun in March 2024, was a sharp drop in rollup transaction fees. The mechanism is the lesson: a new type let Ethereum introduce a whole new resource (blob space) with its own price, without touching how ordinary transactions or the EVM work.
The four types, as answers to four questions
Section titled “The four types, as answers to four questions” Type Name Introduced The problem it answers ──── ───────── ────────── ────────────────────────────────────────── — Legacy genesis (the original; one gasPrice, blind auction) 0x01 2930 Berlin '21 cold-access uncertainty → pre-declare state, warm it, make gas predictable 0x02 1559 London '21 blind first-price auction → posted base fee (burned) + tip; set a cap, pay the price 0x03 4844 Dencun '24 rollups pay forever-storage for temporary data → cheap, separately-priced, pruned blobsRead the table top to bottom and the envelope earns its keep: each row is a byte plus a payload, added without breaking the row above it. That is EIP-2718 doing its one job, over and over.
The architect’s lens
Section titled “The architect’s lens”The major technology on this page is the typed-transaction envelope (EIP-2718) — the thing that makes every other row possible.
- Why does it exist? Because Ethereum’s original transaction was an unversioned RLP list with no field that said “what kind am I,” so it could not be extended without a decoder being unable to tell a new format from corruption.
- What problem does it solve? It lets the protocol add entirely new transaction formats — new pricing, new payloads, new resources like blobs — by reserving a leading type byte, so upgrades stop being all-or-nothing rewrites of the wire format.
- What are the trade-offs? More formats mean more code paths every client must implement and keep correct, and more surface for subtle inconsistencies between clients. The envelope buys extensibility at the cost of a growing menagerie of types to support forever.
- When should I avoid it? You don’t opt out of the envelope — but as a user you should not reach for a niche type (like an EIP-2930 access list) unless it measurably helps; for most transfers, the default type-2 fee-market transaction is the right and cheapest choice.
- What breaks if I remove it? Without the type byte, every future fee or scaling change — EIP-1559’s posted price, EIP-4844’s blobs, and anything after them — becomes a hard fork that risks splitting the chain, because old nodes could not distinguish a new format from a malformed old one.
Check your understanding
Section titled “Check your understanding”- Ethereum’s legacy transaction had no version field. Explain concretely why that made adding a new transaction format impossible without EIP-2718, and how the type byte fixes it.
- What byte-range property lets a decoder tell a legacy transaction from a typed one just by looking at the first byte?
- EIP-1559 replaced a single
gasPricewith two fields. Name them, say what the base fee is, and explain what happens to the base fee versus the tip. - What specific problem did EIP-2930 access lists address, and what do you pay and gain by declaring one?
- Why are EIP-4844 blobs cheaper than putting the same data in calldata? Give the three mechanisms — separate market, commitment reference, and pruning.
Show answers
- A legacy transaction was just an RLP list of nine fields with no field naming its format, so a decoder could not distinguish “a new kind of transaction with extra fields” from “a corrupt old one” — any format change was indistinguishable from garbage. EIP-2718 prepends a type byte that says how to decode the rest, so new formats can be added by assigning a new byte, and unrecognised bytes are cleanly rejected rather than misread.
- A legacy transaction starts with an RLP list header (a byte
≥ 0xc0); EIP-2718 reserves type bytes to the range0x00–0x7f. The two ranges are disjoint, so the first byte alone tells the decoder which it is. maxFeePerGas(the all-in ceiling you’ll tolerate) andmaxPriorityFeePerGas(the tip). The base fee is a per-block price the protocol sets algorithmically from recent block fullness (±12.5%/block, targeting ~50% full). The base fee is burned (destroyed); the tip goes to the block proposer. You’re chargedbaseFee + tip, with anything under your cap refunded.- Berlin made first (“cold”) access to an account or storage slot more expensive than repeat (“warm”) access, which introduced gas-estimate uncertainty. An access list pre-declares the addresses/slots a transaction will touch: you pay a small fixed up-front cost per entry and gain warm (cheaper) access to them plus predictable gas — no “cold access blew my estimate” surprise.
- Separate market: blobs are priced in their own blob-gas unit with an independent base fee, so they don’t compete with ordinary transactions. Commitment reference: the chain stores only a short KZG commitment (fingerprint), not the blob bytes, so blobs never enter EVM-accessible state. Pruning: blobs are dropped after ~18 days, so rollups pay temporary-availability prices instead of the permanent-storage prices that calldata incurs.