Try it Live Run Bytes examples in the interactive playground
Most Important Bytes Type - Bytes32 is THE fundamental 256-bit type in Ethereum. Used everywhere: storage slots, hashes, merkle trees, and generic 32-byte values.
Overview
Bytes32 is a branded Uint8Array specialization of the generic Bytes<N> type, representing exactly 32 bytes (256 bits). It’s the most common fixed-size type in Ethereum and provides strict type safety for 256-bit values through Symbol-based branding.
See the Bytes overview for generic Bytes<N> documentation and other size specializations.
import * as Bytes32 from 'tevm/primitives/Bytes/Bytes32' ;
// Storage slot
const slot = Bytes32 ( 0 );
// Keccak256 hash output (32 bytes, same size as Bytes32)
const hash = Keccak256 . hash ( data );
const hashAsBytes = Bytes32 ( hash );
// Convert to/from bigint
const value = Bytes32 . toBigint ( slot );
// Extract address (last 20 bytes)
const addr = Bytes32 . toAddress ( hashAsBytes );
Why Bytes32 is Critical
Ethereum’s fundamental 256-bit type:
Storage slots - EVM storage uses 32-byte slots
Hashes - Keccak256 outputs 32 bytes
Merkle trees - Node values are 32 bytes
Generic values - Most fixed-size data is 32 bytes
ABI encoding - Uint256, bytes32, etc. use 32 bytes
// Storage slot access
const slot = Bytes32 ( 0 );
// Hash results (same size as Bytes32)
const hash = Keccak256 . hash ( data );
const bytes = Bytes32 ( hash );
// Merkle tree nodes
const left = Bytes32 ( '0x...' );
const right = Bytes32 ( '0x...' );
Difference from Keccak256Hash
Both are 32 bytes, but semantic meaning differs:
import { Keccak256 } from 'tevm/Keccak256' ;
import { Bytes32 } from 'tevm/Bytes32' ;
// Keccak256Hash = keccak256 output (semantic)
const hash = Keccak256 . hash ( data );
// Bytes32 = generic 32-byte value
const slot = Bytes32 ( 0 );
// Convert between them
const hashAsBytes = Bytes32 ( hash );
const bytesAsHash = Keccak256 . from ( slot );
When to use each:
Keccak256Hash - Cryptographic hash outputs (keccak256 results)
Bytes32 - Storage slots, generic 32-byte values, numeric conversions
Type Safety
Bytes32 is a specialization of Bytes<32> using Symbol-based branding for compile-time type safety with zero runtime overhead:
import type { brand } from './brand.js' ;
type Bytes32Type = Uint8Array & {
readonly [ brand ] : "Bytes32" ;
readonly size : 32 ;
};
The brand symbol is a unique TypeScript symbol that creates nominal typing - structurally identical types are incompatible if they have different brands:
const bytes32 = Bytes32 ( 42 );
const bytes16 = Bytes16 ( '0x' + '12' . repeat ( 16 ));
// Type error: cannot assign Bytes16 to Bytes32
const wrong : Bytes32 . Bytes32Type = bytes16 ;
This branding system provides:
Compile-time safety - Cannot mix different sized Bytes types
Zero runtime cost - Brand only exists in TypeScript, erased after compilation
Hardcoded length - Type includes { readonly size: 32 } for static length checking
Hex String Representation
For hex string representations, use the HexBytes32 type which extends branded hex strings with size information:
import type { HexBytes32 } from 'tevm/primitives/Hex' ;
// HexBytes32 is a string type with branding
type HexBytes32 = `0x ${ string } ` & {
readonly [ brand ] : "Hex" ;
readonly size : 32 ;
};
// Convert between Bytes32 and HexBytes32
const bytes = Bytes32 ( 42 );
const hex : HexBytes32 = Bytes32 . toHex ( bytes ); // "0x00...002a"
const restored = Bytes32 . fromHex ( hex );
Case Sensitivity
HexBytes32 supports case variants through additional branding:
// Uppercase hex digits (A-F)
type HexBytes32Uppercase = HexBytes32 & {
readonly [ Symbol . for ( "uppercase" )] : true ;
};
// Lowercase hex digits (a-f)
type HexBytes32Lowercase = HexBytes32 & {
readonly [ Symbol . for ( "lowercase" )] : true ;
};
// Mixed case (EIP-55 checksummed for addresses)
type HexBytes32Mixed = HexBytes32 & {
readonly [ Symbol . for ( "uppercase" )] : false ;
readonly [ Symbol . for ( "lowercase" )] : false ;
};
Most Ethereum tooling uses lowercase hex by default, but uppercase is common in certain contexts (like BLS signatures).
Constructors
from
Universal constructor accepting multiple types:
// From hex
const b1 = Bytes32 ( '0x' + '12' . repeat ( 32 ));
// From bytes
const b2 = Bytes32 ( Bytes32 ());
// From number (padded)
const b3 = Bytes32 ( 42 );
// From bigint (padded)
const b4 = Bytes32 ( 123456789012345678901234567890 n );
fromHex
From hex string with strict validation:
const bytes = Bytes32 ( '0x' + 'ab' . repeat ( 32 ));
// Without 0x prefix
const bytes2 = Bytes32 ( '12' . repeat ( 32 ));
// Throws on wrong length
Bytes32 ( '0x1234' ); // Error: must be 64 hex chars
fromBytes
From Uint8Array with size check:
const arr = Bytes32 ();
const bytes = Bytes32 ( arr );
// Throws on wrong size
Bytes32 ( new Uint8Array ( 31 )); // Error
fromNumber
From number with padding (big-endian):
const slot = Bytes32 ( 0 );
const value = Bytes32 ( 42 );
// Padded to 32 bytes
console . log ( value [ 31 ]); // 42
console . log ( value [ 30 ]); // 0
fromBigint
From bigint with padding (big-endian):
const large = Bytes32 ( 123456789012345678901234567890 n );
// Roundtrip conversion
const original = 42 n ;
const bytes = Bytes32 ( original );
const restored = Bytes32 . toBigint ( bytes );
console . log ( restored === original ); // true
zero
Create zero-filled Bytes32:
const zeros = Bytes32 . zero ();
console . log ( Bytes32 . isZero ( zeros )); // true
// Useful for default storage slots
const defaultSlot = Bytes32 . zero ();
Conversions
toHex
Convert to hex string:
const bytes = Bytes32 ( 255 );
const hex = Bytes32 . toHex ( bytes );
console . log ( hex ); // "0x00...00ff"
toUint8Array
Convert to raw bytes:
const bytes = Bytes32 ( 42 );
const arr = Bytes32 . toUint8Array ( bytes );
console . log ( arr instanceof Uint8Array ); // true
toBigint
Convert to bigint (big-endian):
const bytes = Bytes32 ( 42 );
const value = Bytes32 . toBigint ( bytes );
console . log ( value ); // 42n
// Works with large values
const large = Bytes32 ( '0x' + 'ff' . repeat ( 32 ));
const bigValue = Bytes32 . toBigint ( large );
toKeccak256Hash
Convert to Keccak256Hash (semantic change):
import { Keccak256 } from 'tevm/Keccak256' ;
const slot = Bytes32 ( 0 );
const hash = Keccak256 . from ( slot );
// Same bytes, different type
console . log ( hash . length ); // 32
toAddress
Extract address from last 20 bytes:
const bytes = Bytes32 ();
// Set last 20 bytes to 0xff
for ( let i = 12 ; i < 32 ; i ++ ) {
bytes [ i ] = 0xff ;
}
const b32 = Bytes32 ( bytes );
const addr = Bytes32 . toAddress ( b32 );
console . log ( addr . length ); // 20
// Useful for storage slot address extraction
const storageSlot = Bytes32 ( '0x' + '00' . repeat ( 12 ) + 'aa' . repeat ( 20 ));
const extractedAddr = Bytes32 . toAddress ( storageSlot );
Operations
equals
Check equality:
const a = Bytes32 ( 42 );
const b = Bytes32 ( 42 );
console . log ( Bytes32 . equals ( a , b )); // true
compare
Compare two values:
const a = Bytes32 ( 1 );
const b = Bytes32 ( 2 );
console . log ( Bytes32 . compare ( a , b )); // -1 (a < b)
clone
Create independent copy:
const original = Bytes32 ( 42 );
const copy = Bytes32 . clone ( original );
isZero
Check if all zeros:
const zeros = Bytes32 . zero ();
console . log ( Bytes32 . isZero ( zeros )); // true
// Useful for empty storage slots
const slot = getStorageSlot ( address , 0 );
if ( Bytes32 . isZero ( slot )) {
console . log ( 'Storage slot is empty' );
}
size
Get size (always 32):
const bytes = Bytes32 ( 42 );
console . log ( Bytes32 . size ( bytes )); // 32
Constants
SIZE
Bytes32 size in bytes:
console . log ( Bytes32 . SIZE ); // 32
ZERO
Zero-filled constant:
console . log ( Bytes32 . ZERO . length ); // 32
console . log ( Bytes32 . isZero ( Bytes32 . ZERO )); // true
Error Handling
Bytes32 validates size strictly:
try {
Bytes32 ( new Uint8Array ( 31 ));
} catch ( error ) {
console . log ( error . code ); // "BYTES32_INVALID_LENGTH"
console . log ( error . message ); // "Bytes32 must be 32 bytes, got 31"
}
try {
Bytes32 ( '0x1234' );
} catch ( error ) {
console . log ( error . code ); // "BYTES32_INVALID_HEX_LENGTH"
console . log ( error . message ); // "Bytes32 hex must be 64 characters..."
}
Ethereum Use Cases
Storage Slots
// Slot 0
const slot0 = Bytes32 ( 0 );
// Slot for address mapping
function mappingSlot ( key : Address , slot : number ) : Bytes32 {
const data = concat ([ key , Bytes32 ( slot )]);
const hash = Keccak256 . hash ( data );
return Bytes32 ( hash );
}
Merkle Trees
// Merkle tree node
const leftNode = Bytes32 ( '0x...' );
const rightNode = Bytes32 ( '0x...' );
// Compute parent
const parent = Keccak256 . hash ( concat ([ leftNode , rightNode ]));
const parentNode = Bytes32 ( parent );
Generic 256-bit Values
// Block hash
const blockHash = Bytes32 ( '0x...' );
// Transaction hash
const txHash = Bytes32 ( '0x...' );
// State root
const stateRoot = Bytes32 ( '0x...' );
Bytes - Generic Bytes<N> type documentation
Keccak256 - Cryptographic hashes (also 32 bytes)
Bytes16 - 128-bit fixed-size bytes
Bytes64 - 512-bit fixed-size bytes
Uint - 256-bit unsigned integers
Address - 160-bit Ethereum addresses