Blog | LF Decentralized Trust

How to Create and Validate EIP-4844 Blob Transactions With Sidecar

Written by Dev (Dev10-sys), LF Decentralized Trust mentee for Web3j, 2026 | Aug 11, 2026, 2:30:02 PM

As a mentee on the Web3j Libraries Full Development Lifecycle project under the 2026 LF Decentralized Trust Mentorship Program,I have been going deep into the blob transaction support inside Web3j. This tutorial shows Java developers how to create an EIP-4844 blob, generate and validate its KZG sidecar, attach it to a Web3j transaction, send it to Sepolia, and verify the result.

What Is EIP-4844

EIP-4844 introduced a new transaction type (Type-3) that carries data in blocks called blobs. Instead of storing data permanently on the execution layer via costly calldata, the blob is attached as a temporary “sidecar” on the consensus layer. The execution layer only views a short cryptographic fingerprint called a KZG commitment. This sidecar architecture significantly lowers gas fees for rollups by separating data availability from mainnet execution storage.

If you want the deeper story of how this support first landed inside Web3j, this blog post is worth a read for more details on how EIP-4844 fits into Web3j.

Part 1: Setting Up Your Web3j Project

Install the Web3j CLI if you do not already have it.

A quick web3j -v tells you the install went through.

From there web3j new gives you a fresh project with a Gradle build file and a sample Solidity contract already sitting inside it.

If you would rather import an existing project instead of starting fresh, the Web3j Command Line Tools documentation walks through every command in detail.

Open build.gradle and add the Web3j core dependency. This tutorial was tested with Web3j 5.0.2.

One thing worth flagging here: an earlier Web3j release (not too long before 5.0.1) had a bug where the sidecar would quietly fail to attach when a blob transaction got encoded for signing. If you are working from an older 4.x build, it is worth upgrading first so your signed transaction does not go out missing its sidecar.

Part 2: The Shape of a Blob Transaction

A blob transaction sits under EIP-2718 as transaction type 3. Most of it looks familiar and behaves exactly the way it does in an EIP-1559 transaction, with a few new additions.

Network representation: The pooled transaction wrapper can carry the transaction body, blobs, commitments, and proofs.

Block representation: When the execution transaction is included in a block, its standard type-3 payload is stored while the blob sidecar is handled through the consensus layer.

Each of those 32 byte field elements has to sit numerically below the BLS12-381 scalar field modulus. That is simply how the KZG commitment scheme works under the hood and not something Web3j invented. It matters once you start filling a blob with your own data, which we will get to shortly.

Web3j hands you two ways to put a blob transaction together:

  • Automatic: Provide blobs and let Web3j calculate sidecar values.
  • Explicit: Provide blobs, commitments, proofs, and versioned hashes yourself.

This tutorial uses the explicit path for inspection and validation. If you want to look at exactly how these classes are implemented before using them, the Web3j GitHub repository has the full source.

Part 3: Creating a Blob and Its Sidecar

Start with the blob itself. Separate the Web3j object behavior from the protocol requirement. The blob constructor itself wraps the supplied bytes without enforcing the size, but the EIP-4844/KZG blob input must be exactly 131,072 bytes. An invalid length will cause problems when the data is processed.

Nothing more and nothing less.

For testing, create a file named blob_data.text inside your src/main/resources/ directory and paste this sample hex string inside it: 48656c6c6f20576f726c64207468697320697320612073616d706c6520626c6f622064617461

One note before you copy this in. loadResourceAsString is not a Web3j method. It is a small helper you write yourself that reads a file and returns its contents as a hex string. That is the same way the original Web3j example does it. Swap it for a plain file read or drop in your own hex string directly and the rest of the flow stays the same.

Once the blob exists, BlobUtils takes care of everything else the sidecar needs.

Run checkProofValidity before you go anywhere near signing anything. A false here means something is off with the blob or with how the commitment got built. No amount of retrying the send will patch that over. Fix the sidecar first.

Part 4: Putting Your Own Data in a Blob

For testing, it helps to drop something recognisable into your blob so you can spot it later on a block explorer. This step replaces the file based loadResourceAsString approach from Part 3. It is not extra code you add on top of it. A protocol blob is exactly 131,072 bytes. A shorter application payload must be encoded into and padded within that fixed-size structure.

Run the commitment and proof and validity check again on this new blob before you move on to Part 5. The values from Part 3 belonged to the old file based blob and will not match this one. This runs fine as ordinary Java. But keep the field element rule from earlier in mind. A short ASCII string like the one above sits safely inside the first 32 byte chunk because the rest of that chunk is zero. Zero padded values stay comfortably under the BLS modulus. Drop a much larger custom payload in without checking each 32 byte chunk on its own and one of those chunks could end up sitting above the modulus, which makes getCommitment fail. Worth remembering before you reuse this trick for anything bigger than a short message.

Part 5: Building the Raw Transaction With the Sidecar Attached

Before putting the raw transaction together, remember that cryptographic proof validation alone is not a complete structural validation. As part of a thorough sidecar validation, you must explicitly verify that your lists of blobs, commitments, proofs, and versioned hashes all have equal lengths, and ensure that every versioned hash correctly corresponds to its respective commitment. Once these relationships are verified, fetch a nonce and a sensible blob gas fee to construct the transaction payload.

(Note: The values used above {10 gwei, 50 gwei, and 300,000 gas} are just examples. Fees and gas requirements vary with network conditions and transaction behavior. In production, you should estimate or configure these dynamically instead of hardcoding them.)

This is the overload that takes the blobs, commitments and proofs all together rather than just the blobs on their own. It is the explicit sidecar path we talked about earlier. Web3j does not recompute anything at this point. It simply trusts the sidecar you already validated back in Part 3 or Part 4.

A quick code check here since this is the part most likely to trip someone up. The signature of RawTransaction.createTransaction used above matches what actually ships in Web3j's source. The order runs blobs then kzgCommitments then kzgProofs then chainId then nonce then maxPriorityFeePerGas then maxFeePerGas then gasLimit then to then value then data then maxFeePerBlobGas then versionedHashes. ethGetBaseFeePerBlobGas is also being called the right way here since unlike most Web3j RPC calls it hands back its value directly without needing a .send() call.

Part 6: Signing and Sending the Transaction

From here signing and sending a blob transaction looks no different from any other transaction type in Web3j.

Before pointing any of this at a real network, make sure that network actually supports EIP-4844. Most public testnets, including Sepolia, do at this point, but do not assume the same for every private or local chain.

receipt.getType returns 0x3 once a blob transaction confirms. That is your first on chain sign that it went through as an actual blob transaction and not something else entirely.

Part 7: Validating the Result

There are really two separate layers of validation happening across this whole walkthrough, and it helps to keep them apart in your head.

The first is what checkProofValidity already handled back in Part 3 or Part 4. That check runs entirely on your own machine before anything gets broadcast, and it confirms the proof genuinely matches the blob and the commitment.

The second layer only shows up on chain. Once the transaction is mined, its versioned hash gets recorded in the block but the actual blob content never touches the execution layer. It lives with the consensus nodes for that same window of close to eighteen days before it disappears. A normal block explorer will show you the transaction and confirm its type, but seeing the blob itself and its commitment needs an explorer built specifically for blobs.

After sending the transaction, the network returns the hash, and we can track it directly on Sepolia Etherscan.

As you can see in the screenshot, we are able to see our Commit EIP-4844 Blob action alongside the dedicated Blobs tab:

Conclusion

This walkthrough covered the full path from a raw blob file all the way to a confirmed transaction on chain. You built a Blob. You generated its commitment and proof. You validated that sidecar locally with checkProofValidity before spending a single unit of gas. Then you signed the transaction and read back its receipt.

Web3j was the first Java library to ship this feature, and it keeps improving with every release. Try building your own blob transaction on Sepolia. If you get stuck at any step, drop your question in the web3j Discord. You are also welcome on the wider LF Decentralized Trust Discord if you want to explore the other projects under the foundation. Also, take part in our community call where maintainers walk through ongoing work. All are welcome to join.