Common utility functions for python code that interacts with Ethereum
—
Functional programming utilities including curried functions, return value transformers, and functional composition tools for advanced data processing patterns.
Transform function return values with decorators.
def apply_to_return_value(callback):
"""
Decorator to apply callback to function return value.
Args:
callback: Function to apply to return value
Returns:
Decorator function
"""
def to_tuple(func):
"""Decorator to convert return value to tuple."""
def to_list(func):
"""Decorator to convert return value to list."""
def to_set(func):
"""Decorator to convert return value to set."""
def to_dict(func):
"""Decorator to convert return value to dict."""
def to_ordered_dict(func):
"""Decorator to convert return value to OrderedDict."""
def flatten_return(func):
"""Decorator to flatten returned iterables."""
def reversed_return(func):
"""Decorator to reverse returned sequences."""
def sort_return(func):
"""Decorator to sort returned sequences."""Access curried versions of functions for functional composition.
from eth_utils.curried import (
# Address functions
to_checksum_address, is_checksum_address,
# Conversion functions
to_bytes, to_hex, to_int, to_text,
# ABI functions
filter_abi_by_type, filter_abi_by_name,
# Formatting functions
apply_formatter_if, apply_formatters_to_dict,
# Currency functions
from_wei, to_wei,
# Other utilities
clamp, get_logger
)from eth_utils import to_list, to_tuple, flatten_return, sort_return
@to_list
def get_transaction_hashes():
"""Get transaction hashes as generator, return as list."""
for i in range(5):
yield f"0x{i:064x}"
@to_tuple
def get_address_components(address):
"""Split address into components, return as tuple."""
return [address[:2], address[2:10], address[10:]]
@flatten_return
def get_nested_data():
"""Return nested data, flattened."""
return [[1, 2], [3, 4], [5, 6]]
@sort_return
def get_unsorted_addresses():
"""Return addresses in sorted order."""
return [
"0xd3CdA913deB6f67967B99D67aCDFa1712C293601",
"0x742d35cc6634c0532925a3b8c17b1e8b4e1d1123",
"0xa1b2c3d4e5f6789012345678901234567890abcd"
]
# Usage
tx_hashes = get_transaction_hashes() # Returns list instead of generator
print(type(tx_hashes)) # <class 'list'>
components = get_address_components("0x742d35cc6634c0532925a3b8c17b1e8b4e1d1123")
print(components) # ('0x', '742d35cc', '6634c053...')
flat_data = get_nested_data() # Returns [1, 2, 3, 4, 5, 6]
sorted_addrs = get_unsorted_addresses() # Returns sorted listfrom eth_utils.curried import to_checksum_address, filter_abi_by_type, apply_formatter_if, is_string
from functools import partial
# Create specialized formatters using curried functions
format_address = apply_formatter_if(is_string, to_checksum_address)
get_functions = filter_abi_by_type("function")
get_events = filter_abi_by_type("event")
# Use in data processing pipeline
def process_contract_data(contract_abi, addresses):
"""Process contract ABI and addresses with functional style."""
# Extract functions and events using curried functions
functions = get_functions(contract_abi)
events = get_events(contract_abi)
# Format all addresses using curried formatter
formatted_addresses = [format_address(addr) for addr in addresses]
return {
"functions": functions,
"events": events,
"addresses": formatted_addresses
}
# Example usage
contract_abi = [
{"type": "function", "name": "transfer"},
{"type": "event", "name": "Transfer"}
]
addresses = [
"0xd3cda913deb6f67967b99d67acdfa1712c293601",
b'\x74\x2d\x35\xcc\x66\x34\xc0\x53\x29\x25\xa3\xb8\xc1\x7b\x1e\x8b\x4e\x1d\x11\x23'
]
result = process_contract_data(contract_abi, addresses)
print(f"Found {len(result['functions'])} functions")
print(f"Formatted addresses: {result['addresses']}")from eth_utils import apply_to_return_value, to_list, flatten_return
from eth_utils.curried import to_int, from_wei
def create_pipeline(*transformers):
"""Create a functional processing pipeline."""
def pipeline(data):
result = data
for transformer in transformers:
result = transformer(result)
return result
return pipeline
# Create specialized transformers
@to_list
def extract_values(transactions):
"""Extract values from transaction objects."""
for tx in transactions:
yield tx.get('value', '0x0')
@apply_to_return_value(lambda values: [to_int(hexstr=v) for v in values])
def convert_hex_to_int(hex_values):
"""Convert hex values to integers."""
return hex_values
@apply_to_return_value(lambda values: [from_wei(v, 'ether') for v in values])
def convert_wei_to_ether(wei_values):
"""Convert wei values to ether."""
return wei_values
# Create processing pipeline
process_transaction_values = create_pipeline(
extract_values,
convert_hex_to_int,
convert_wei_to_ether
)
# Example usage
transactions = [
{"hash": "0x123...", "value": "0xde0b6b3a7640000"}, # 1 ETH
{"hash": "0x456...", "value": "0x1bc16d674ec80000"}, # 2 ETH
{"hash": "0x789...", "value": "0x2386f26fc10000"} # 0.01 ETH
]
ether_values = process_transaction_values(transactions)
print(f"Transaction values in ETH: {ether_values}")from eth_utils.curried import apply_formatters_to_dict, to_checksum_address, to_int
from functools import partial
# Create reusable formatters using curried functions
transaction_formatters = {
'to': to_checksum_address,
'from': to_checksum_address,
'value': partial(to_int, hexstr=None),
'gasPrice': partial(to_int, hexstr=None),
'gasLimit': partial(to_int, hexstr=None),
'nonce': partial(to_int, hexstr=None)
}
# Create specialized formatter
format_transaction = apply_formatters_to_dict(transaction_formatters)
def process_transaction_batch(raw_transactions):
"""Process batch of raw transactions functionally."""
return [dict(format_transaction(tx)) for tx in raw_transactions]
# Example usage
raw_transactions = [
{
'to': '0xd3cda913deb6f67967b99d67acdfa1712c293601',
'from': '0x742d35cc6634c0532925a3b8c17b1e8b4e1d1123',
'value': '0xde0b6b3a7640000',
'gasPrice': '0x4a817c800',
'gasLimit': '0x5208',
'nonce': '0x1'
}
]
formatted_transactions = process_transaction_batch(raw_transactions)
print(formatted_transactions[0]['to']) # Checksummed address
print(formatted_transactions[0]['value']) # Integer valuefrom eth_utils import apply_to_return_value, to_tuple, flatten_return
from eth_utils.curried import get_logger
from functools import wraps, reduce
import operator
def memoize(func):
"""Simple memoization decorator."""
cache = {}
@wraps(func)
def wrapper(*args, **kwargs):
key = str(args) + str(sorted(kwargs.items()))
if key not in cache:
cache[key] = func(*args, **kwargs)
return cache[key]
return wrapper
def compose(*functions):
"""Compose functions right to left."""
return reduce(lambda f, g: lambda x: f(g(x)), functions, lambda x: x)
def pipe(*functions):
"""Pipe functions left to right."""
return reduce(lambda f, g: lambda x: g(f(x)), functions, lambda x: x)
# Create functional processing chains
@memoize
@to_tuple
def expensive_calculation(data):
"""Expensive calculation with memoization."""
# Simulate expensive operation
return [x * 2 for x in data]
# Function composition example
from eth_utils.curried import to_hex, keccak
# Compose functions: hash then encode
hash_and_encode = compose(to_hex, keccak)
# Use composed function
text_data = "Hello, Ethereum!"
result = hash_and_encode(text=text_data)
print(f"Hash and encoded: {result}")
# Pipeline example
process_data = pipe(
lambda x: x.upper(),
lambda x: x.replace(" ", "_"),
lambda x: f"processed_{x}"
)
processed = process_data("hello world")
print(processed) # processed_HELLO_WORLDfrom eth_utils.curried import clamp, get_logger
from functools import partial
# Create specialized clamping functions
clamp_percentage = clamp(0, 100)
clamp_gas_price = clamp(1000000000, 100000000000) # 1-100 gwei
# Create specialized loggers
debug_logger = get_logger("debug")
error_logger = get_logger("error")
def validate_gas_price(gas_price):
"""Validate and clamp gas price."""
clamped = clamp_gas_price(gas_price)
if clamped != gas_price:
debug_logger.warning(f"Gas price clamped from {gas_price} to {clamped}")
return clamped
def validate_percentage(value):
"""Validate and clamp percentage value."""
return clamp_percentage(value)
# Examples
print(validate_gas_price(50000000000)) # Valid gas price
print(validate_gas_price(200000000000)) # Clamped to max
print(validate_percentage(150)) # Clamped to 100from eth_utils import apply_to_return_value
from functools import wraps
def safe_call(default_value=None):
"""Decorator for safe function calls with default return."""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except Exception as e:
print(f"Error in {func.__name__}: {e}")
return default_value
return wrapper
return decorator
def maybe(func):
"""Maybe monad-like decorator."""
@wraps(func)
def wrapper(value):
if value is None:
return None
try:
return func(value)
except Exception:
return None
return wrapper
# Usage with functional patterns
@safe_call(default_value=[])
@apply_to_return_value(list)
def safe_process_data(data):
"""Safely process data with default return."""
if not data:
raise ValueError("No data provided")
for item in data:
yield item * 2
@maybe
def safe_to_checksum(address):
"""Safely convert to checksum address."""
from eth_utils import to_checksum_address
return to_checksum_address(address)
# Examples
result1 = safe_process_data([1, 2, 3]) # [2, 4, 6]
result2 = safe_process_data(None) # [] (default)
addr1 = safe_to_checksum("0x742d35cc6634c0532925a3b8c17b1e8b4e1d1123") # Checksummed
addr2 = safe_to_checksum("invalid") # None
addr3 = safe_to_checksum(None) # Nonefrom eth_utils.curried import to_checksum_address, is_checksum_address, filter_abi_by_type
from functools import reduce
def functional_processing_example(addresses, contract_abi):
"""Example of functional processing with eth-utils."""
# Map: Convert all addresses to checksum format
checksum_addresses = list(map(to_checksum_address, addresses))
# Filter: Keep only valid checksum addresses
valid_addresses = list(filter(is_checksum_address, checksum_addresses))
# Reduce: Count total character length
total_length = reduce(lambda acc, addr: acc + len(addr), valid_addresses, 0)
# Extract functions using curried ABI filter
functions = filter_abi_by_type("function", contract_abi)
return {
"original_count": len(addresses),
"valid_count": len(valid_addresses),
"total_length": total_length,
"function_count": len(functions)
}
# Example usage
addresses = [
"0xd3cda913deb6f67967b99d67acdfa1712c293601",
"0x742d35cc6634c0532925a3b8c17b1e8b4e1d1123",
"invalid_address"
]
abi = [
{"type": "function", "name": "transfer"},
{"type": "function", "name": "approve"},
{"type": "event", "name": "Transfer"}
]
result = functional_processing_example(addresses, abi)
print(f"Processed {result['original_count']} addresses")
print(f"Found {result['valid_count']} valid addresses")
print(f"Found {result['function_count']} functions")