CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-eth-utils

Common utility functions for python code that interacts with Ethereum

Pending
Overview
Eval results
Files

functional-programming.mddocs/

Functional Programming

Functional programming utilities including curried functions, return value transformers, and functional composition tools for advanced data processing patterns.

Capabilities

Return Value Transformers

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."""

Curried Functions

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
)

Usage Examples

Return Value Transformation

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 list

Curried Function Composition

from 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']}")

Functional Data Pipeline

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}")

Functional Utilities with Currying

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 value

Advanced Functional Patterns

from 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_WORLD

Curried Utility Functions

from 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 100

Functional Error Handling

from 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)          # None

Functional Programming Patterns

Map, Filter, Reduce with eth-utils

from 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")

Install with Tessl CLI

npx tessl i tessl/pypi-eth-utils

docs

abi-processing.md

address-operations.md

crypto-functions.md

currency-units.md

data-conversions.md

data-formatting.md

functional-programming.md

hexadecimal-utilities.md

index.md

logging-debugging.md

network-information.md

type-checking.md

tile.json