Blob Transaction Sending Tutorial + KZG explanation

Vladislav Lenskii
7 min readJun 15, 2024

--

The article is written as a result of group research for a 블록체인의 실무응용1 course at Seoul National University during the 2024 Spring term. Infra group: Vladislav Lenskii, 조상현, 전경민 and 조창민.

Blobs

The term “blob” (Binary Large OBject) refers to a large chunk of data that can be attached to a block in the blockchain. It is a file-like object of immutable, raw data, that can be read as text or binary data. This data cannot be accessed by the EVM directly. Instead, the commitment to it is provided to the blockchain.
Blobs on Ethereum are stored in beacon nodes, not in the execution layer. They are removed from storage after ~ 2 weeks.

Proto-DankSharding

Rollups are in the short and medium term the only trustless scaling solution for Ethereum. L1 transaction fees are a significant blocker for new users and applications. Thus, EIP(Ethereum Improvement Proposal)-4844 was introduced to support the ecosystem transition to rollups.

The proposal introduces a new kind of transaction type (type 3) to Ethereum which accepts “blobs” of data. Blobs are small enough to keep disk use manageable. The format is intended to be fully compatible with the format used in full sharding later since future sharding work only requires changes to the Beacon node.

Proto-DankSharding Schema

Blobs are committed to blockchain with KZG: an efficient vector commitment scheme, with fixed-size proof data, and forward-compatible with data-availability-sampling. We need it because we cannot access the data on-chain.

Ethereum transaction types

  • Legacy(type 0x00):
    Legacy transactions allow users to manually set the gas price (gasPrice) they are willing to pay for network processing. This was the standard transaction type before the introduction of EIP-1559 (tx type 0x02), where users directly proposed the gas price for their transactions to be processed.
  • EIP-2930 (type 0x01):
    EIP-2930 introduces “access lists” that provide a list of addresses and storage keys, which help reduce gas costs and optimize transaction processing. This type is particularly beneficial for optimizing gas expenses when executing smart contracts.
  • EIP-1559 (type 0x02):
    EIP-1559 transactions introduce baseFeePerGas and maxPriorityFeePerGas, which improve the transaction fee model. This structure allows fees to dynamically adjust based on network congestion, enabling users to propose gas prices that better reflect the current state of the network. These are the common transactions you usually send.
  • EIP-4844 (type 0x03)
    Blob transactions were designed to improve Ethereum’s data throughput and scalability by using “blob” data to enable large-scale data processing at Layer 1, complementing Layer 2 solutions and increasing overall network throughput. (They are the topic of this tutorial)

Sending a Blob Transaction using viem.

To start you can open an empty folder of your choice.
Prerequirements:

To create a blockchain client we will need to use the private key. To handle it relatively safely we will need to install “dotenv” package:

# install locally (recommended)
npm install dotenv --save

Next, we need to install some KZG bindings.

npm i c-kzg
# or
npm i kzg-wasm

Let’s start writing the code, we can call the file “example.ts”:

  • start with all the required imports
import { parseGwei, stringToHex, toBlobs, parseEther, setupKzg, createWalletClient, http } from 'viem'
import { privateKeyToAccount } from "viem/accounts";
import { sepolia } from 'viem/chains'
import * as cKzg from 'c-kzg'
import { resolve } from 'path'
import dotenv from "dotenv";
dotenv.config();

We are importing necessary functions from viem, choosing Sepolia testnet, and importing KZG bindings and packages to deal with environment variables.

Now let’s create a blockchain client using viem functions.

const PRIVATE_KEY: string = process.env.PRIVATEKEY
const account = privateKeyToAccount(`0x${PRIVATE_KEY}`);
const client = createWalletClient({
account,
chain: sepolia,
transport: http()
});

Then, we need to set up the KZG

const mainnetTrustedSetupPath = resolve(
'./mainnet.json',
)
const kzg = setupKzg(cKzg, mainnetTrustedSetupPath);

Here we need to set the trusted setup for the KZG. Viem docs, tell that it can be imported using

import { mainnetTrustedSetupPath } from 'viem/node'

It didn’t work in our case, so we had to import it manually (by putting the necessary “mainnet.json” file in our folder). We got the “mainnet.json” file from “node_modules/viem/trusted-setups/”.

Next, let’s create our blob. The “toBlobs()” function accepts parameters of type <`0x${string}` | Uint8Array, “hex” | “bytes”>, so we use the “stringToHex” function to transform our string to a hex string.

const blobs = toBlobs({ data: stringToHex('Blockchain Application, SNU 2024 Spring') });

We are ready to send a transaction.

async function type3transaction() {
const hash = await client.sendTransaction({
account,
blobs: blobs,
kzg,
maxFeePerBlobGas: parseGwei('300'),
to: '0x0000000000000000000000000000000000000000',
chain: sepolia,
gas: 21000n,
})
console.log(`Transaction: https://sepolia.etherscan.io/tx/${hash}`)
}

Here we use our client and account, paste our blob and KZG setup, and set the gas fees (you can adjust them depending on the network congestion) together with the transaction destination address.
Later we just console.log the transaction link.

To safely run the function I suggest adding the “catch” functionality, as follows.

type3transaction().catch((error) => {
console.error(error);
process.exitCode = 1;
});

Although this is not necessary and function can just be called this way:

type3transaction();

Now we can run the script:

ts-node example.ts
# or
npx ts-node example.ts
Result of script execution. https://sepolia.etherscan.io/tx/0xd062e43e647a2a47212a956bff3ea460c553c8956e5d4a91b20652c8d26c9175

The code for this tutorial can be found on GitHub here: https://github.com/rrhlrmrr/BlobTransactionTutorial/tree/main

It also demonstrates how to send transactions of other types using viem.

KZG Commitment

KZG commitment is used in various cryptographic protocols.
It allows one to commit to a polynomial. Creating a cryptographic commitment to a polynomial means that you want to “lock in” this polynomial in a way that:

  1. Nobody can change it later (it’s binding).
  2. Nobody else knows what the polynomial looks like (it’s hiding).

Later, the committer can prove specific properties of the polynomial, such as its evaluation (result) at certain points, without revealing the entire polynomial.

Let’s run through the process in an example:

Let’s say our polynomial is P(x) = 3 + 4*x + x^2 ( 3, 4, and 2 are the coefficients of the polynomial)

Using a special mathematical process we can create a commitment C from the polynomial. We can share the commitment C without revealing the polynomial. (we can actually share all of the coefficients as a stupid(?) commitment, it will reveal the polynomial)

Later, we might want to show that your polynomial evaluates (results) to a specific value at a certain point (for example, P(2) = 15). So we compute P(2) = 3 + 4*2 + 2^2= 3 + 8+ 4 = 15. Then, we provide 15 and prove that this value comes from P(x) using the commitment C. The main idea is that the prover can show that the polynomial value at a certain position is equal to a claimed value.
The intuition behind the proof is that at point a, the polynomial evaluates to y. If we move the polynomial down by y (i.e. f(x) — y), the resulting polynomial has a root at point a, meaning it can be divided by (x-a) (0/0 division may give valid results). Therefore, f(x) — y / x — a gives rise to a quotient.

Move the polynomial down by y.
KZG Commitment illustration

Some properties of KZG:

  1. The commitment size is one group element of an elliptic group (size of the coefficient).
  2. The proof size, independent of the polynomial size, is always only one group element (size of the coefficient). Verification, also independent from the polynomial size, requires two group multiplications and two pairings (constant complexity), no matter what the degree of the polynomial is.
  3. The scheme usually hides the polynomial — an infinite number of polynomials can have exactly the same commitment. However, it is not perfectly hiding: If you can guess the polynomial (for example because it is very simple or in a small set of possible polynomials) you can find out which polynomial was committed to.

In Proto-Danksharding particularly, KZG enables efficient and secure data availability proofs. A blob is a vector of 4096 field elements — numbers.

The blob is mathematically treated as a degree < 4096 polynomial over the finite field, where the field element at position i in the blob is the evaluation of that polynomial at ωi (ω is a constant that satisfies ω⁴⁰⁹⁶=1).

How KZG commitment is used in EIP-4844

A commitment to a blob is a hash of the KZG commitment to the polynomial. From the point of view of implementation, it is not important to be concerned with the mathematical details of the polynomial. Instead, we can simply treat it as a vector of elliptic curve points (the Lagrange-basis trusted setup), and the KZG commitment to a blob will simply be a linear combination.

References

DankSharding:

General on Sharding:

KZG:

--

--

Vladislav Lenskii

Blockchain Developer and Researcher | Co-founder @miki.digital