如何通过web3.py获取Solidity合约调用函数时的事件数据?
使用web3.py捕获Solidity合约事件数据的完整方案
嘿,我来帮你搞定这个问题!首先先修正你的Solidity合约(补全了pragma后面的分号,不然编译会报错):
pragma solidity ^0.4.17; contract Test { event greeting(string name); function say() pure public { greeting('jack'); } }
接下来咱们完善你的web3.py代码,实现捕获greeting事件里的'jack'数据。我会把完整代码写出来,再一步步解释关键部分:
from web3 import Web3 import time # 初始化web3连接,这里以本地Ganache节点为例,根据你的实际节点修改 w3 = Web3(Web3.HTTPProvider('http://127.0.0.1:7545')) # 合约地址和ABI(确保ABI和你编译合约得到的完全一致) contractAddress = '0x345ca3e014aaf5dca488057592ee47305d9b3e10' abiJson = { "abi": [ { "anonymous": False, "inputs": [{"indexed": False, "name": "name", "type": "string"}], "name": "greeting", "type": "event" }, { "constant": True, "inputs": [], "name": "say", "outputs": [], "payable": False, "stateMutability": "pure", "type": "function" } ] } # 实例化合约对象 contract = w3.eth.contract(address=contractAddress, abi=abiJson['abi']) accounts = w3.eth.accounts # 处理事件的函数:这里提取事件中的name参数 def handle_event(event): # 直接从event的args里取name,就是我们要的'jack' captured_name = event['args']['name'] print(f"成功捕获greeting事件!传递的name值是:{captured_name}") # 如果想查看完整事件结构,可以取消下面的注释 # print("完整事件详情:", event) # 事件监听循环:持续轮询获取新事件 def log_loop(event_filter, poll_interval): while True: # 获取所有新增的事件条目 for event in event_filter.get_new_entries(): handle_event(event) # 每隔指定秒数轮询一次,避免频繁请求节点 time.sleep(poll_interval) # 创建greeting事件的过滤器,从最新区块开始监听 event_filter = contract.events.greeting.create_filter(fromBlock='latest') # 启动监听循环,这里设置每隔2秒轮询一次 log_loop(event_filter, 2)
关键部分解释:
- 事件过滤器创建:
contract.events.greeting.create_filter(fromBlock='latest')指定监听greeting事件,并且只关注最新区块之后的事件。你也可以指定具体区块号,比如fromBlock=123456。 - 事件数据提取:在
handle_event函数里,event['args']['name']直接对应Solidity事件中定义的name参数,也就是触发事件时传入的'jack'。 - 触发事件(可选):如果需要主动调用
say()函数来触发事件,可以添加这段代码(放在启动循环之前):# 调用say函数触发事件,用第一个账户发送交易 tx_hash = contract.functions.say().transact({'from': accounts[0]}) # 等待交易上链,确保事件被记录 w3.eth.wait_for_transaction_receipt(tx_hash) print("交易已上链,事件已触发!")
注意事项:
- 确保你的web3节点连接正常,比如Ganache、Infura或者本地Geth节点。
- ABI必须和你编译Solidity合约后得到的ABI完全一致,不然无法正确解析事件。
- 如果是在公网测试链或主网,要确保发送交易的账户有足够的Gas费(虽然
say()是pure函数,但发送交易仍需要Gas来记录事件日志)。
内容的提问来源于stack exchange,提问作者hang
相关产品推荐
相关产品推荐

