DEV Community

Byaigo
Byaigo

Posted on

Crafting a Custom Ethereum Transaction Decoder: From Raw Hex to Human-Readable Data with Python

Crafting a Custom Ethereum Transaction Decoder: From Raw Hex to Human-Readable Data with Python

Ever looked at a raw Ethereum transaction and wondered what all that hex gibberish actually means? Let's build a decoder that turns opaque calldata into something you can actually read.

Why Decode Transactions?

Every time someone interacts with a smart contract, they send calldata — hex-encoded function signatures and parameters. Understanding this data is crucial for:

  • Auditing contract interactions
  • Building block explorers
  • Monitoring whale wallets
  • Debugging failed transactions

The Building Blocks

We'll use just two Python libraries: web3.py for chain interaction and eth-abi for decoding. Here's the core approach:

from web3 import Web3
from eth_abi import decode
import requests

# Connect to Ethereum
w3 = Web3(Web3.HTTPProvider('https://eth.llamarpc.com'))

# Get a transaction
tx_hash = '0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060'
tx = w3.eth.get_transaction(tx_hash)

# Extract function selector (first 4 bytes of input)
input_data = tx['input']
function_selector = '0x' + input_data.hex()[:8]
print(f"Function Selector: {function_selector}")
Enter fullscreen mode Exit fullscreen mode

The Magic: ABI-Based Decoding

Function selectors alone aren't enough — we need the contract's ABI. Here's a smart trick: query the Etherscan API for verified contract ABIs, cache them, then decode on the fly.

import json
from eth_abi import decode

def get_contract_abi(contract_address):
    """Fetch verified ABI from Etherscan with caching."""
    url = f"https://api.etherscan.io/api"
    params = {
        'module': 'contract',
        'action': 'getabi',
        'address': contract_address,
        'apikey': 'YOUR_API_KEY'
    }
    response = requests.get(url, params=params)
    if response.status_code == 200:
        data = response.json()
        if data['status'] == '1':
            return json.loads(data['result'])
    return None

def decode_transaction_input(tx_input, abi):
    """Decode calldata using contract ABI."""
    selector = tx_input[:4]

    for item in abi:
        if item.get('type') != 'function':
            continue

        # Compute function selector
        signature = f"{item['name']}({','.join(i['type'] for i in item['inputs'])})"
        computed_selector = w3.keccak(text=signature)[:4]

        if selector == computed_selector:
            # Decode parameters
            param_types = [i['type'] for i in item['inputs']]
            param_names = [i['name'] for i in item['inputs']]
            decoded = decode(param_types, tx_input[4:])

            return {
                'function': item['name'],
                'params': dict(zip(param_names, decoded))
            }

    return {'function': 'unknown', 'params': {}}
Enter fullscreen mode Exit fullscreen mode

Putting It All Together

Here's a complete script that decodes any transaction to a verified contract:

def decode_any_transaction(tx_hash):
    tx = w3.eth.get_transaction(tx_hash)

    if tx['input'] == b'' or tx['input'] == '0x':
        return {
            'type': 'ETH transfer',
            'value': w3.from_wei(tx['value'], 'ether'),
            'to': tx['to']
        }

    abi = get_contract_abi(tx['to'])
    if not abi:
        return {'type': 'contract call (unverified)', 'raw_input': tx['input'].hex()}

    decoded = decode_transaction_input(tx['input'], abi)
    return {
        'type': 'contract interaction',
        'contract': tx['to'],
        **decoded
    }

# Example usage
result = decode_any_transaction('0x1234...')
print(json.dumps(result, indent=2, default=str))
Enter fullscreen mode Exit fullscreen mode

Going Further

This approach scales well. You can extend it with:

  • Event log decoding — parse emitted events the same way
  • Proxy contract resolution — follow EIP-1967 to find implementation ABIs
  • Multi-chain support — works on any EVM chain with a compatible explorer API

The Code Is Open Source

All the code from this tutorial is available on GitHub: github.com/Byaigo


Support open-source development:

ETH: 0x18da907cb9d981bc798acb87ac27b03a2dc3cbb7

Happy decoding! 🚀

Top comments (0)