Logo
New RPC users get 35% off their first monthView the offer
OnFinality Learn
Network & Protocol Guides14 min read

Ethereum Blob Transactions over RPC: EIP-4844 Type-3 Mechanics

A practical RPC-layer guide to sending, reading, and reconciling EIP-4844 type-3 blob transactions, including blob gas accounting and sidecar availability.

TL;DR

An EIP-4844 blob transaction is a type-0x03 envelope that carries one or more blobs, but the blob data itself is not part of the execution payload; the transaction commits to it via versioned hashes (KZG commitments) and the receipt records blobGasUsed and blobGasPrice. Blobs are priced by an independent blob base fee updated through an excess-blob-gas mechanism, so execution gas and blob gas can be cheap or expensive independently. Reading a blob transaction over JSON-RPC requires eth_getTransactionByHash for blobVersionedHashes and the receipt for blobGasUsed and blobGasPrice; the sidecar (blobs, commitments, proofs) travels with the raw transaction but is not stored on chain and is pruned after a retention window. This article shows how to send and verify a type-3 transaction against a single endpoint, compute the total blob fee explicitly, and distinguish provider-specific sidecar availability from protocol-level retention. It also covers troubleshooting for stripped sidecars, incomplete receipts, pre-Dencun nodes, and fee estimators that omit the blob dimension.

Type-3 Transaction Envelope and Blob Commitment Mechanics

An EIP-4844 blob transaction is a normal transaction envelope with a new type identifier, 0x03, that carries one or more blobs. The blob data itself is not part of the execution payload; instead, the transaction commits to the blob via a versioned hash, which is a KZG commitment to the blob's polynomial. This means an RPC caller reading only execution fields cannot tell whether a blob was posted or how much it cost. The authoritative specification is EIP-4844: Shard Blob Transactions, and the JSON-RPC method semantics are defined in the Ethereum execution-apis specification.

The type-3 fields a reader must recognise are maxFeePerBlobGas, blobVersionedHashes, and the sidecar (blobs, commitments, proofs) that travels with the raw transaction but is not stored on chain. The sidecar is propagated and retained by the consensus layer and pruned after a bounded window rather than kept as part of Ethereum state. Consequently, a JSON-RPC read of a blob's contents is an availability query that can legitimately fail for an old block while the block itself is perfectly queryable. You must distinguish 'this endpoint does not serve blob sidecars' from 'this blob has aged out'.

Because the blob data is not in the execution payload, the block commits to data it does not store. This is why blobVersionedHashes rather than the blob bytes are what survives in the block. For a broader view of how OnFinality exposes Ethereum networks, see /en/networks/eth.

  • Type 0x03 envelope: execution fields plus blob-specific fields.
  • blobVersionedHashes: one per blob, derived from KZG commitments.
  • Sidecar: blobs, commitments, proofs — not stored on chain.
  • Receipt: blobGasUsed and blobGasPrice record the blob fee actually paid.

Two-Dimensional Fee Market: Execution Gas vs Blob Gas

Execution gas is priced by baseFee plus a priority tip, while blobs are priced by an independent blob base fee that is updated by its own excess-blob-gas mechanism. This means a transaction can be cheap in execution gas and expensive in blob gas at the same time. A fee estimator that only reads eth_feeHistory's baseFeePerGas will under-price or drop the blob fee entirely. For execution fee estimation, see Ethereum fee history and fee estimation.

The blob fee computation is explicit: total blob fee equals blobGasUsed multiplied by blobGasPrice, and blobGasUsed equals the number of blobs multiplied by the protocol constant GAS_PER_BLOB (131072). You should verify this against your own receipt rather than hard-coding. The blob base fee is not returned by eth_feeHistory in all implementations; some providers expose it via eth_blobBaseFee or include it in block headers as blobBaseFee. This behavior is documented / varies by provider.

When sending a type-3 transaction, you must set maxFeePerBlobGas in addition to maxFeePerGas and maxPriorityFeePerGas. If maxFeePerBlobGas is too low, the transaction will be rejected or stuck. The blob fee market is separate, so a spike in blob demand does not necessarily affect execution gas prices.

  • Execution gas: baseFee + priority tip.
  • Blob gas: independent blob base fee from excess blob gas.
  • GAS_PER_BLOB = 131072 (protocol constant).
  • maxFeePerBlobGas is required for type-3 transactions.

Sending a Type-3 Blob Transaction over JSON-RPC

To send a blob transaction, you construct a type-3 transaction with the blob sidecar and submit it via eth_sendRawTransaction. The raw transaction must include the blobs, commitments, and proofs. The node will validate the KZG commitments and propagate the sidecar to the consensus layer. The execution-apis specification defines eth_sendRawTransaction as accepting a signed raw transaction and returning the transaction hash. For a production endpoint, see /en/networks/eth.

Below is a runnable Node.js script that sends a type-3 transaction using ethers.js. It assumes you have a provider URL and a signer with funds. It constructs a blob transaction with one blob, sets maxFeePerBlobGas, and sends it. The script prints the transaction hash and then polls for the receipt to show blobGasUsed and blobGasPrice.

Note that the sidecar is not stored on chain; the script only sends it. After sending, you can verify the transaction by hash using eth_getTransactionByHash and eth_getTransactionReceipt. The receipt will contain blobGasUsed and blobGasPrice, which you can use to compute the total blob fee.

const { ethers } = require('ethers');

async function main() {
  const provider = new ethers.JsonRpcProvider(process.env.RPC_URL);
  const wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider);

  // Example blob data (32 bytes)
  const blobData = '0x' + '00'.repeat(32);

  const tx = {
    to: wallet.address,
    value: 0,
    maxFeePerGas: ethers.parseUnits('10', 'gwei'),
    maxPriorityFeePerGas: ethers.parseUnits('1', 'gwei'),
    maxFeePerBlobGas: ethers.parseUnits('10', 'gwei'),
    blobs: [{ data: blobData }],
    kzg: undefined // ethers v6 handles KZG via provider
  };

  const sent = await wallet.sendTransaction(tx);
  console.log('Transaction hash:', sent.hash);

  const receipt = await sent.wait();
  console.log('blobGasUsed:', receipt.blobGasUsed.toString());
  console.log('blobGasPrice:', receipt.blobGasPrice.toString());
  const totalBlobFee = receipt.blobGasUsed * receipt.blobGasPrice;
  console.log('Total blob fee (wei):', totalBlobFee.toString());
}

main().catch(console.error);

Reading and Reconciling a Type-3 Transaction by Hash

The read path that actually works is: retrieve the transaction by hash with eth_getTransactionByHash to obtain blobVersionedHashes and blobGasUsed/blobGasPrice, then retrieve the receipt and read blobGasUsed and blobGasPrice to compute the blob fee actually paid. You should also check whether the endpoint exposes a blob-sidecar method at all; this is documented / varies by provider. Some providers offer eth_getBlobSidecar or eth_getBlobs, but these are not part of the core execution-apis specification.

Below is a runnable Node.js script that takes a known type-3 transaction hash, fetches the transaction and receipt, classifies the transaction type, extracts the versioned hashes, and prints a table of versioned hashes, blob count, blobGasUsed, blobGasPrice, derived total blob fee, and execution fee side by side. It uses only standard JSON-RPC methods.

The script uses eth_getTransactionByHash and eth_getTransactionReceipt. It computes the execution fee as gasUsed multiplied by effectiveGasPrice. It also checks for the presence of blobVersionedHashes and blobGasUsed to confirm the transaction is type-3. If the provider strips the sidecar fields, the script will still work because it only reads on-chain fields.

const { ethers } = require('ethers');

async function inspectBlobTx(txHash) {
  const provider = new ethers.JsonRpcProvider(process.env.RPC_URL);

  const tx = await provider.getTransaction(txHash);
  if (!tx) throw new Error('Transaction not found');

  const receipt = await provider.getTransactionReceipt(txHash);
  if (!receipt) throw new Error('Receipt not found');

  const type = tx.type;
  console.log('Transaction type:', type);

  const versionedHashes = tx.blobVersionedHashes || [];
  const blobCount = versionedHashes.length;
  const blobGasUsed = receipt.blobGasUsed ? receipt.blobGasUsed.toString() : 'N/A';
  const blobGasPrice = receipt.blobGasPrice ? receipt.blobGasPrice.toString() : 'N/A';

  let totalBlobFee = 'N/A';
  if (receipt.blobGasUsed && receipt.blobGasPrice) {
    totalBlobFee = (receipt.blobGasUsed * receipt.blobGasPrice).toString();
  }

  const executionFee = (receipt.gasUsed * receipt.effectiveGasPrice).toString();

  console.log('\n--- Blob Transaction Report ---');
  console.log('Versioned Hashes:');
  versionedHashes.forEach((h, i) => console.log(`  ${i}: ${h}`));
  console.log('Blob Count:', blobCount);
  console.log('blobGasUsed:', blobGasUsed);
  console.log('blobGasPrice:', blobGasPrice);
  console.log('Total Blob Fee (wei):', totalBlobFee);
  console.log('Execution Fee (wei):', executionFee);
}

inspectBlobTx(process.argv[2]).catch(console.error);

Blob Fee Computation and Verification Against Your Receipt

The total blob fee is computed as blobGasUsed multiplied by blobGasPrice. blobGasUsed is the number of blobs multiplied by GAS_PER_BLOB (131072). You should verify this against your own receipt rather than hard-coding. For example, if your receipt shows blobGasUsed = 131072 and blobGasPrice = 1000000000 wei, the total blob fee is 131072 * 1000000000 = 131072000000000 wei. This is separate from the execution fee, which is gasUsed multiplied by effectiveGasPrice.

To verify, fetch the receipt and check that blobGasUsed equals the number of versioned hashes times 131072. If it does not, the provider may be returning incomplete data. Also check that blobGasPrice is not null. Some providers return blobGasUsed but omit blobGasPrice, which indicates incomplete indexing. This is a provider-specific issue, not a protocol one.

The blob base fee is updated based on excess blob gas, which is a function of the total blob gas used in previous blocks relative to a target. The exact formula is in EIP-4844. You can read the current blob base fee from the latest block header if the provider exposes it, or use eth_blobBaseFee if available. Again, this varies by provider.

  • Total blob fee = blobGasUsed * blobGasPrice.
  • blobGasUsed = blobCount * 131072.
  • Verify blobGasUsed against versioned hashes count.
  • Check blobGasPrice is not null; if null, provider indexing is incomplete.

KZG Commitments and Point-Evaluation Verification

A KZG commitment is a polynomial commitment that lets a verifier check that a blob's data matches a small commitment without holding the blob. This is why the block can commit to data it does not store. The commitment is a point on an elliptic curve, and the versioned hash is derived from it. The versioned hash is what appears in the transaction and block.

Point-evaluation verification is a cryptographic operation the reader performs on data obtained out of band rather than a JSON-RPC method. To verify a blob, you need the blob data, the commitment, and a proof. You can obtain these from the sidecar if available, or from a consensus-layer node. The verification itself uses the KZG library, not JSON-RPC. The execution-apis specification does not define a method for point evaluation.

If you need to verify blob data, you must fetch the sidecar from a source that retains it. The sidecar is pruned after a retention window, so for old blobs you may need to rely on archival consensus-layer data. This is a retention-window question rather than a permanent guarantee.

  • KZG commitment: small commitment to blob polynomial.
  • Versioned hash: derived from commitment, stored in transaction.
  • Point evaluation: cryptographic verification, not JSON-RPC.
  • Sidecar retention: bounded window, not permanent.

Results Table: Endpoint Behaviour for Type-3 Fields and Sidecar Methods

Use the following table to record your own endpoint's behaviour. Fill in the values by running the inspection script against a known type-3 transaction hash. This will help you distinguish provider-specific limitations from protocol-level behaviour.

For each row, note whether the field is present, null, or absent. For sidecar methods, check if the endpoint supports eth_getBlobSidecar, eth_getBlobs, or similar. Document the results for your provider.

  • Transaction type: 0x03 present?
  • blobVersionedHashes: present? count?
  • blobGasUsed in receipt: present? value?
  • blobGasPrice in receipt: present? value?
  • Sidecar method (eth_getBlobSidecar): supported?
  • Blob base fee method (eth_blobBaseFee): supported?

Troubleshooting Common Type-3 RPC Failures

A provider that returns type-3 transactions with the sidecar fields stripped is common. The transaction will still have blobVersionedHashes, but the blobs, commitments, and proofs will be missing. This is expected because the sidecar is not stored on chain. If you need the sidecar, you must query a consensus-layer node or a provider that explicitly serves blob sidecars. This is documented / varies by provider.

A receipt whose blobGasUsed is present but whose blobGasPrice is null indicates the provider's indexing is incomplete. This can happen if the provider has not fully implemented EIP-4844 receipt fields. In this case, you cannot compute the blob fee from the receipt alone. You may need to derive it from the block header's blobBaseFee and the transaction's maxFeePerBlobGas, but the actual blobGasPrice is determined by the protocol.

An eth_sendRawTransaction that rejects a type-3 payload because the node is not post-Dencun will return an error like 'transaction type not supported'. Ensure your node is running a post-Dencun client version. Similarly, a fee estimator that silently omits the blob dimension will under-price the transaction. Always set maxFeePerBlobGas explicitly. For more on transaction pool behavior, see Ethereum transaction pool and the txpool namespace.

  • Sidecar stripped: expected; sidecar not on chain.
  • blobGasPrice null: provider indexing incomplete.
  • Type-3 rejected: node not post-Dencun.
  • Fee estimator omits blob fee: set maxFeePerBlobGas manually.

Limitations, Tradeoffs, and Provider Variability

What is documented protocol behaviour: type-3 transactions, blob gas, GAS_PER_BLOB, versioned hashes, and the receipt fields blobGasUsed and blobGasPrice are defined in EIP-4844 and the execution-apis specification. What varies by provider: support for blob sidecar methods, availability of blob base fee in eth_feeHistory, and completeness of receipt indexing. You must verify against your own endpoint.

Blob availability is a retention-window question rather than a permanent guarantee. The sidecar is pruned after a bounded window, so a JSON-RPC read of a blob's contents can fail for an old block while the block itself is perfectly queryable. EIP-7594 (PeerDAS) changes how sidecars are propagated, so you must re-verify after network upgrades. Always check the latest specifications and your provider's documentation.

For bulk receipt retrieval, see eth_getBlockReceipts: bulk receipts in one call. For tracing, see Ethereum transaction tracing: trace vs debug. For endpoint selection, see RPC endpoints guide (RPC Assistant).

  • Protocol-defined: type-3 fields, blob gas, receipt fields.
  • Provider-specific: sidecar methods, blob base fee availability.
  • Retention window: sidecar pruned, not permanent.
  • EIP-7594 changes propagation; re-verify after upgrades.

Next Steps: Integrating Blob Transactions into Your Workflow

To integrate blob transactions into your workflow, start by ensuring your node or provider is post-Dencun and supports type-3 transactions. Use the inspection script to verify that your endpoint returns blobVersionedHashes and receipt blob fields. If you need to send blobs, use a library like ethers.js that handles KZG commitments and sidecar construction.

For production, consider using a provider that offers reliable blob sidecar availability if you need to read blob data. OnFinality provides Ethereum RPC endpoints; see /en/networks/eth and RPC pricing for details. You can also explore the OnFinality Learn hub for more guides, and the API service for managed access.

Finally, always compute the total blob fee from the receipt and compare it to your expectations. Monitor blob base fee trends to time your blob postings. Remember that blob gas and execution gas are independent, so optimize each separately.

  • Verify endpoint supports type-3 and receipt blob fields.
  • Use ethers.js or similar for sending blobs.
  • Choose a provider with sidecar availability if needed.
  • Monitor blob base fee for cost optimization.

Never Worry about Infrastructure Again

OnFinality takes away the heavy lifting of DevOps so you can build smarter and faster.

Get Started