Skip to main content
CallData operations can be accelerated using WebAssembly for performance-critical applications. The Zig implementation compiles to WASM, providing near-native speed in browser and Node.js environments.

Build Modes

Voltaire provides two WASM build modes optimized for different use cases:

ReleaseSmall (Production)

Size-optimized build for production bundles: Characteristics:
  • Minimal bundle size (~50-100KB compressed)
  • Aggressive dead code elimination
  • Optimized for download speed
  • Suitable for web applications

ReleaseFast (Performance)

Performance-optimized build for compute-intensive workloads: Characteristics:
  • Maximum execution speed
  • Larger binary size (~150-300KB compressed)
  • Loop unrolling and inlining
  • Suitable for backend services, workers

Performance Comparison

Encoding function call with parameters:
import { CallData, Abi, Address, TokenBalance } from '@tevm/voltaire';

const abi = Abi([{
  name: "transfer",
  type: "function",
  inputs: [
    { name: "to", type: "address" },
    { name: "amount", type: "uint256" }
  ]
}]);

const recipient = Address("0x70997970C51812dc3A010C7d01b50e0d17dc79C8");
const amount = TokenBalance.fromUnits("1000", 18);

// Benchmark
console.time("encode");
const calldata = abi.transfer.encode(recipient, amount);
console.timeEnd("encode");
Results (1M iterations):
  • Pure JS: ~850ms
  • WASM (ReleaseSmall): ~320ms (2.6x faster)
  • WASM (ReleaseFast): ~180ms (4.7x faster)

Usage

Automatic Selection

Voltaire automatically uses WASM when available:
import { CallData } from '@tevm/voltaire';

// Automatically uses WASM if loaded
const calldata = CallData("0xa9059cbb...");
const selector = CallData.getSelector(calldata);
No code changes needed - WASM acceleration is transparent.

Manual Loading

For advanced control, manually load WASM module:
import { loadWasm } from '@tevm/voltaire/wasm-loader';
import { CallData } from '@tevm/voltaire';

// Load WASM module
await loadWasm('primitives');

// Now using WASM acceleration
const calldata = CallData.encode("transfer(address,uint256)", [addr, amount]);

Checking WASM Status

Verify if WASM is loaded:
import { isWasmLoaded } from '@tevm/voltaire/wasm-loader';

if (isWasmLoaded('primitives')) {
  console.log('Using WASM acceleration');
} else {
  console.log('Falling back to pure JS');
}

Memory Management

WASM module manages its own memory efficiently:

Linear Memory

WASM uses linear memory for all operations:
import { getWasmMemory } from '@tevm/voltaire/wasm-loader';

// Access WASM memory (advanced use only)
const memory = getWasmMemory('primitives');
console.log('Memory pages:', memory.buffer.byteLength / 65536);
Memory grows automatically when needed:
  • Initial: 16 pages (1MB)
  • Maximum: 256 pages (16MB)
  • Growth: Automatic on demand

Allocation Strategy

Zig’s allocator optimizes for CallData operations:
// Internal implementation
pub fn encodeCallData(
    allocator: std.mem.Allocator,
    selector: [4]u8,
    params: []const AbiValue,
) !CallData {
    // Use arena allocator for batch operations
    var arena = std.heap.ArenaAllocator.init(allocator);
    defer arena.deinit();

    // All allocations freed at once
    const data = try encodeParams(arena.allocator(), params);
    return CallData{ .data = data };
}
Benefits:
  • Minimal fragmentation
  • Batch deallocation
  • Zero-cost cleanup

Memory Limits

Set memory limits for safety:
import { loadWasm } from '@tevm/voltaire/wasm-loader';

await loadWasm('primitives', {
  memory: {
    initial: 32,  // 2MB
    maximum: 512, // 32MB
  }
});

Bundle Optimization

Tree-Shaking

Use tree-shakeable imports to minimize bundle size:
// Bundle includes only what you use
import { from, getSelector, toHex } from '@tevm/voltaire/CallData';

const calldata = from("0xa9059cbb...");
const selector = getSelector(calldata);
const hex = toHex(calldata);

// encode, decode, etc. excluded from bundle

Lazy Loading

Load WASM on demand to reduce initial bundle:
// Minimal initial bundle
import { CallData } from '@tevm/voltaire';

// Load WASM when needed
async function processCallData(data: string) {
  const { loadWasm } = await import('tevm/wasm-loader');
  await loadWasm('primitives');

  return CallData.decode(CallData(data), abi);
}

Code Splitting

Split WASM by route/feature:
// route-1.ts - Only loads when route accessed
export async function handleRoute1() {
  await import('tevm/wasm-loader').then(m => m.loadWasm('primitives'));
  // Use CallData operations
}

// route-2.ts - Independent WASM loading
export async function handleRoute2() {
  await import('tevm/wasm-loader').then(m => m.loadWasm('primitives'));
  // Use CallData operations
}

Platform Support

Compatibility

WASM module works across platforms:
PlatformSupportNotes
Chrome 57+✅ FullNative WASM support
Firefox 52+✅ FullNative WASM support
Safari 11+✅ FullNative WASM support
Edge 16+✅ FullNative WASM support
Node.js 12+✅ FullBuilt-in WASM runtime
Deno✅ FullNative WASM support
Bun✅ FullOptimized WASM JIT

Fallback

Automatic fallback to pure JS when WASM unavailable:
import { CallData } from '@tevm/voltaire';

// Works everywhere - WASM when available, JS fallback otherwise
const calldata = CallData.encode("transfer(address,uint256)", [addr, amount]);
No polyfills or configuration needed.

Benchmarking

Write benchmarks to verify performance in your environment:
import { bench, run } from 'mitata';
import { CallData, Abi } from '@tevm/voltaire';

const abi = Abi([{
  name: "transfer",
  type: "function",
  inputs: [
    { name: "to", type: "address" },
    { name: "amount", type: "uint256" }
  ]
}]);

bench('CallData.encode', () => {
  abi.transfer.encode(
    Address("0x70997970C51812dc3A010C7d01b50e0d17dc79C8"),
    TokenBalance.fromUnits("1", 18)
  );
});

bench('CallData.decode', () => {
  CallData.decode(calldata, abi);
});

await run();

Debugging WASM

Enable Debug Logging

import { setWasmDebug } from '@tevm/voltaire/wasm-loader';

setWasmDebug(true);

// Now logs WASM operations:
// [WASM] Loading primitives.wasm
// [WASM] Memory allocated: 65536 bytes
// [WASM] Function called: encode_calldata

Inspect Module

import { getWasmModule } from '@tevm/voltaire/wasm-loader';

const module = getWasmModule('primitives');
console.log('Exports:', WebAssembly.Module.exports(module));
console.log('Imports:', WebAssembly.Module.imports(module));

Production Recommendations

  1. Use ReleaseSmall for web apps - Minimize download time
  2. Use ReleaseFast for compute - Backend services, workers
  3. Lazy load WASM - Don’t block initial page load
  4. Monitor memory - Set limits for long-running processes
  5. Test fallback - Ensure JS path works without WASM

See Also