如何为 web3py 中的合约创建的合约创建事件过滤器
How to create event filter for contract created by contract in web3py
我正在尝试为由我想要的合约创建的合约引发的事件创建一个过滤器。但是,我无法弄清楚。这是我现在拥有的。
MergeModule.sol
pragma solidity ^0.4.23;
contract MergeModule {
event MergeEvent(uint prID);
function MergeModule(){
}
function merge(uint prID) public {
emit MergeEvent(prID);
}
}
Handler.sol
pragma solidity ^0.4.23;
import "./merge_module.sol";
contract Handler {
// a getter for this will automatically be made by the compiler
address public mergeModule;
function Handler() public {
mergeModule = new MergeModule();
}
function execute() public {
mergeModule.merge();
}
}
test_handler.py
from web3 import Web3, EthereumTesterProvider
import unittest
import os
from eth_tester.exceptions import TransactionFailed
import tests.utils.utils as utils
from web3.utils.filters import Filter
class TestHandler(unittest.TestCase):
PROJECT_ROOT = os.path.dirname(os.path.dirname(__file__))
CONTRACT_ROOT = os.path.join(PROJECT_ROOT, "contracts")
TEST_CONTRACT_ROOT = os.path.join(CONTRACT_ROOT, "test_contracts")
def setUp(self):
handler_contract_path = os.path.join(self.CONTRACT_ROOT, "handler.sol")
# web3.py instance
self.w3 = Web3(EthereumTesterProvider())
self.contract, self.contract_address, self.contract_instance = utils.create_contract(self.CONTRACT_ROOT,
handler_contract_path,
"Handler", self.w3)
def test_event_emitted(self):
# this prints something different from self.contract_address
print(self.contract_instance.mergeModule())
# this creates a reference to the Handler contract. I know this because when I inspect it with the debugger I see `execute` as one of the functions
merge_contract = self.w3.eth.contract(self.contract_instance.mergeModule())
merge_event_filter: Filter = merge_contract.events.MergeEvent.createFilter(fromBlock=0)
# do stuff here with the filter
utils.create_contract
或多或少做了 quickstart for web3py 中显示的内容,并进行了一些修改以处理编译多个文件。我怀疑在执行merge_contract = self.w3.eth.contract(self.contract_instance.mergeModule())
时我需要传递mergeModule
的abi
但我不确定。
我在 运行 时得到的错误是:AttributeError: 'ContractEvents' object has no attribute 'MergeEvent'
这是有道理的,因为 merge_contract
是 Handler
合同而不是 MergeModule
合同。
看来我是对的。将 abi
传递给 MergeModule
是关键。这是我编写的一个 hacky 函数,用于获取对合并模块的引用。
def get_merge_module(self):
from solc import compile_source, compile_files
# compile all of my contract files, one of which is the solidity file with the merge module
compiled_sol = compile_files(utils.get_contract_file_paths(self.CONTRACT_ROOT))
contract_interface = compiled_sol[f'{os.path.join(self.CONTRACT_ROOT, "merge_module.sol")}:MergeModule']
abi = contract_interface["abi"]
merge_contract = self.w3.eth.contract(self.contract_instance.mergeModule(), abi=abi)
return merge_contract
我正在尝试为由我想要的合约创建的合约引发的事件创建一个过滤器。但是,我无法弄清楚。这是我现在拥有的。
MergeModule.sol
pragma solidity ^0.4.23; contract MergeModule { event MergeEvent(uint prID); function MergeModule(){ } function merge(uint prID) public { emit MergeEvent(prID); } }
Handler.sol
pragma solidity ^0.4.23; import "./merge_module.sol"; contract Handler { // a getter for this will automatically be made by the compiler address public mergeModule; function Handler() public { mergeModule = new MergeModule(); } function execute() public { mergeModule.merge(); } }
test_handler.py
from web3 import Web3, EthereumTesterProvider import unittest import os from eth_tester.exceptions import TransactionFailed import tests.utils.utils as utils from web3.utils.filters import Filter class TestHandler(unittest.TestCase): PROJECT_ROOT = os.path.dirname(os.path.dirname(__file__)) CONTRACT_ROOT = os.path.join(PROJECT_ROOT, "contracts") TEST_CONTRACT_ROOT = os.path.join(CONTRACT_ROOT, "test_contracts") def setUp(self): handler_contract_path = os.path.join(self.CONTRACT_ROOT, "handler.sol") # web3.py instance self.w3 = Web3(EthereumTesterProvider()) self.contract, self.contract_address, self.contract_instance = utils.create_contract(self.CONTRACT_ROOT, handler_contract_path, "Handler", self.w3) def test_event_emitted(self): # this prints something different from self.contract_address print(self.contract_instance.mergeModule()) # this creates a reference to the Handler contract. I know this because when I inspect it with the debugger I see `execute` as one of the functions merge_contract = self.w3.eth.contract(self.contract_instance.mergeModule()) merge_event_filter: Filter = merge_contract.events.MergeEvent.createFilter(fromBlock=0) # do stuff here with the filter
utils.create_contract
或多或少做了 quickstart for web3py 中显示的内容,并进行了一些修改以处理编译多个文件。我怀疑在执行merge_contract = self.w3.eth.contract(self.contract_instance.mergeModule())
时我需要传递mergeModule
的abi
但我不确定。
我在 运行 时得到的错误是:AttributeError: 'ContractEvents' object has no attribute 'MergeEvent'
这是有道理的,因为 merge_contract
是 Handler
合同而不是 MergeModule
合同。
看来我是对的。将 abi
传递给 MergeModule
是关键。这是我编写的一个 hacky 函数,用于获取对合并模块的引用。
def get_merge_module(self): from solc import compile_source, compile_files # compile all of my contract files, one of which is the solidity file with the merge module compiled_sol = compile_files(utils.get_contract_file_paths(self.CONTRACT_ROOT)) contract_interface = compiled_sol[f'{os.path.join(self.CONTRACT_ROOT, "merge_module.sol")}:MergeModule'] abi = contract_interface["abi"] merge_contract = self.w3.eth.contract(self.contract_instance.mergeModule(), abi=abi) return merge_contract