Press ESC to close

EIP-4844 & Ethereum Blobs: Lower Costs & Cheap Data Storage

March 2024 permanently reshaped layer-2 economics. Before Dencun, rollups like Arbitrum and Optimism were burning through team budgets just to push calldata onto mainnet. Ethereum smart contracts permanently slammed those byte arrays into global state, forcing every single validator to store them forever. This accounted for up to ninety percent of L2 user costs.

EIP-4844 broke this paradigm by introducing type-3 transactions and blobs. But behind those shiny numbers boasting 10x to 50x fee reductions lies a mountain of engineering nuance that marketers usually gloss over.

How blobs work at the memory and consensus levels

A blob is not just a file sent to the blockchain. It is an isolated data array of exactly 131,072 bytes (128 KB) that exists parallel to EVM execution. Ethereum smart contracts cannot read a blob's contents inside a CALL or STATICCALL. They only have access to a single piece of bytecode, the special BLOBHASH opcode, which returns the version and commitment hash.

  • Block size: the target is 3 blobs (~384 KB), with a peak maximum of 6 blobs (~768 KB).
  • Lifespan: about 4096 epochs (roughly 18 days), after which consensus nodes prune the data from their disks.
  • Mempool isolation: blobs do not circulate in the standard execution mempool. They have their own pool at the consensus client level (P2P blob pool), protecting core transaction queues from overload.

Parameter | Traditional calldata | EIP-4844 Blob (Type 3)
Where data lives: In permanent EVM state | In the temporary consensus P2P pool
Retention time: Forever | ~18 days
EVM access: Direct (via byte reading) | Indirect (only via hash and KZG)
Pricing: Shared execution gas | Independent blob gas market (EIP-1559)

Under-the-hood features and architectural traps of proto-danksharding

Few people talk about how blob gas pricing actually works. Unlike standard gas, which spikes with every DeFi hiccup, the blob market is governed by an independent adjustment algorithm. If rollups pack slots above the target of three blobs, the price starts growing exponentially. If slots sit empty, it drops to the floor. In practice, this means sending a blob during off-peak hours costs a tiny fraction of a cent, but during network surges, prices can jump hundreds of times faster than in the execution layer.

Another subtle detail is cryptographic verification via KZG commitments (Kate-Zaverucha-Goldberg). Validators do not need to download the entire 128 KB to confirm data validity. They just need to check a 48-byte polynomial. This lowers validator CPU requirements while shifting the heavy lifting onto the network bandwidth.

Where to dump "awkward" data in practice

The temporary nature of blobs makes them the ideal container for data that does not need eternal bookkeeping, but requires cryptographic immutability over a short validation window:

  • Oracle dumps and raw metrics: intermediate data arrays signed by providers before aggregation into a smart contract.
  • L3 state snapshots and gaming worlds: state delta changes that only need to prove correctness for the past week.
  • Backend audit logs: transaction bundles or receipts that need to be publicly verifiable but lose operational value after a month.

Pushing payloads into a blob via web3.py Python scripts requires packaging bytes into polynomials separately and generating a versioned hash with the 0x01 prefix. Miscalculating maxFeePerBlobGas at submission guarantees your transaction will hang in the mempool.

The theory and economics make sense, but what does working with blobs actually look like at the code level? Using a standard send_transaction call will throw an error since type-3 transactions require manually prepping KZG commitments, generating proofs, and figuring out a separate gas limit.

Sending a Type-3 Transaction in Python

Here is a working pattern for shipping a blob using up-to-date node libraries (like Geth or Reth) that support the Dencun upgrade:

import os
from eth_account import Account
from web3 import Web3
w3 = Web3(Web3.HTTPProvider("http://127.0.0.1:8545"))
key = os.environ["OPERATOR_KEY"]
acct = Account.from_key(key)
FE_COUNT, FE_PAYLOAD = 4096, 31  # 31 bytes of data per field element
def encode_blob(data: bytes) -> bytes:
    data = len(data).to_bytes(4, "big") + data
    if len(data) > FE_COUNT * FE_PAYLOAD:
        raise ValueError("payload doesn't fit in a single blob")
    data = data.ljust(FE_COUNT * FE_PAYLOAD, b"\x00")
    # high byte of each element = 0 to keep the value < BLS modulus
    return b"".join(b"\x00" + data[i:i + FE_PAYLOAD]
                    for i in range(0, len(data), FE_PAYLOAD))
blob = encode_blob(b"EXMON Infrastructure DA Payload: batch #98412")
# Current blob base fee with a buffer for spikes
blob_base_fee = int(w3.provider.make_request("eth_blobBaseFee", [])["result"], 16)
base_fee = w3.eth.get_block("latest")["baseFeePerGas"]
tx = {
    "type": 3,
    "chainId": w3.eth.chain_id,
    "nonce": w3.eth.get_transaction_count(acct.address),
    "gas": 21000,
    "maxPriorityFeePerGas": w3.to_wei(1, "gwei"),
    "maxFeePerGas": base_fee * 2 + w3.to_wei(1, "gwei"),
    "maxFeePerBlobGas": max(blob_base_fee * 2, w3.to_wei(1, "gwei")),
    "to": acct.address,  # not contract creation: `to` is mandatory for blob-tx
    "value": 0,
    "data": b"",
}
# ckzg computes the commitment, proofs, and versioned hash; sidecar gets injected into raw tx
signed = Account.sign_transaction(tx, key, blobs=[blob])
tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
print(f"Blob-tx sent: {tx_hash.to_0x_hex()}")

If you fail to account for maxFeePerBlobGas volatility during rollup peak hours, your transaction will get stuck in the consensus mempool until the market cools down.

The Archive Node Problem: Where Data Goes After 18 Days

The major trap when designing blob-based architectures comes down to pruning. Consensus clients store blob bodies for at least 4096 epochs (roughly 18 days) before dropping them. The transaction itself and the KZG commitment stay on-chain forever, but you can't recover the blob's actual contents from them. If you need to fetch blob data a month later, a standard node will return an error.

Teams building products on top of EIP-4844 have to tackle this problem in one of three ways:

  • Custom archive scrapers: Spinning up background services that pull blobs out of the P2P network the moment they hit and stash them in cold storage (S3-compatible buckets).
  • Decentralized archives: Leveraging networks like Celestia, Avail, or The Graph for long-term Data Availability storage.
  • Dropping deep history: Storing local indexes only for the active two-week window while dumping older aggregates in compressed form.

EXMON Practice: Storage Optimization

Within the EXMON infrastructure, we use temporary blob namespaces to offload heavy intermediate transaction logs, service dumps, and verification snapshots.

This lets us hit two targets at once:

  • Budget savings: Data publishing costs dropped by an order of magnitude compared to legacy calldata methods.
  • Blockchain hygiene: The global network state doesn't get clogged up with garbage that loses relevance within a week, while 100% cryptographic verifiability is maintained across the data's lifecycle window.
Summarize this blog post with:

FAQ

EIP-4844 introduces proto-danksharding through type-3 transactions that carry temporary data containers called blobs, bypassing permanent execution state storage. Each blob contains exactly 128 KB of data handled exclusively at the consensus client P2P layer, validated via KZG commitments without direct execution access from EVM smart contracts.

Blobs persist on Ethereum consensus nodes for approximately 18 days, specifically 4096 epochs, before being permanently pruned from local disk storage. Because validator nodes discard blob bodies after this window, long-term historical data retrieval requires external decentralized indexers or custom archival scrapers rather than native L1 nodes.

Sending a blob transaction in Python requires constructing a type-3 transaction dictionary via web3.py containing a separate maxFeePerBlobGas parameter and a blobVersionedHashes array. Developers must sign the transaction using an operator private key after packaging the payload into KZG commitment hashes for transmission into the P2P consensus memory pool.
Astra EXMON

Astra is the official voice of EXMON and the editorial collective dedicated to bringing you the most timely and accurate information from the crypto market. Astra represents the combined expertise of our internal analysts, product managers, and blockchain engineers.

...

Leave a comment

Your email address will not be published. Required fields are marked *