Skip to main content

Try it Live

Run Blob examples in the interactive playground
Tevm provides WASM builds for blob operations with some limitations. This page covers WASM support, performance, and workarounds.

Current Status

Supported Operations

import { Blob } from 'tevm';

// ✅ Data encoding/decoding
const blob = Blob.fromData(data);
const decoded = Blob.toData(blob);

// ✅ Splitting and joining
const blobs = Blob.splitData(largeData);
const reconstructed = Blob.joinData(blobs);

// ✅ Validation
const isValid = Blob.isValid(blob);
const isValidVersion = Blob.isValidVersion(versionedHash);

// ✅ Gas estimation
const blobCount = Blob.estimateBlobCount(data);
const gas = Blob.calculateGas(blobCount);

Unsupported Operations

KZG operations require c-kzg-4844 native library (not available in WASM):
import { Blob } from 'tevm';

// ❌ KZG commitment (requires native library)
try {
  const commitment = Blob.toCommitment(blob);
} catch (e) {
  console.error(e); // "Not implemented: requires c-kzg-4844 library"
}

// ❌ KZG proof generation
try {
  const proof = Blob.toProof(blob);
} catch (e) {
  console.error(e); // "Not implemented: requires c-kzg-4844 library"
}

// ❌ KZG verification
try {
  const isValid = Blob.verify(blob, commitment, proof);
} catch (e) {
  console.error(e); // "Not implemented: requires c-kzg-4844 library"
}

Use Cases

Browser-Based L2 Clients

Use WASM for data encoding/decoding, compute KZG operations server-side:
import { Blob } from 'tevm';

class BrowserL2Client {
  async prepareBlob(data: Uint8Array): Promise<PreparedBlob> {
    // ✅ WASM: Encode data
    const blob = Blob.fromData(data);

    // ✅ WASM: Validate
    if (!Blob.isValid(blob)) {
      throw new Error('Invalid blob');
    }

    // ❌ WASM: Cannot compute commitment
    // Send to backend for KZG operations
    const { commitment, proof, versionedHash } =
      await this.computeKZGOnServer(blob);

    return { blob, commitment, proof, versionedHash };
  }

  private async computeKZGOnServer(blob: Blob) {
    const response = await fetch('/api/compute-kzg', {
      method: 'POST',
      body: blob,
    });

    return await response.json();
  }
}

Backend KZG Service

import { Blob } from 'tevm';
import express from 'express';

const app = express();

// Native environment - KZG available
app.post('/api/compute-kzg', async (req, res) => {
  try {
    const blob = Blob(req.body);

    // ✅ Native: Compute KZG operations
    const commitment = Blob.toCommitment(blob);
    const proof = Blob.toProof(blob);
    const versionedHash = Blob.Commitment.toVersionedHash(commitment);

    res.json({ commitment, proof, versionedHash });
  } catch (e) {
    res.status(500).json({ error: (e as Error).message });
  }
});

app.listen(3000);

Build Configuration

WASM Builds

Tevm provides optimized WASM builds:
# ReleaseSmall (production - size optimized)
zig build build-ts-wasm

# ReleaseFast (benchmarking - performance optimized)
zig build build-ts-wasm-fast

Output Files

wasm/primitives.wasm       # ReleaseSmall (default)
wasm/primitives-fast.wasm  # ReleaseFast

Import in Browser

// Automatic WASM loading
import { Blob } from 'tevm';

// WASM is loaded automatically when needed
const blob = Blob.fromData(data);

Performance

Data Operations

WASM performs well for data encoding/decoding:
import { Blob } from 'tevm';

// Benchmark: fromData (WASM)
console.time('fromData');
const blob = Blob.fromData(new Uint8Array(100_000));
console.timeEnd('fromData'); // ~1ms

// Benchmark: toData (WASM)
console.time('toData');
const data = Blob.toData(blob);
console.timeEnd('toData'); // ~0.5ms

// Benchmark: splitData (WASM)
console.time('splitData');
const blobs = Blob.splitData(new Uint8Array(300_000));
console.timeEnd('splitData'); // ~3ms

KZG Operations

For KZG operations, use native builds or backend services:
// Native performance (c-kzg-4844)
console.time('commitment');
const commitment = Blob.toCommitment(blob);
console.timeEnd('commitment'); // ~10ms

console.time('proof');
const proof = Blob.toProof(blob);
console.timeEnd('proof'); // ~10ms

console.time('verify');
const isValid = Blob.verify(blob, commitment, proof);
console.timeEnd('verify'); // ~5ms

Workarounds

Hybrid Approach

Use WASM for data operations, native for KZG:
import { Blob } from 'tevm';

class HybridBlobProcessor {
  async process(data: Uint8Array): Promise<BlobTransaction> {
    // ✅ WASM: Data encoding
    const blob = Blob.fromData(data);

    // ✅ WASM: Estimate costs
    const blobCount = Blob.estimateBlobCount(data);
    const estimatedGas = Blob.calculateGas(blobCount);

    console.log(`Estimated gas: ${estimatedGas}`);

    // ❌ WASM: Send to native backend for KZG
    const kzgData = await this.computeKZGNative(blob);

    return {
      blob,
      ...kzgData,
    };
  }

  private async computeKZGNative(blob: Blob) {
    // Call native KZG service
    if (typeof window === 'undefined') {
      // Node.js - use native library
      return {
        commitment: Blob.toCommitment(blob),
        proof: Blob.toProof(blob),
        versionedHash: Blob.toVersionedHash(blob),
      };
    } else {
      // Browser - call backend
      const response = await fetch('/api/kzg', {
        method: 'POST',
        body: blob,
      });
      return await response.json();
    }
  }
}

Pre-computed Commitments

For known data, pre-compute commitments:
import { Blob } from 'tevm';

// Generate commitments ahead of time (native)
const precomputed = new Map<string, KZGData>();

function precomputeCommitments(testData: Uint8Array[]) {
  for (const data of testData) {
    const blob = Blob.fromData(data);
    const commitment = Blob.toCommitment(blob);
    const proof = Blob.toProof(blob);
    const versionedHash = Blob.Commitment.toVersionedHash(commitment);

    const key = Hex.fromBytes(data).slice(2);
    precomputed.set(key, { commitment, proof, versionedHash });
  }
}

// Use in WASM environment
function getKZGData(data: Uint8Array): KZGData {
  const key = Hex.fromBytes(data).slice(2);
  const cached = precomputed.get(key);

  if (!cached) {
    throw new Error('KZG data not precomputed');
  }

  return cached;
}

Testing

WASM-Specific Tests

import { Blob } from 'tevm';
import { describe, it, expect } from 'vitest';

describe('Blob WASM', () => {
  it('should encode and decode data', () => {
    const data = new Uint8Array([1, 2, 3, 4, 5]);
    const blob = Blob.fromData(data);
    const decoded = Blob.toData(blob);

    expect(decoded).toEqual(data);
  });

  it('should split and join data', () => {
    const data = new Uint8Array(300_000);
    const blobs = Blob.splitData(data);
    const reconstructed = Blob.joinData(blobs);

    expect(reconstructed.length).toBe(data.length);
  });

  it('should estimate blob count', () => {
    const data = new Uint8Array(300_000);
    const count = Blob.estimateBlobCount(data);

    expect(count).toBe(3);
  });

  it('should throw on KZG operations', () => {
    const blob = Blob.fromData(new Uint8Array([1, 2, 3]));

    expect(() => Blob.toCommitment(blob)).toThrow(
      'Not implemented: requires c-kzg-4844 library'
    );
  });
});

Run WASM Tests

# Build WASM
zig build build-ts-wasm

# Run WASM-specific tests
bun run test:wasm

Future Support

Potential Improvements

  1. WASM KZG Library
    • Port c-kzg-4844 to WASM
    • Maintain compatibility with trusted setup
  2. Progressive Enhancement
    • Detect native KZG availability
    • Fall back to WASM-only operations
  3. Worker Threads
    • Offload KZG operations to workers
    • Parallel commitment computation

Tracking Issue

Follow progress on WASM KZG support:

Limitations

Memory Constraints

WASM has stricter memory limits:
import { Blob } from 'tevm';

// Large data may fail in WASM
try {
  const largeData = new Uint8Array(1_000_000_000); // 1 GB
  Blob.splitData(largeData);
} catch (e) {
  console.error('WASM memory exceeded');
}

// Recommendation: Process in chunks
function processInChunks(data: Uint8Array, chunkSize: number) {
  const chunks: Blob[][] = [];

  for (let i = 0; i < data.length; i += chunkSize) {
    const chunk = data.slice(i, i + chunkSize);
    const blobs = Blob.splitData(chunk);
    chunks.push(blobs);
  }

  return chunks;
}

No Trusted Setup Access

WASM builds cannot load trusted setup:
// ❌ Not available in WASM
import { loadTrustedSetup } from 'c-kzg';

// Use backend service instead
async function loadTrustedSetupRemote() {
  const response = await fetch('/api/trusted-setup');
  return await response.json();
}

Best Practices

  1. Use Native for KZG - Always compute commitments/proofs in native environment
  2. WASM for Data - Use WASM builds for encoding/decoding/validation
  3. Backend Services - Provide KZG API for browser clients
  4. Pre-compute When Possible - Generate commitments ahead of time
  5. Test Both Environments - Run tests in both native and WASM

Resources

See Also