Voltaire provides three implementation tiers for performance-critical operations. Choose based on your runtime environment and performance needs.
Three Tiers
| Tier | Import | Runtime | Use Case |
|---|
| TypeScript | @tevm/voltaire | Any JS runtime | Default, universal compatibility |
| WASM | @tevm/voltaire/wasm | Browser, Node, Bun, Deno | High performance, portable |
| Native | @tevm/voltaire/native | Bun | Maximum performance via FFI |
All three tiers expose identical APIs. Swap implementations without changing your code.
TypeScript (Default)
Pure TypeScript with zero dependencies. Works everywhere JavaScript runs.
import { Keccak256, Address, Hex } from '@tevm/voltaire'
// Keccak256.hash expects Uint8Array, Hex.fromString returns hex string
const hash = Keccak256.hash(Hex.toBytes(Hex.fromString('hello')))
const addr = Address.from('0x742d35Cc6634C0532925a3b844Bc9e7595f51e3e')
Best for:
- Universal browser/server compatibility
- Debugging and development
- Bundle size sensitive applications
- Environments without WASM support
WASM
WebAssembly bindings to Zig implementations. Portable high-performance.
import * as wasm from '@tevm/voltaire/wasm'
// Crypto operations
const hash = wasm.keccak256(data)
const recovered = wasm.secp256k1RecoverAddress(hash, signature)
// Primitives
const bytes = wasm.hexToBytes('0xdeadbeef')
const hex = wasm.bytesToHex(bytes)
// Full namespaces
wasm.Secp256k1Wasm.sign(message, privateKey)
wasm.Blake2Wasm.hash(data)
wasm.Sha256Wasm.hash(data)
Available WASM modules:
- Crypto: Keccak256, Secp256k1, SHA256, RIPEMD160, Blake2, Ed25519, X25519, P256
- Primitives: Hex, RLP, U256, Blob, AccessList, Transaction, Bytecode
Best for:
- Browser applications needing crypto performance
- Serverless/edge functions (Cloudflare Workers, Vercel Edge)
- Cross-platform consistency
Native FFI (Bun)
Direct bindings to Zig via Bun FFI. Maximum performance.
import * as native from '@tevm/voltaire/native'
// Check runtime
native.isBun() // true in Bun
native.isNode() // true in Node.js
// Keccak256 (async - lazy loads native lib)
const hash = await native.Keccak256.hash(data)
const selector = await native.Keccak256.selector('transfer(address,uint256)')
// Sync after initial load
const hash2 = native.Keccak256.hashSync(data)
Runtime support: Native FFI is Bun-only right now. In Node.js, use the regular TypeScript API or the WASM modules. Also ensure the compiled .dylib/.so/.dll is available (run zig build build-ts-native).
Best for:
- Server-side applications with heavy crypto workloads
- CLI tools
- Maximum throughput requirements
Per-Module Imports
For fine-grained control, import specific implementations:
// Keccak256 variants
import * as Keccak256 from '@tevm/voltaire/Keccak256' // Default (TS)
import * as Keccak256 from '@tevm/voltaire/Keccak256/native' // Native FFI
import * as Keccak256 from '@tevm/voltaire/Keccak256/wasm' // WASM
Performance is nuanced. WASM/Native aren’t always faster than TypeScript.
Bridging overhead: Crossing the JS↔WASM or JS↔FFI boundary has constant overhead (~1-10μs). For cheap operations (simple math, short string manipulation), this overhead can exceed the operation itself.
When WASM/Native wins:
- Cryptographic operations (keccak256, secp256k1, BLS) - 5-15x faster
- Large data encoding/decoding (RLP, ABI with big payloads)
- Batch operations that amortize bridging cost
When TypeScript wins:
- Simple operations (hex encoding small values, address validation)
- Single-item operations with low computational cost
- When avoiding async overhead matters (native FFI is async on first load)
Bundle size: For cryptography specifically, WASM is often smaller than equivalent pure-JS implementations. A full JS secp256k1 library can be 50-100KB, while WASM crypto modules are typically 20-40KB.
| Operation | TypeScript | WASM | Native | Default |
|---|
| keccak256 | 1x | ~5x | ~10x | TypeScript |
| secp256k1 sign | 1x | ~8x | ~15x | TypeScript |
| secp256k1 recover | 1x | ~8x | ~15x | TypeScript |
| RLP encode | 1x | ~1.2x | ~1.5x | TypeScript |
| Hex encode | 1x | ~1.1x | ~1.2x | TypeScript |
Benchmark your actual workload. Default implementations are chosen for common use cases, but your specific access patterns may differ.
WASM Limitations
Some cryptographic modules require native C/Rust libraries and are not available in WASM. These modules will return errors when called from the WASM entrypoint.
| Module | WASM Status | Reason | Alternative |
|---|
| BLS12-381 | ❌ Not available | Requires BLST (C library) | Use native entrypoint |
| BN254 (arkworks) | ❌ Not available | Requires Rust FFI | Pure Zig bn254 works in WASM |
| KZG | ❌ Not available | Requires c-kzg-4844 (C library) | Use native entrypoint |
| BIP-39 / HD Wallet | ❌ Not available | Requires libwally-core (C library) | Use native or JS entrypoint |
Calling unavailable modules in WASM returns error.NotSupported or equivalent errors. Check your target environment before using these modules.
What works in WASM:
- All primitives (Address, Hex, Uint, Hash, RLP, ABI, etc.)
- Keccak256, SHA256, RIPEMD160, Blake2
- secp256k1, Ed25519, P256, X25519
- Pure Zig BN254 implementation (not arkworks)
- ChaCha20-Poly1305, AES-GCM
- EIP-712 typed data signing