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.
Usage Patterns
Best practices and common patterns for using Keccak256 in Ethereum applications.Implementation Selection
Default Strategy
Start with pure TypeScript, optimize when needed:Copy
Ask AI
// 1. Start with default (universal compatibility)
import { Keccak256 } from '@tevm/voltaire/Keccak256';
const hash = Keccak256.hash(data);
// 2. Profile - is hashing a bottleneck?
// 3. If yes, switch to WASM or native
// 3a. Browser/Edge - use standalone WASM (3KB)
import { Keccak256Standalone } from '@tevm/voltaire/crypto/keccak256.standalone';
await Keccak256Standalone.init();
const hash = Keccak256Standalone.hash(data);
// 3b. Node.js/Bun - use native (fastest)
import { Keccak256 } from '@tevm/voltaire/native/crypto/keccak256';
const hash = Keccak256.hash(data);
Environment-Based Selection
Automatic selection based on runtime:Copy
Ask AI
async function getKeccak256Impl() {
// Try native first (Node.js/Bun)
if (typeof process !== 'undefined' && process.versions?.node) {
try {
const { Keccak256 } = await import('@tevm/voltaire/Keccak256/native');
return Keccak256;
} catch {
// Native not available, fall through
}
}
// Try WASM (browser/edge)
if (typeof WebAssembly !== 'undefined') {
const { Keccak256Standalone } = await import('@tevm/voltaire/Keccak256');
await Keccak256Standalone.init();
return Keccak256Standalone;
}
// Fallback to pure JS
const { Keccak256 } = await import('@tevm/voltaire/Keccak256');
return Keccak256;
}
// Use selected implementation
const Keccak256 = await getKeccak256Impl();
const hash = Keccak256.hash(data);
Bundle Size Optimization
Minimize bundle size for client deployments:Copy
Ask AI
// ❌ Large bundle - imports full primitives WASM (200KB)
import { Keccak256Wasm } from '@tevm/voltaire/Keccak256/wasm';
// ✅ Small bundle - standalone WASM (3KB)
import { Keccak256Standalone } from '@tevm/voltaire/crypto/keccak256.standalone';
// ✅ Conditional import (3KB WASM only when needed)
const Keccak256 = await import('@tevm/voltaire/Keccak256')
.then(m => m.Keccak256Standalone);
await Keccak256.init();
Type Safety
Working with Keccak256Hash
Type-safe hash handling:Copy
Ask AI
import { Keccak256 } from '@tevm/voltaire/Keccak256';
import { Hex } from '@tevm/voltaire/Hex';
// Hash returns branded type
const hash: Keccak256Hash = Keccak256.hash(data);
// Use as Uint8Array
hash[0]; // First byte
hash.length; // Always 32
hash.slice(0, 4); // First 4 bytes (selector)
hash.slice(12); // Last 20 bytes (address)
// Convert to other types
const hexHash: string = Hex.fromBytes(hash);
const bigintHash: bigint = BigInt('0x' + hexHash.slice(2));
// Pass to functions expecting Uint8Array
function processHash(h: Uint8Array) { }
processHash(hash); // ✅ Works - branded type extends Uint8Array
Type Narrowing
Ensure correct input types:Copy
Ask AI
import { Keccak256 } from '@tevm/voltaire/Keccak256';
import { Address } from '@tevm/voltaire/Address';
// ❌ Type error - string not Uint8Array
const hash = Keccak256.hash('0x1234'); // Error
// ✅ Convert first
const hash = Keccak256.hashHex('0x1234'); // OK
// ✅ Use typed primitives
const address = Address('0x742d35Cc6634C0532925a3b844Bc9e7595f51e3e');
const addressHash = Keccak256.hash(address); // OK - Address extends Uint8Array
Performance Optimization
Batch Processing
Process multiple hashes efficiently:Copy
Ask AI
import { Keccak256 } from '@tevm/voltaire/Keccak256';
// ❌ Inefficient - repeated function calls
const hashes = data.map(d => Keccak256.hash(d));
// ✅ More efficient - batch with typed array
function batchHash(inputs: readonly Uint8Array[]): Uint8Array[] {
const result = new Array(inputs.length);
for (let i = 0; i < inputs.length; i++) {
result[i] = Keccak256.hash(inputs[i]);
}
return result;
}
const hashes = batchHash(dataArray);
Caching Selectors
Cache frequently used selectors:Copy
Ask AI
import { Keccak256 } from '@tevm/voltaire/Keccak256';
import { Hex } from '@tevm/voltaire/Hex';
// Create selector cache
const SelectorCache = {
transfer: Keccak256.selector('transfer(address,uint256)'),
approve: Keccak256.selector('approve(address,uint256)'),
balanceOf: Keccak256.selector('balanceOf(address)'),
} as const;
// Use cached selectors
function buildTransferCalldata(to: Uint8Array, amount: bigint): Uint8Array {
const selector = SelectorCache.transfer; // No recomputation
const params = encodeParams(['address', 'uint256'], [to, amount]);
return concat([selector, params]);
}
Reusing Hash Results
Avoid redundant hashing:Copy
Ask AI
import { Keccak256 } from '@tevm/voltaire/Keccak256';
// ❌ Inefficient - hashes initCode twice
const create2Addr1 = Keccak256.create2Address(
deployer,
salt1,
Keccak256.hash(initCode)
);
const create2Addr2 = Keccak256.create2Address(
deployer,
salt2,
Keccak256.hash(initCode)
);
// ✅ Efficient - hash once, reuse
const initCodeHash = Keccak256.hash(initCode);
const create2Addr1 = Keccak256.create2Address(deployer, salt1, initCodeHash);
const create2Addr2 = Keccak256.create2Address(deployer, salt2, initCodeHash);
Error Handling
Input Validation
Validate inputs before hashing:Copy
Ask AI
import { Keccak256 } from '@tevm/voltaire/Keccak256';
import { Address } from '@tevm/voltaire/Address';
function computeContractAddress(
sender: Uint8Array,
nonce: bigint
): Uint8Array {
// Validate sender length
if (sender.length !== 20) {
throw new Error(`Invalid sender length: ${sender.length}, expected 20`);
}
// Validate nonce range
if (nonce < 0n) {
throw new Error(`Invalid nonce: ${nonce}, must be >= 0`);
}
return Keccak256.contractAddress(sender, nonce);
}
Catching Errors
Handle CREATE2 validation errors:Copy
Ask AI
import { Keccak256 } from '@tevm/voltaire/Keccak256';
function safeCreate2Address(
deployer: Uint8Array,
salt: Uint8Array,
initCodeHash: Uint8Array
): Uint8Array | null {
try {
return Keccak256.create2Address(deployer, salt, initCodeHash);
} catch (error) {
if (error instanceof InvalidLengthError) {
console.error('Invalid input length:', error.message);
return null;
}
throw error; // Unexpected error
}
}
Ethereum Integration
Transaction Hashing
Hash transaction data for signing:Copy
Ask AI
import { Keccak256 } from '@tevm/voltaire/Keccak256';
import { Rlp } from '@tevm/voltaire/Rlp';
// Legacy transaction hash
function hashLegacyTransaction(tx: {
nonce: bigint;
gasPrice: bigint;
gasLimit: bigint;
to: Uint8Array;
value: bigint;
data: Uint8Array;
}): Uint8Array {
const encoded = Rlp.encode([
tx.nonce,
tx.gasPrice,
tx.gasLimit,
tx.to,
tx.value,
tx.data
]);
return Keccak256.hash(encoded);
}
// EIP-1559 transaction hash
function hashEip1559Transaction(tx: {
chainId: bigint;
nonce: bigint;
maxPriorityFeePerGas: bigint;
maxFeePerGas: bigint;
gasLimit: bigint;
to: Uint8Array;
value: bigint;
data: Uint8Array;
accessList: unknown[];
}): Uint8Array {
const encoded = Rlp.encode([
tx.chainId,
tx.nonce,
tx.maxPriorityFeePerGas,
tx.maxFeePerGas,
tx.gasLimit,
tx.to,
tx.value,
tx.data,
tx.accessList
]);
// EIP-2718: prepend transaction type (0x02)
const typed = new Uint8Array([0x02, ...encoded]);
return Keccak256.hash(typed);
}
Address Derivation
Derive Ethereum address from public key:Copy
Ask AI
import { Keccak256 } from '@tevm/voltaire/Keccak256';
import { Secp256k1 } from '@tevm/voltaire/Secp256k1';
function publicKeyToAddress(publicKey: Uint8Array): Uint8Array {
// Public key must be uncompressed (65 bytes starting with 0x04)
if (publicKey.length !== 65 || publicKey[0] !== 0x04) {
throw new Error('Public key must be uncompressed (65 bytes, starts with 0x04)');
}
// Hash public key (excluding 0x04 prefix)
const pubKeyWithoutPrefix = publicKey.slice(1);
const hash = Keccak256.hash(pubKeyWithoutPrefix);
// Take last 20 bytes as address
return hash.slice(12);
}
// Derive from private key
const privateKey = new Uint8Array(32);
crypto.getRandomValues(privateKey);
const publicKey = Secp256k1.derivePublicKey(privateKey, false); // uncompressed
const address = publicKeyToAddress(publicKey);
Contract Interaction
Build contract calls with selectors:Copy
Ask AI
import { Keccak256 } from '@tevm/voltaire/Keccak256';
import { Abi } from '@tevm/voltaire/Abi';
function encodeContractCall(
signature: string,
types: string[],
values: unknown[]
): Uint8Array {
// Get selector
const selector = Keccak256.selector(signature);
// Encode parameters
const params = Abi.encodeParams(types, values);
// Combine selector + params
const calldata = new Uint8Array(selector.length + params.length);
calldata.set(selector, 0);
calldata.set(params, selector.length);
return calldata;
}
// Build transfer call
const transferCall = encodeContractCall(
'transfer(address,uint256)',
['address', 'uint256'],
[recipientAddress, transferAmount]
);
// Send transaction
await provider.sendTransaction({
to: tokenAddress,
data: transferCall
});
Event Filtering
Filter logs by event signature:Copy
Ask AI
import { Keccak256 } from '@tevm/voltaire/Keccak256';
import { Hex } from '@tevm/voltaire/Hex';
async function getTransferLogs(
tokenAddress: string,
fromBlock: number,
toBlock: number
): Promise<Log[]> {
// Compute Transfer event topic
const transferTopic = Hex.fromBytes(
Keccak256.topic('Transfer(address,address,uint256)')
);
// Query logs
return await provider.getLogs({
address: tokenAddress,
topics: [transferTopic],
fromBlock,
toBlock
});
}
// Get all transfers
const logs = await getTransferLogs(
'0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC
0,
'latest'
);
// Process logs
for (const log of logs) {
const from = '0x' + log.topics[1].slice(26); // Remove padding
const to = '0x' + log.topics[2].slice(26);
console.log(`Transfer from ${from} to ${to}`);
}
Security Best Practices
Signature Normalization
Always normalize function/event signatures:Copy
Ask AI
import { Keccak256 } from '@tevm/voltaire/Keccak256';
// ❌ Dangerous - user input not normalized
function getSelectorUnsafe(userInput: string): Uint8Array {
return Keccak256.selector(userInput);
}
// ✅ Safe - normalize signature
function getSelectorSafe(signature: string): Uint8Array {
// Remove whitespace
const normalized = signature.replace(/\s/g, '');
// Validate format: name(type1,type2,...)
if (!/^[a-zA-Z_][a-zA-Z0-9_]*\([a-zA-Z0-9_,\[\]]*\)$/.test(normalized)) {
throw new Error(`Invalid signature format: ${signature}`);
}
return Keccak256.selector(normalized);
}
Input Sanitization
Validate inputs before hashing:Copy
Ask AI
import { Keccak256 } from '@tevm/voltaire/Keccak256';
function hashUserData(data: unknown): Uint8Array {
// ❌ Unsafe - no validation
// return Keccak256.hash(data as Uint8Array);
// ✅ Safe - validate type
if (!(data instanceof Uint8Array)) {
throw new TypeError('Data must be Uint8Array');
}
// ✅ Safe - validate length
if (data.length === 0) {
throw new Error('Data cannot be empty');
}
return Keccak256.hash(data);
}
Constant-Time Comparisons
Compare hashes in constant time:Copy
Ask AI
import { Keccak256 } from '@tevm/voltaire/Keccak256';
// ❌ Timing attack vulnerable
function verifyHashUnsafe(data: Uint8Array, expectedHash: Uint8Array): boolean {
const hash = Keccak256.hash(data);
return hash.toString() === expectedHash.toString(); // Early exit on mismatch
}
// ✅ Constant-time comparison
function verifyHashSafe(data: Uint8Array, expectedHash: Uint8Array): boolean {
const hash = Keccak256.hash(data);
if (hash.length !== expectedHash.length) {
return false;
}
let result = 0;
for (let i = 0; i < hash.length; i++) {
result |= hash[i] ^ expectedHash[i];
}
return result === 0;
}
Testing
Test Vectors
Validate implementation with known vectors:Copy
Ask AI
import { Keccak256 } from '@tevm/voltaire/Keccak256';
import { Hex } from '@tevm/voltaire/Hex';
import { describe, it, expect } from 'vitest';
describe('Keccak256', () => {
it('produces correct hash for empty string', () => {
const hash = Keccak256.hashString('');
expect(Hex.fromBytes(hash)).toBe(
'0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470'
);
});
it('produces correct hash for "abc"', () => {
const hash = Keccak256.hashString('abc');
expect(Hex.fromBytes(hash)).toBe(
'0x4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45'
);
});
it('produces correct transfer selector', () => {
const selector = Keccak256.selector('transfer(address,uint256)');
expect(Hex.fromBytes(selector)).toBe('0xa9059cbb');
});
});
Property Testing
Test hash properties:Copy
Ask AI
import { Keccak256 } from '@tevm/voltaire/Keccak256';
import { describe, it, expect } from 'vitest';
describe('Keccak256 properties', () => {
it('always produces 32-byte output', () => {
const inputs = [
new Uint8Array(0),
new Uint8Array(1),
new Uint8Array(100),
new Uint8Array(10000)
];
for (const input of inputs) {
const hash = Keccak256.hash(input);
expect(hash.length).toBe(32);
}
});
it('is deterministic', () => {
const data = new Uint8Array([1, 2, 3, 4, 5]);
const hash1 = Keccak256.hash(data);
const hash2 = Keccak256.hash(data);
expect(hash1).toEqual(hash2);
});
it('produces different hashes for different inputs', () => {
const data1 = new Uint8Array([1, 2, 3]);
const data2 = new Uint8Array([1, 2, 4]);
const hash1 = Keccak256.hash(data1);
const hash2 = Keccak256.hash(data2);
expect(hash1).not.toEqual(hash2);
});
});
Common Pitfalls
SHA-3 vs Keccak Confusion
Copy
Ask AI
import { Keccak256 } from '@tevm/voltaire/Keccak256';
import { sha3_256 } from '@noble/hashes/sha3';
// ❌ Wrong - SHA-3 is NOT Keccak-256
const wrongHash = sha3_256(data); // Different padding
// ✅ Correct - Use Keccak-256
const correctHash = Keccak256.hash(data);
// Ethereum uses original Keccak, not finalized SHA-3
Signature Format Errors
Copy
Ask AI
import { Keccak256 } from '@tevm/voltaire/Keccak256';
// ❌ Wrong - space in signature
const wrong1 = Keccak256.selector('transfer(address, uint256)');
// ❌ Wrong - parameter names included
const wrong2 = Keccak256.selector('transfer(address to, uint256 amount)');
// ❌ Wrong - non-canonical type
const wrong3 = Keccak256.selector('transfer(address,uint)');
// ✅ Correct - canonical signature
const correct = Keccak256.selector('transfer(address,uint256)');
Address Derivation Errors
Copy
Ask AI
import { Keccak256 } from '@tevm/voltaire/Keccak256';
// ❌ Wrong - hashing compressed public key
const compressedPubKey = new Uint8Array(33); // 33 bytes
const wrongAddr = Keccak256.hash(compressedPubKey).slice(12);
// ❌ Wrong - including 0x04 prefix
const uncompressedPubKey = new Uint8Array(65);
const wrongAddr2 = Keccak256.hash(uncompressedPubKey).slice(12);
// ✅ Correct - hash uncompressed key without 0x04 prefix
const correctAddr = Keccak256.hash(uncompressedPubKey.slice(1)).slice(12);
Related
- Core Hashing Methods - hash, hashString, hashHex, hashMultiple
- Ethereum Methods - selector, topic, contractAddress, create2Address
- Implementations - Implementation comparison and selection
- Keccak256 Overview - Main documentation

