Skip to main content

Try it Live

Run Keccak256 examples in the interactive playground
Future Plans: This page is planned and under active development. Examples are placeholders and will be replaced with accurate, tested content.
View executable examples: hash-bytes.ts | hash-string.ts | hash-hex.ts | merkle-tree.ts
Semantic Hash Type - Keccak256Hash extends Bytes32 with a keccak256 semantic flag. Same runtime representation (32 bytes), different compile-time meaning for type safety.

Overview

Keccak256Hash is a branded Uint8Array that extends Bytes32 with additional semantic meaning. It represents the output of a keccak256 hash operation through TypeScript branding. While Bytes32 is a generic 32-byte value, Keccak256Hash explicitly communicates “this came from a keccak256 operation” at compile-time with zero runtime overhead.
import { Keccak256 } from '@tevm/voltaire/Keccak256';
import * as Bytes32 from '@tevm/voltaire/Bytes32';

// Keccak256Hash output
const hash = Keccak256.hash(data);

// Convert to Bytes32 for generic operations
const bytes = Bytes32.from(hash);

// Both are 32 bytes at runtime
console.log(hash.length);  // 32
console.log(bytes.length); // 32

Type Definition

Keccak256Hash extends Bytes32 with a keccak256 semantic symbol:
import type { brand } from '@tevm/voltaire/brand';
import type { BrandedBytes } from '@tevm/voltaire/Bytes';

type Keccak256Hash = BrandedBytes<32> & {
  readonly [Symbol.for("keccak256")]: true;
};
Type structure:
  • Base: Uint8Array (32 bytes)
  • Brand: { readonly [brand]: "Bytes32" }
  • Size: { readonly size: 32 }
  • Semantic: { readonly [Symbol.for("keccak256")]: true }
This layered branding provides:
  • Runtime: Plain Uint8Array (zero overhead)
  • Compile-time: Type safety preventing mixing hash types
  • Semantic: Documents this value came from keccak256

Relationship to Bytes32

Keccak256Hash and Bytes32 are the same size (32 bytes) but convey different semantics:
import { Keccak256 } from '@tevm/voltaire/Keccak256';
import * as Bytes32 from '@tevm/voltaire/Bytes32';

// Keccak256Hash = semantic "this is a keccak256 hash"
const txHash: Keccak256Hash = Keccak256.hash(txData);

// Bytes32 = generic 32-byte value
const storageSlot: Bytes32Type = Bytes32.from(0);

// Same runtime representation
console.log(txHash instanceof Uint8Array);      // true
console.log(storageSlot instanceof Uint8Array); // true

// Different compile-time types
type HashType = typeof txHash;      // Keccak256Hash
type BytesType = typeof storageSlot; // Bytes32Type
When to use each:
  • Keccak256Hash - Transaction hashes, block hashes, merkle nodes, keccak256 outputs
  • Bytes32 - Storage slots, generic 32-byte values, numeric conversions
See Bytes32 documentation for generic 32-byte operations.

Migration from Hash Primitive

Old Hash primitive replaced by Keccak256Hash for clarity:
// OLD (deprecated Hash primitive)
import { Keccak256 } from '@tevm/voltaire/Keccak256';
const hash = Keccak256.hash(data);

// NEW (Keccak256Hash type)
import { Keccak256 } from '@tevm/voltaire/Keccak256';
const hash = Keccak256.hash(data);
Migration steps:
  1. Replace Keccak256.hash() with Keccak256.hash()
  2. Replace HashType type with Keccak256Hash
  3. All operations remain the same (same 32-byte structure)
The new approach is more explicit about which hash algorithm produced the value.

When to Use Keccak256Hash vs Bytes32

Use Keccak256Hash when:

// Transaction hashes
const txHash = Keccak256.hash(txData);

// Block hashes
const blockHash = Keccak256.hash(blockHeader);

// Merkle tree nodes
const merkleNode = Keccak256.hashMultiple([left, right]);

// Event topics
const topic = Keccak256.topic('Transfer(address,address,uint256)');

// Any value that came from keccak256
const digest = Keccak256.hash(message);

Use Bytes32 when:

import * as Bytes32 from '@tevm/voltaire/Bytes32';

// Storage slots
const slot = Bytes32.from(0);

// Generic 32-byte values
const padding = Bytes32.zero();

// Numeric conversions
const value = Bytes32.from(42n);

// When hash algorithm doesn't matter
const genericHash: Bytes32.Bytes32Type = Bytes32.from(anyHash);

Constructors

All Keccak256Hash values come from Keccak256 module methods:

Keccak256.hash

Direct hashing of bytes:
import { Keccak256 } from '@tevm/voltaire/Keccak256';

const data = new Uint8Array([1, 2, 3, 4, 5]);
const hash: Keccak256Hash = Keccak256.hash(data);

Keccak256.hashString

Hash UTF-8 string:
const hash: Keccak256Hash = Keccak256.hashString('hello world');

Keccak256.hashHex

Hash hex-encoded string:
const hash: Keccak256Hash = Keccak256.hashHex('0xdeadbeef');

Keccak256.hashMultiple

Hash multiple chunks:
const hash: Keccak256Hash = Keccak256.hashMultiple([chunk1, chunk2, chunk3]);

Keccak256.topic

Event topic (full 32-byte hash):
const topic: Keccak256Hash = Keccak256.topic('Transfer(address,address,uint256)');
See Keccak256 index for all hash methods.

Conversions

Keccak256Hash can be converted using Bytes32 operations:

toHex

Convert to hex string:
import * as Bytes32 from '@tevm/voltaire/Bytes32';

const hash = Keccak256.hash(data);
const hex = Bytes32.toHex(hash);
// "0x..." (64 hex characters)

toBytes

Convert to plain Uint8Array:
const bytes = Bytes32.toUint8Array(hash);
console.log(bytes instanceof Uint8Array); // true

toBigint

Convert to bigint (big-endian):
const value = Bytes32.toBigint(hash);
console.log(typeof value); // "bigint"

toAddress

Extract address (last 20 bytes):
import { Keccak256 } from '@tevm/voltaire/Keccak256';

// Ethereum address derivation
const pubKeyHash = Keccak256.hash(publicKey);
const address = Bytes32.toAddress(pubKeyHash);

Operations

equals

Check equality:
import * as Bytes32 from '@tevm/voltaire/primitives/Bytes/Bytes32';

const hash1 = Keccak256.hash(data1);
const hash2 = Keccak256.hash(data2);

const same = Bytes32.equals(hash1, hash2);

compare

Compare two hashes:
const cmp = Bytes32.compare(hash1, hash2);
// -1 if hash1 < hash2
//  0 if hash1 === hash2
//  1 if hash1 > hash2

isZero

Check if all zeros (rare for cryptographic hashes):
const isEmpty = Bytes32.isZero(hash);

clone

Create independent copy:
const copy = Bytes32.clone(hash);
See Bytes32 documentation for all operations.

Ethereum Use Cases

Transaction Hashes

import { Keccak256 } from '@tevm/voltaire/Keccak256';
import * as Transaction from '@tevm/voltaire/Transaction';

// Hash transaction for signing
const tx = Transaction.from({
  to: '0x742d35Cc6634C0532925a3b844Bc9e7595f51e3e',
  value: 1000000000000000000n,
  nonce: 5n,
  gasLimit: 21000n,
  maxFeePerGas: 20000000000n,
  maxPriorityFeePerGas: 1000000000n,
});

const txHash: Keccak256Hash = Keccak256.hash(Transaction.toBytes(tx));

Block Hashes

// Block header hash
const blockHeaderData = encodeBlockHeader(block);
const blockHash: Keccak256Hash = Keccak256.hash(blockHeaderData);

Merkle Tree Nodes

// Compute merkle parent node
const leftNode = Keccak256.hash(leftData);
const rightNode = Keccak256.hash(rightData);

// Parent = keccak256(left || right)
const parentNode: Keccak256Hash = Keccak256.hashMultiple([leftNode, rightNode]);

Event Topics

// Event signature hash
const transferTopic: Keccak256Hash = Keccak256.topic('Transfer(address,address,uint256)');

// Used in log filtering
const logs = await provider.getLogs({
  topics: [transferTopic],
  address: tokenAddress,
});

Address Derivation

import * as Bytes32 from '@tevm/voltaire/Bytes32';

// Ethereum address = last 20 bytes of keccak256(publicKey)
const pubKeyHash: Keccak256Hash = Keccak256.hash(publicKey);
const address = Bytes32.toAddress(pubKeyHash);

Storage Proofs

// Storage slot key
const storageKey = Keccak256.hashMultiple([
  address,
  Bytes32.from(slot),
]);

// Merkle proof verification
function verifyProof(leaf: Keccak256Hash, proof: Keccak256Hash[], root: Keccak256Hash): boolean {
  let current = leaf;

  for (const sibling of proof) {
    current = Keccak256.hashMultiple([current, sibling]);
  }

  return Bytes32.equals(current, root);
}