Common utility functions for python code that interacts with Ethereum
—
Hex string processing including encoding, decoding, prefix handling, and validation. Essential for working with Ethereum's hex-encoded data formats.
Convert between bytes and hex string representations.
def encode_hex(value) -> str:
"""
Encode bytes or integer to 0x-prefixed hex string.
Args:
value: Bytes, bytearray, or integer to encode
Returns:
str: 0x-prefixed hex string
Raises:
TypeError: If value cannot be hex-encoded
"""
def decode_hex(value: str) -> bytes:
"""
Decode hex string to bytes.
Args:
value (str): Hex string (with or without 0x prefix)
Returns:
bytes: Decoded byte data
Raises:
ValidationError: If hex string is invalid
"""Check if values are valid hex strings or contain hex characters.
def is_hex(value) -> bool:
"""
Check if value contains only valid hex characters.
Args:
value: Value to check
Returns:
bool: True if value contains only hex characters (0-9, a-f, A-F)
"""
def is_hexstr(value) -> bool:
"""
Check if value is a valid hex string.
Args:
value: Value to check
Returns:
bool: True if value is a string with valid hex format
"""Handle 0x prefix addition and removal.
def is_0x_prefixed(value: str) -> bool:
"""
Check if string has 0x prefix.
Args:
value (str): String to check
Returns:
bool: True if string starts with '0x' or '0X'
"""
def add_0x_prefix(value: str) -> str:
"""
Add 0x prefix to hex string if not present.
Args:
value (str): Hex string
Returns:
str: Hex string with 0x prefix
"""
def remove_0x_prefix(value: str) -> str:
"""
Remove 0x prefix from hex string.
Args:
value (str): Hex string (with or without 0x prefix)
Returns:
str: Hex string without 0x prefix
Raises:
ValidationError: If string doesn't have 0x prefix
"""from eth_utils import encode_hex, decode_hex, is_hex, is_hexstr
# Encode bytes to hex
data = b"Hello, World!"
hex_string = encode_hex(data)
print(hex_string) # 0x48656c6c6f2c20576f726c6421
# Decode hex back to bytes
decoded = decode_hex(hex_string)
print(decoded) # b'Hello, World!'
# Encode integer to hex
number = 12345
hex_number = encode_hex(number)
print(hex_number) # 0x3039
# Validation
print(is_hex("abc123")) # True
print(is_hex("xyz")) # False
print(is_hexstr("0x123")) # True
print(is_hexstr("123")) # True (hex chars, no prefix required)from eth_utils import encode_hex, decode_hex, add_0x_prefix
# Process transaction hash
tx_hash_bytes = bytes.fromhex("1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef")
tx_hash_hex = encode_hex(tx_hash_bytes)
print(tx_hash_hex) # 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef
# Process contract bytecode (might come without prefix)
bytecode = "608060405234801561001057600080fd5b50"
prefixed_bytecode = add_0x_prefix(bytecode)
print(prefixed_bytecode) # 0x608060405234801561001057600080fd5b50
# Decode for analysis
bytecode_bytes = decode_hex(prefixed_bytecode)
print(f"Bytecode length: {len(bytecode_bytes)} bytes")from eth_utils import is_0x_prefixed, add_0x_prefix, remove_0x_prefix
def normalize_hex_input(hex_input):
"""Ensure hex string has 0x prefix."""
if not is_0x_prefixed(hex_input):
return add_0x_prefix(hex_input)
return hex_input
def clean_hex_for_storage(hex_input):
"""Remove 0x prefix for compact storage."""
if is_0x_prefixed(hex_input):
return remove_0x_prefix(hex_input)
return hex_input
# Usage examples
raw_hex = "1234abcd"
normalized = normalize_hex_input(raw_hex)
print(normalized) # 0x1234abcd
prefixed_hex = "0x1234abcd"
cleaned = clean_hex_for_storage(prefixed_hex)
print(cleaned) # 1234abcdfrom eth_utils import encode_hex, decode_hex, is_hexstr, ValidationError
def process_hex_data(data_input):
"""Process various hex data formats."""
if isinstance(data_input, bytes):
# Already bytes, encode to hex for display
return encode_hex(data_input)
elif isinstance(data_input, str) and is_hexstr(data_input):
# Hex string, decode and re-encode for normalization
try:
decoded = decode_hex(data_input)
return encode_hex(decoded)
except ValidationError:
raise ValueError(f"Invalid hex string: {data_input}")
elif isinstance(data_input, int):
# Integer, encode to hex
return encode_hex(data_input)
else:
raise TypeError(f"Unsupported data type: {type(data_input)}")
# Examples
print(process_hex_data(b"test")) # 0x74657374
print(process_hex_data("0x74657374")) # 0x74657374
print(process_hex_data("74657374")) # 0x74657374
print(process_hex_data(123)) # 0x7bfrom eth_utils import encode_hex, decode_hex, keccak, to_bytes
def create_contract_address(sender_address, nonce):
"""Generate contract address from sender and nonce."""
# Remove 0x prefix for processing
sender_bytes = decode_hex(sender_address)
# Encode nonce (simple case for small nonces)
if nonce == 0:
nonce_bytes = b''
else:
nonce_bytes = to_bytes(primitive=nonce)
# RLP encode (simplified for example)
rlp_encoded = sender_bytes + nonce_bytes
# Keccak hash and take last 20 bytes
hash_result = keccak(rlp_encoded)
contract_address = hash_result[-20:]
return encode_hex(contract_address)
# Example usage
sender = "0xd3CdA913deB6f67967B99D67aCDFa1712C293601"
contract_addr = create_contract_address(sender, 0)
print(f"Contract address: {contract_addr}")from eth_utils import is_hex, is_hexstr, is_0x_prefixed
def validate_hex_input(hex_string, expected_length=None):
"""Validate hex string input with optional length check."""
if not isinstance(hex_string, str):
raise TypeError("Input must be string")
if not is_hexstr(hex_string):
raise ValueError("Input is not valid hex string")
# Clean for length check
clean_hex = hex_string[2:] if is_0x_prefixed(hex_string) else hex_string
if expected_length and len(clean_hex) != expected_length:
raise ValueError(f"Expected {expected_length} hex chars, got {len(clean_hex)}")
return True
def format_hex_display(hex_string, max_length=10):
"""Format hex string for display with truncation."""
if not is_0x_prefixed(hex_string):
hex_string = f"0x{hex_string}"
if len(hex_string) > max_length:
visible_chars = max_length - 6 # Account for "0x" and "..."
return f"{hex_string[:2+visible_chars//2]}...{hex_string[-visible_chars//2:]}"
return hex_string
# Examples
validate_hex_input("0x1234") # True
validate_hex_input("1234") # True
validate_hex_input("0x1234", 4) # True (4 hex chars)
print(format_hex_display("0x1234567890abcdef1234567890abcdef")) # 0x12...effrom eth_utils import decode_hex, encode_hex, ValidationError
def safe_hex_decode(hex_input):
"""Safely decode hex string with error handling."""
try:
return decode_hex(hex_input)
except ValidationError as e:
print(f"Failed to decode hex: {e}")
return None
def ensure_hex_format(data):
"""Ensure data is in hex string format."""
if isinstance(data, bytes):
return encode_hex(data)
elif isinstance(data, str) and is_hexstr(data):
return add_0x_prefix(data) if not is_0x_prefixed(data) else data
else:
raise TypeError("Cannot convert to hex format")from eth_utils import decode_hex, is_0x_prefixed
def hex_equal(hex1, hex2):
"""Compare two hex strings for equality (prefix-agnostic)."""
# Normalize both to bytes
bytes1 = decode_hex(hex1)
bytes2 = decode_hex(hex2)
return bytes1 == bytes2
# Usage
print(hex_equal("0x1234", "1234")) # True
print(hex_equal("0x1234", "0x1234")) # True
print(hex_equal("1234", "1234")) # True