You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求助:修复Web3.py中通过ABI解码以太坊交易输入的函数

修复以太坊交易输入解码函数

问题说明

我已确认获取的ABI与Etherscan上的一致,也正确拿到了transaction[input].hex()返回的待解码输入十六进制数据。现在需要修复自己编写的Python函数,实现本地用Web3.py库解码以太坊交易输入的功能,但当前函数匹配不到对应函数签名,无法完成解码。以下是待修复的函数代码、当前输出及ABI,请求帮助完成解码以提取交易数据。

待修复的函数代码

def decode(input_data, abi):
    print("Decoding input data: ", input_data)
    print("Using ABI: ", abi)
    input_bytes = bytes.fromhex(input_data[2:])  # 转换为bytes,跳过'0x'前缀
    for item in abi:
        if item['type'] == 'function':
            signature = item['name'] + '('
            for i in range(len(item['inputs'])):
                signature += item['inputs'][i]['type']
                if i != len(item['inputs']) - 1:
                    signature += ','
            signature += ')'
            
            signature_hash = Web3.keccak(text=signature).hex()

            # 移除'0x'前缀后转换为bytes
            signature_bytes = bytes.fromhex(signature_hash[2:])

            # 无法通过以下判断
            # print("Signature Bytes: ", signature_bytes)
            # print("Input Bytes: ", input_bytes)

            if input_bytes.startswith(signature_bytes):
                print(f"Function: {item['name']}")
                print(f"Inputs: {item['inputs']}")
                print(f"Outputs: {item['outputs']}")
                print(f"Method ID: {signature_hash[:10]}")
                print(f"Input Data: {input_data}")
                break

当前输出

Decoding input data:  0x845d76f7000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000200000000000000000000000020243aff03840e7bf14bd53b7c18015018be4b3c000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
Using ABI:  [{'inputs': [], 'stateMutability': 'payable', 'type': 'constructor'}, {'anonymous': False, 'inputs': [{'indexed': True, 'internalType': 'address', 'name': 'owner', 'type': 'address'}, {'indexed': True, 'internalType': 'address', 'name': 'spender', 'type': 'address'}, {'indexed': False, 'internalType': 'uint256', 'name': 'value', 'type': 'uint256'}], 'name': 'Approval', 'type': 'event'}, {'anonymous': False, 'inputs': [{'indexed': False, 'internalType': 'uint256', 'name': '_maxTxAmount', 'type': 'uint256'}], 'name': 'MaxTxAmountUpdated', 'type': 'event'}, {'anonymous': False, 'inputs': [{'indexed': True, 'internalType': 'address', 'name': 'previousOwner', 'type': 'address'}, {'indexed': True, 'internalType': 'address', 'name': 'newOwner', 'type': 'address'}], 'name': 'OwnershipTransferred', 'type': 'event'}, {'anonymous': False, 'inputs': [{'indexed': True, 'internalType': 'address', 'name': 'from', 'type': 'address'}, {'indexed': True, 'internalType': 'address', 'name': 'to', 'type': 'address'}, {'indexed': False, 'internalType': 'uint256', 'name': 'value', 'type': 'uint256'}], 'name': 'Transfer', 'type': 'event'}, {'inputs': [], 'name': '_maxTaxSwap', 'outputs': [{'internalType': 'uint256', 'name': '', 'type': 'uint256'}], 'stateMutability': 'view', 'type': 'function'}, {'inputs': [], 'name': '_maxTxAmount', 'outputs': [{'internalType': 'uint256', 'name': '', 'type': 'uint256'}], 'stateMutability': 'view', 'type': 'function'}, {'inputs': [], 'name': '_maxWalletSize', 'outputs': [{'internalType': 'uint256', 'name': '', 'type': 'uint256'}], 'stateMutability': 'view', 'type': 'function'}, {'inputs': [], 'name': '_taxSwapThreshold', 'outputs': [{'internalType': 'uint256', 'name': '', 'type': 'uint256'}], 'stateMutability': 'view', 'type': 'function'}, {'inputs': [{'internalType': 'address[]', 'name': 'bots_', 'type': 'address[]'}], 'name': 'addBots', 'outputs': [], 'stateMutability': 'nonpayable', 'type': 'function'}, {'inputs': [{'internalType': 'address', 'name': 'owner', 'type': 'address'}, {'internalType': 'address', 'name': 'spender', 'type': 'address'}], 'name': 'allowance', 'outputs': [{'internalType': 'uint256', 'name': '', 'type': 'uint256'}], 'stateMutability': 'view', 'type': 'function'}, {'inputs': [{'internalType': 'address', 'name': 'spender', 'type': 'address'}, {'internalType': 'uint256', 'name': 'amount', 'type': 'uint256'}], 'name': 'approve', 'outputs': [{'internalType': 'bool', 'name': '', 'type': 'bool'}], 'stateMutability': 'nonpayable', 'type': 'function'}, {'inputs': [{'internalType': 'address', 'name': 'account', 'type': 'address'}], 'name': 'balanceOf', 'outputs': [{'internalType': 'uint256', 'name': '', 'type': 'uint256'}], 'stateMutability': 'view', 'type': 'function'}, {'inputs': [], 'name': 'decimals', 'outputs': [{'internalType': 'uint8', 'name': '', 'type': 'uint8'}], 'stateMutability': 'pure', 'type': 'function'}, {'inputs': [{'internalType': 'address[]', 'name': 'notbot', 'type': 'address[]'}], 'name': 'delBots', 'outputs': [], 'stateMutability': 'nonpayable', 'type': 'function'}, {'inputs': [{'internalType': 'address', 'name': 'a', 'type': 'address'}], 'name': 'isBot', 'outputs': [{'internalType': 'bool', 'name': '', 'type': 'bool'}], 'stateMutability': 'view', 'type': 'function'}, {'inputs': [], 'name': 'manualSwap', 'outputs': [], 'stateMutability': 'nonpayable', 'type': 'function'}, {'inputs': [], 'name': 'manualsend', 'outputs': [], 'stateMutability': 'nonpayable', 'type': 'function'}, {'inputs': [], 'name': 'name', 'outputs': [{'internalType': 'string', 'name': '', 'type': 'string'}], 'stateMutability': 'pure', 'type': 'function'}, {'inputs': [], 'name': 'openTrading', 'outputs': [], 'stateMutability': 'nonpayable', 'type': 'function'}, {'inputs': [], 'name': 'owner', 'outputs': [{'internalType': 'address', 'name': '', 'type': 'address'}], 'stateMutability': 'view', 'type': 'function'}, {'inputs': [{'internalType': 'uint256', 'name': '_newFee', 'type': 'uint256'}], 'name': 'reduceFee', 'outputs': [], 'stateMutability': 'nonpayable', 'type': 'function'}, {'inputs': [], 'name': 'removeLimits', 'outputs': [], 'stateMutability': 'nonpayable', 'type': 'function'}, {'inputs': [], 'name': 'renounceOwnership', 'outputs': [], 'stateMutability': 'nonpayable', 'type': 'function'}, {'inputs': [], 'name': 'symbol', 'outputs': [{'internalType': 'string', 'name': '', 'type': 'string'}], 'stateMutability': 'pure', 'type': 'function'}, {'inputs': [], 'name': 'totalSupply', 'outputs': [{'internalType': 'uint256', 'name': '', 'type': 'uint256'}], 'stateMutability': 'pure', 'type': 'function'}, {'inputs': [{'internalType': 'address', 'name': 'recipient', 'type': 'address'}, {'internalType': 'uint256', 'name': 'amount', 'type': 'uint256'}], 'name': 'transfer', 'outputs': [{'internalType': 'bool', 'name': '', 'type': 'bool'}], 'stateMutability': 'nonpayable', 'type': 'function'}, {'inputs': [{'internalType': 'address', 'name': 'sender', 'type': 'address'}, {'internalType': 'address', 'name': 'recipient', 'type': 'address'}, {'internalType': 'uint256', 'name': 'amount', 'type': 'uint256'}], 'name': 'transferFrom', 'outputs': [{'internalType': 'bool', 'name': '', 'type': 'bool'}], 'stateMutability': 'nonpayable', 'type': 'function'}, {'stateMutability': 'payable', 'type': 'receive'}]

修复方案

核心问题定位

你的输入数据方法ID是0x845d76f7,但当前提供的ABI中没有对应签名的函数,这是匹配失败的直接原因。同时原函数仅实现了签名匹配逻辑,未完成参数解码,手动拼接签名也存在出错风险。

方案1:使用Web3.py内置解码函数(推荐)

Web3.py提供了decode_function_input方法,可自动匹配函数签名并解码参数,无需手动实现复杂逻辑:

from web3 import Web3

def decode(input_data, abi):
    print("Decoding input data: ", input_data)
    print("Using ABI: ", abi)
    
    # 创建合约实例
    contract = Web3().eth.contract(abi=abi)
    try:
        # 自动匹配函数并解码输入参数
        function, decoded_input = contract.decode_function_input(input_data)
        print(f"函数名称: {function.fn_name}")
        print(f"解码参数: {decoded_input}")
        print(f"方法ID: {input_data[:10]}")
    except ValueError as e:
        print(f"解码失败: {e}")
        print("提示:请确认ABI是否包含该交易调用的函数签名")

方案2:修复原函数的签名匹配与参数解码

若坚持手动实现,需确保签名拼接正确,并添加参数解码逻辑,同时需确认ABI包含目标函数:

from web3 import Web3
from eth_abi import decode

def decode(input_data, abi):
    print("Decoding input data: ", input_data)
    print("Using ABI: ", abi)
    
    input_bytes = bytes.fromhex(input_data[2:])
    # 提取前4字节作为方法ID
    method_id = input_bytes[:4]
    
    for item in abi:
        if item['type'] == 'function':
            # 生成标准函数签名字符串
            input_types = [inp['type'] for inp in item['inputs']]
            signature = f"{item['name']}({','.join(input_types)})"
            
            # 计算签名哈希并取前4字节匹配
            signature_hash = Web3.keccak(text=signature)
            if signature_hash[:4] == method_id:
                print(f"函数名称: {item['name']}")
                print(f"输入参数定义: {item['inputs']}")
                print(f"方法ID: {Web3.to_hex(method_id)}")
                
                # 解码参数(跳过前4字节的方法ID)
                param_bytes = input_bytes[4:]
                try:
                    decoded_params = decode(input_types, param_bytes)
                    # 将参数与名称对应输出
                    param_dict = {inp['name']: val for inp, val in zip(item['inputs'], decoded_params)}
                    print(f"解码后的参数: {param_dict}")
                except Exception as e:
                    print(f"参数解码失败: {e}")
                break
    else:
        print("未找到匹配的函数签名,请确认ABI是否完整")

关键提示

你当前的输入数据对应的函数签名不在提供的ABI中,需重新从Etherscan获取完整ABI(可通过方法ID0x845d76f7反向查询,该ID对应的函数大概率是批量转账类函数,比如transferBatch(address[],uint256[]))。

内容的提问来源于stack exchange,提问作者xampL

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 05:35:54