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.