与智能合约交互,处理日志
内容
与智能合约交互,处理日志¶
单击顶部栏上的🚀
-> Binder
在线运行此示例!
准备¶
import pprint
from conflux_web3 import Web3
w3 = Web3(Web3.HTTPProvider("https://test.confluxrpc.com"))
account = w3.account.create()
w3.cfx.default_account = account
faucet = w3.cfx.contract(name="Faucet")
faucet.functions.claimCfx().transact().executed()
w3.cfx.get_balance(account.address).to("CFX")
1000 CFX
与合约交互¶
在准备部分,我们已经展示了如何与合约交互。我们可以通过w3.cfx.contract
初始化合约实例,并使用name
参数创建一些常用的合约实例。但我们常常需要自行编写合约,本部分将展示如何编译、部署合约,并与已部署的合约进行交互。
编译和部署合约¶
合约是在区块链上运行的程序。如果我们想与自己编写的程序进行交互,我们需要首先将合约部署到区块链上。
这是一个简单的智能合约:
// SPDX-License-Identifier: MIT
// modified from https://solidity-by-example.org/first-app/
pragma solidity ^0.8.13;
contract Counter {
uint public count;
event Change(address indexed sender, uint new_value);
constructor(uint init_value) {
count = init_value;
}
// Function to get the current count
function get() public view returns (uint) {
return count;
}
// Function to increment count by 1
function inc() public {
count += 1;
emit Change(msg.sender, count);
}
}
编译部署此合约后,你可以:
从接口
get
读取count
的值通过调用
inc
添加变量count
另外,合约在inc
执行后会发出Change
事件,我们可以通过分析日志知道链上发生了什么。
接下来我们将编译上述合约。如果你在本地环境中运行代码,则可能需要运行pip install py-solc-x
来安装相关依赖。
# py-solc-x is already installed in the test environment
from solcx import install_solc, compile_source
source_code = r"""
// SPDX-License-Identifier: MIT
// modified from https://solidity-by-example.org/first-app/
pragma solidity ^0.8.13;
contract Counter {
uint public count;
event Change(address indexed sender, uint new_value);
constructor(uint init_value) {
count = init_value;
}
// Function to get the current count
function get() public view returns (uint) {
return count;
}
// Function to increment count by 1
function inc() public {
count += 1;
emit Change(msg.sender, count);
}
}
"""
metadata = compile_source(
source_code,
output_values=['abi', 'bin'],
solc_version=install_solc(version="0.8.13")
).popitem()[1]
# "abi" defines the interface, "bin" is the contract bytecode
assert "abi" in metadata and "bin" in metadata
# 根据metadata的abi与bytecode字段初始化contract对象
factory = w3.cfx.contract(abi=metadata["abi"], bytecode=metadata["bin"])
# 部署合约
tx_receipt = factory.constructor(init_value=0).transact().executed()
contract_address = tx_receipt["contractCreated"]
assert contract_address is not None
print(f"contract deployed: {contract_address}")
# 使用address参数初始化合约,这样我们可以调用该对象的链上接口
deployed_contract = w3.cfx.contract(address=contract_address, abi=metadata["abi"])
contract deployed: cfxtest:acc0mmk9t5sukfybrg8sy2sbw49tar6u8jf4r1u7tg
与已部署的合约交互¶
tx_hash = deployed_contract.functions.inc().transact()
inc_receipt = w3.cfx.wait_for_transaction_receipt(tx_hash)
# "call" 代表着模拟执行但不真正发送交易
# 下面两种方法都能调用"call"
current_counter = deployed_contract.functions.get().call()
current_counter_ = deployed_contract.caller().get()
assert current_counter == current_counter_ == 1
print("counter added to 1")
counter added to 1
处理日志¶
我们可以通过查看交易日志来了解交易执行中发生了什么。但原始的日志以十六进制编码,可读性较差。
# get_logs parameter definitions: https://developer.confluxnetwork.org/conflux-doc/docs/json_rpc#cfx_getlogs
fromEpoch = inc_receipt["epochNumber"]
# use get_logs to get raw logs
logs = w3.cfx.get_logs(fromEpoch=fromEpoch, address=contract_address)
print("raw log: ")
pprint.pprint(dict(logs[0]))
raw log:
{'address': 'cfxtest:acc0mmk9t5sukfybrg8sy2sbw49tar6u8jf4r1u7tg',
'blockHash': HexBytes('0x5d7f3bc654370883359835aa22be509a7a36a8215d011e21bf0c2ca4592c6665'),
'data': HexBytes('0x0000000000000000000000000000000000000000000000000000000000000001'),
'epochNumber': 99721993,
'logIndex': 0,
'topics': [HexBytes('0x05b5d46649ab2015d3a08705cbaa391e094d9594c393ce89d3afffe960744da1'),
HexBytes('0x0000000000000000000000001f4bffef33cdf6e6b854f6aaf2c4498f18027bb1')],
'transactionHash': HexBytes('0x1d703cd4602bb02603b8154ca43ec4f4ff8107e757afd11b4b8d118db03cc222'),
'transactionIndex': 0,
'transactionLogIndex': 0}
在以下部分中,我们将介绍几种处理和过滤日志的方法。
我们可以使用contract.event
来处理日志。处理后,日志的args
字段包含了我们需要的信息。
processed_logs = deployed_contract.events.Change.process_receipt(inc_receipt)
processed_log = processed_logs[0]
assert processed_log["args"]["sender"] == w3.cfx.default_account
assert processed_log["args"]["new_value"] == 1
# 从交易receipt拿到的日志中, 字段 "logIndex" 为 None
pprint.pprint(dict(processed_log))
{'address': 'cfxtest:acc0mmk9t5sukfybrg8sy2sbw49tar6u8jf4r1u7tg',
'args': AttributeDict({'sender': 'CFXTEST:TYPE.USER:AATY199TGTG9R3Z2MX5MZ60EKGHVUAX50EC9SFY3HB', 'new_value': 1}),
'blockHash': HexBytes('0x5d7f3bc654370883359835aa22be509a7a36a8215d011e21bf0c2ca4592c6665'),
'epochNumber': 99721993,
'event': 'Change',
'logIndex': None,
'transactionHash': HexBytes('0x1d703cd4602bb02603b8154ca43ec4f4ff8107e757afd11b4b8d118db03cc222'),
'transactionIndex': 0,
'transactionLogIndex': 0}
此外,我们可以使用contract.events
对合约topics进行编码以用于过滤日志
# 编码topics以用于get_logs
filter_topics = deployed_contract.events.Change.get_filter_topics(
sender=w3.cfx.default_account
)
new_logs = w3.cfx.get_logs(fromEpoch=fromEpoch, topics=filter_topics)
print("log filtered by topics:")
pprint.pprint(dict(new_logs[0]))
log filtered by topics:
{'address': 'cfxtest:acc0mmk9t5sukfybrg8sy2sbw49tar6u8jf4r1u7tg',
'blockHash': HexBytes('0x5d7f3bc654370883359835aa22be509a7a36a8215d011e21bf0c2ca4592c6665'),
'data': HexBytes('0x0000000000000000000000000000000000000000000000000000000000000001'),
'epochNumber': 99721993,
'logIndex': 0,
'topics': [HexBytes('0x05b5d46649ab2015d3a08705cbaa391e094d9594c393ce89d3afffe960744da1'),
HexBytes('0x0000000000000000000000001f4bffef33cdf6e6b854f6aaf2c4498f18027bb1')],
'transactionHash': HexBytes('0x1d703cd4602bb02603b8154ca43ec4f4ff8107e757afd11b4b8d118db03cc222'),
'transactionIndex': 0,
'transactionLogIndex': 0}
或者我们可以使用contract.events
中的get_logs
接口
# event get_logs 接口会直接返回已处理的日志
new_processed_logs = deployed_contract.events.Change.get_logs(
argument_filters={
"sender": w3.cfx.default_account
},
fromEpoch=fromEpoch
)
print("processed log from contract event get_logs")
pprint.pprint(dict(new_processed_logs[0]))
processed log from contract event get_logs
{'address': 'cfxtest:acc0mmk9t5sukfybrg8sy2sbw49tar6u8jf4r1u7tg',
'args': AttributeDict({'sender': 'CFXTEST:TYPE.USER:AATY199TGTG9R3Z2MX5MZ60EKGHVUAX50EC9SFY3HB', 'new_value': 1}),
'blockHash': HexBytes('0x5d7f3bc654370883359835aa22be509a7a36a8215d011e21bf0c2ca4592c6665'),
'epochNumber': 99721993,
'event': 'Change',
'logIndex': 0,
'transactionHash': HexBytes('0x1d703cd4602bb02603b8154ca43ec4f4ff8107e757afd11b4b8d118db03cc222'),
'transactionIndex': 0,
'transactionLogIndex': 0}
conflux_web3
的 api 与web3.py
的 api 一致。您还可以尝试web3.py
文档中的示例。