Skip to main content

EIP-4844: Blob Transactions

EIP-4844 (proto-danksharding) introduces a new transaction type (0x03) that carries large data blobs alongside the transaction. Blobs are 128 KB each (6 per block on Cancun; fork-dependent, HardforkConfig.MaxBlobsPerBlock, 21 on Amsterdam) and are stored temporarily on the consensus layer. This is the primary mechanism for L2 rollups to post data to L1 at reduced cost.

Quick Start — Send a Blob Transaction​

using Nethereum.EVM.Precompiles.Kzg;
using Nethereum.Model;
using Nethereum.Signer;
using System.Text;

// 1. Initialize KZG (once per process)
CkzgOperations.InitializeFromEmbeddedSetup();
var kzg = new CkzgOperations();
var builder = new BlobSidecarBuilder(kzg);

// 2. Encode your data into blobs + compute KZG commitments/proofs
var data = Encoding.UTF8.GetBytes("Hello Ethereum Blobs!");
var (sidecar, versionedHashes) = builder.BuildFromData(data);

// 3. Build the blob transaction
var tx = new Transaction4844(
chainId: 1,
nonce: 0,
maxPriorityFeePerGas: 2_000_000_000,
maxFeePerGas: 50_000_000_000,
gasLimit: 21_000,
receiverAddress: "0xRecipientAddress",
amount: 0,
data: null,
accessList: null,
maxFeePerBlobGas: 10_000_000_000,
blobVersionedHashes: versionedHashes);

tx.Sidecar = sidecar;

// 4. Sign
var signer = new Transaction4844Signer();
signer.SignTransaction(privateKeyBytes, tx);

// 5. Send via eth_sendRawTransaction (includes sidecar in network wrapper)
var rawTx = tx.GetRLPEncodedWithSidecar().ToHex();
var txHash = await web3.Eth.Transactions.SendRawTransaction.SendRequestAsync(rawTx);

Reading Blobs Back​

Blobs are available from beacon nodes via the standard REST API:

using Nethereum.Beaconchain;
using Nethereum.Model;

var beacon = new BeaconApiClient("https://your-beacon-node.example.com");

// Fetch blobs for a specific slot
var response = await beacon.GetBlobSidecarsAsync("9000000");

// Decode back to original data
var originalData = BlobSidecarHelper.DecodeData(response);

Encoding Details​

Each blob is 131,072 bytes (4,096 field elements x 32 bytes). The first byte of each field element is zeroed to stay within the BLS12-381 scalar field, giving 31 usable bytes per element (126,976 usable bytes per blob).

// Encode — auto-splits into multiple blobs if data > 126,976 bytes
var blobs = BlobEncoder.EncodeBlobs(largeData);

// Decode — strips padding and concatenates
var decoded = BlobEncoder.DecodeBlobs(blobs);

KZG Operations​

The KZG cryptographic operations use the c-kzg-4844 native library via Ckzg.Bindings:

var kzg = new CkzgOperations();

// Individual operations
byte[] commitment = kzg.BlobToKzgCommitment(blob); // 48 bytes
byte[] proof = kzg.ComputeBlobKzgProof(blob, commitment); // 48 bytes
byte[] hash = kzg.ComputeVersionedHash(commitment); // 0x01 || SHA256[1:]
bool valid = kzg.VerifyBlobKzgProof(blob, commitment, proof);

DevChain Testing​

The Nethereum DevChain supports blob transactions in-process:

var node = DevChainNode.CreateInMemory(config);
await node.StartAsync(accounts);

// Send blob tx — sidecar is automatically stored
tx.Sidecar = sidecar;
await node.SendTransactionAsync(tx);
await node.MineBlockAsync();

// Read back stored blobs
var blobs = await node.BlobStore.GetBlobsByBlockNumberAsync(blockNumber);
var decoded = BlobEncoder.DecodeBlobs(blobs.Select(b => b.Blob).ToList());

Key Constants​

ConstantValue
Blob size131,072 bytes (128 KB)
Field elements per blob4,096
Usable bytes per blob126,976
Max blobs per block6 on Cancun; fork-dependent (HardforkConfig.MaxBlobsPerBlock, 21 on Amsterdam)
KZG commitment size48 bytes
KZG proof size48 bytes
Versioned hash prefix0x01
Transaction type0x03