我有这个交易,它从 2 个不同的合约发出 2 个不同的事件。假设我有两个合约的 ABI 文件,如何从logs
字段中解析事件?
例如,下面是发出 2 个事件的此类事务 https://rinkeby.etherscan.io/tx/0xc6525195135a868897bd4c74ea0f6285b98492103be4df6fc5ea43f83b96b8eb#eventlog
这是getTransactionReceiptRPC调用的响应,如何从logs
字段中解析事件名称及其参数?
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"blockHash": "0x3da27197fe084a82d76273b40c32e231d2662b229337795a43fde9f8d73b4d2c",
"blockNumber": "0x39a62b",
"contractAddress": null,
"cumulativeGasUsed": "0x48ad9",
"from": "0x20b53b91da0a2d9afdd442b2bb433a40ab7f9613",
"gasUsed": "0x9e36",
"logs": [
{
"address": "0x5328276603d169165d0f71ca67ccc89c45027df3",
"blockHash": "0x3da27197fe084a82d76273b40c32e231d2662b229337795a43fde9f8d73b4d2c",
"blockNumber": "0x39a62b",
"data": "0x000000000000000000000000f63843b0b7fc5097bc1f92658379617513cce546000000000000000000000000ff0e3299e55efd859176d582fc805481e83449150000000000000000000000000000000000000000000000000000000000002245",
"logIndex": "0x1",
"removed": false,
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
],
"transactionHash": "0x5442a15c593b3fcf3069ad7f6a41d8805a66a49ed407d872e46ea95b1c9f6054",
"transactionIndex": "0x3"
},
{
"address": "0x7387e0b25165e9a621f624e47b3362a937892c7b",
"blockHash": "0x3da27197fe084a82d76273b40c32e231d2662b229337795a43fde9f8d73b4d2c",
"blockNumber": "0x39a62b",
"data": "0x0000000000000000000000000000000000000000000000000000000000002245000000000000000000000000ff0e3299e55efd859176d582fc805481e8344915000000000000000000000000f63843b0b7fc5097bc1f92658379617513cce546000000000000000000000000000000000000000000000000016345785d8a0000000000000000000000000000000000000000000000000000000ffcb9e57d4000",
"logIndex": "0x2",
"removed": false,
"topics": [
"0x9accbcf984c4cd67a675ee4d38143974e1fa62aa95da283bd4ca645e408ec283"
],
"transactionHash": "0x5442a15c593b3fcf3069ad7f6a41d8805a66a49ed407d872e46ea95b1c9f6054",
"transactionIndex": "0x3"
}
],
"logsBloom": "0x20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000048000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000010000000000000000000000000000000000000000000000004000000000000000000080000008000000000000000000000000000000000000000400000000000000000000000000002000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"status": "0x1",
"to": "0x7387e0b25165e9a621f624e47b3362a937892c7b",
"transactionHash": "0x5442a15c593b3fcf3069ad7f6a41d8805a66a49ed407d872e46ea95b1c9f6054",
"transactionIndex": "0x3"
}
}
我找不到任何现有的可以轻松解码事件日志,所以我做了以下操作:
from web3 import Web3
from eth_utils import decode_hex
from eth_abi import decode
def get_event_by_signature(abi, signature):
for entry in abi:
if entry['type'] == 'event':
input_params = [f"({','.join([component['type'] for component in param['components']])})" if param['type'] == "tuple" else param['type'] for param in entry['inputs']]
text_to_hash = f"{entry['name']}({','.join(input_params)})"
if Web3.keccak(text=text_to_hash).hex() == signature:
return entry
return None
def decode_data(event, data):
unindexed_params = [param for param in event['inputs'] if not param['indexed']]
definition = [f"({','.join([component['type'] for component in param['components']])})" if param['type'] == "tuple" else param['type'] for param in unindexed_params]
decoded_data = decode(definition, decode_hex(data))
data_dict = {}
for i, param in enumerate(unindexed_params):
if param["type"] == "tuple":
data_dict[param['name']] = {component['name']: decoded_data[i][j] for j, component in enumerate(param["components"])}
else:
data_dict[param['name']] = decoded_data[i]
return data_dict
def decode_topics(event, topics):
decoded_topics = {}
topic_count = 1
for param in event['inputs']:
if param['indexed']:
if param["type"] == "tuple":
decoded_topic = topics[topic_count]
else:
decoded_topic = decode([param['type']], decode_hex(topics[topic_count]))[0]
decoded_topics[param['name']] = decoded_topic
topic_count += 1
return decoded_topics
def decode_log(abi, result):
signature = result['topics'][0]
event = get_event_by_signature(abi, signature)
data = result['data']
topics = result['topics']
decoded_data = decode_data(event, data)
decoded_topics = decode_topics(event, topics)
return {**decoded_topics, **decoded_data}
decode_log
函数采用协定 ABI 和一个结果对象,并返回所有事件参数及其各自值的字典。其他函数只是 decode_log
使用的辅助函数。
如果其中一个参数是结构,则该值将是结构键和值的另一个字典。
如果其中一个参数是数组,则该值将是数组中值的列表。
如果其中一个参数是索引结构或数组,由于它大于 32 位,因此不适合主题,因此值的哈希存储在关联的主题中,并且不存储数据。在这种情况下,哈希将作为值返回
例:
结果对象:
{
"removed": false,
"logIndex": "0x37",
"transactionIndex": "0xd",
"transactionHash": "0xaa600a42c767cd6fa60a331ceb68c7d38e79d14ba45ef3f590916bdf65657aeb",
"blockHash": "0x0479e3cad2a0e94d7cc3af096c40f440252bf658df4d4f2cfa45c2435f8b8831",
"blockNumber": "0x881b1c",
"address": "0x2ec255b90855c20c1c51d3fa6259bf9605412180",
"data": "0x00000000000000000000000000000000000000000000000000000000000000020000000000000000000000001bc858e1b8c174fdcbc5df7fbbf3a361fefefa7100000000000000000000000000000000000000000000000000000000000003de000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000644ee55800000000000000000000000000000000000000000000000000000000644ee6c00000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000016546869732069732074657874206578616d706c652027000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000004e000000000000000000000000000000000000000000000000000000000000004e000000000000000000000000000000000000000000000000000000000000004e",
"topics": [
"0x96969eb2a3d3e165bc0514f80c20d2535fdf2ae2d35de97fe807c028e95b616a",
"0x0000000000000000000000000000000000000000000000000000000000000001",
"0x0000000000000000000000000000000000000000000000000000000000000005",
"0x5d60038e81b1a59543b60113b96879ca6954fee6ab55d62a75952c8c0381b4d5"
]
}
解码日志:
{
"param1": 1,
"param6": 5,
"param7": "0x5d60038e81b1a59543b60113b96879ca6954fee6ab55d62a75952c8c0381b4d5",
"param2": 2,
"param3": {
"ownerAddress": "0x1bc858e1b8c174fdcbc5df7fbbf3a361fefefa71",
"balance": 990,
"reward": 0,
"startDateTime": 1682892120,
"endDateTime": 1682892480
},
"param4": 3,
"text": "This is text example '",
"param8": [
78,
78,
78
]
}