How to decode a log['data'] hexBytes with contract built on zkEVM testnet using web3.py

Hi Everyone,
I need help pulling zkEVM log data from the testnet.

I have this simple solidity event in my smart contract (Contract Address 0x98544219dd60eCc071302dAfBfce22F74334f244 | zkEVM) that I deployed to the zkEMV testnet:

event LogData(
    uint256 indexed amount,
    string reason,
    address donatorAddress,
    uint256 timestamp
);

With the code below, I was able to iterate over the blocks and retrieve the event logs. But I can’t read the second event argument (string reason) as a string. It’s showing as hexBytes data: b’\x00\x00\x00.…

Does anyone know how to decode the event’s reason which is inside the log[‘data’], on liine 49 of the code.

I tried using Web3.to_text(log[‘data’]) and a few other proposed solutions but I wasn’t able to decode it. The decoded data from both logs should be the following strings: “testnow” and “for adam”

from dash import Dash, html, callback, clientside_callback, Output, Input, State, no_update
import dash_bootstrap_components as dbc
from web3 import Web3
import json


app = Dash(__name__, external_stylesheets=[dbc.themes.CYBORG], external_scripts=[{'src': '../static/signerinfo.js', 'type':'module'}])
app.layout = dbc.Container([
    html.H1('Donation Decentralized Application'),

    dbc.Button("See donations over 45 wei", id="get-data", color='info', n_clicks=0),
    html.Div(id='placeholder', children=""),
    html.Div(id='placeholder2', children=""),

])


@callback(
    Output("placeholder2", "children"),
    Input("get-data", "n_clicks"),
    prevent_initial_call=True,
)
def access_data(alert_text):
    public_zkevm_rpc = 'https://rpc.public.zkevm-test.net'
    w3 = Web3(Web3.HTTPProvider(public_zkevm_rpc))
    print(w3.is_connected())
    abi = json.loads(
        '[{"inputs": [],"stateMutability": "payable","type": "constructor"},{"anonymous": false,"inputs": [{"indexed": true,"internalType": "uint256","name": "amount","type": "uint256"},{"indexed": false,"internalType": "string","name": "reason","type": "string"},{"indexed": false,"internalType": "address","name": "donatorAddress","type": "address"},{"indexed": false,"internalType": "uint256","name": "timestamp","type": "uint256"}],"name": "LogData","type": "event"},{"inputs": [{"internalType": "string","name": "donationReason","type": "string"}],"name": "offerDonation","outputs": [],"stateMutability": "payable","type": "function"},{"inputs": [],"name": "onlineEducator","outputs": [{"internalType": "address payable","name": "","type": "address"}],"stateMutability": "view","type": "function"}]')
    address = w3.to_checksum_address(
        '0x98544219dd60eCc071302dAfBfce22F74334f244')
    contract = w3.eth.contract(address=address, abi=abi)


    event_signature = "LogData(uint256,string,address,uint256)"
    over_45_wei = []
    # Iterate over the blocks from earliest to latest and retrieve the event logs
    for block_number in range(1444600, 1444700):
        logs = w3.eth.get_logs({
            "address": '0x98544219dd60eCc071302dAfBfce22F74334f244',
            "fromBlock": block_number,
            "toBlock": block_number,
            "topics": [w3.keccak(text=event_signature).hex()]
        })

        for log in logs:
            print(log)
            event_data = {
                "amount": int.from_bytes(log['topics'][1], byteorder='big'),
                "reason": log["data"],
            }
            print(event_data['amount'])
            print(event_data['reason'])

            if event_data['amount'] > 45:
                over_45_wei.append(event_data['amount'])
    print(over_45_wei)

    return no_update


if __name__ == '__main__':
    app.run(debug=True)


The Python code above is fully functional (it’s a dApp, using Python for the front end). For it to run on your computer, all you need to do is:

  1. save the code as a .py file
  2. pip install dash
  3. pip install dash-bootstrap-components
  4. pip install web3
2 Likes

Very good info, thanks mate!

1 Like

hey all,
my question was answered on stackexchange by Usman Farooq.


The Data field consists of five different attributes and by calling through the below web3 API it returns the hex bytes of those five attributes combined.

w3.eth.get_logs({
            "address": '0x98544219dd60eCc071302dAfBfce22F74334f244',
            "fromBlock": block_number,
            "toBlock": block_number,
            "topics": [w3.keccak(text=event_signature).hex()]
        })

So basically when you do this "reason": log["data"], you are assigning the hex bytes of all five attributes.

all you have to do is to take the hex bytes of your required attribute which is the last 64 characters in this case and then convert it to text. below is the solution.

event_data = {
                "amount": int.from_bytes(log['topics'][1], byteorder='big'),
                "reason": w3.to_text(log["data"][-64:]),
            }
2 Likes