I'm trying to create a filter for an event raised by a contract created by my desired contract. However, I can't figure it out. Here's what I have right now.
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
more or less does what's shown in the quickstart for web3py with some modifications to handle compiling multiple files. I suspect that I need to pass the abi
of the mergeModule
when executing merge_contract = self.w3.eth.contract(self.contract_instance.mergeModule())
but I'm not sure.
The error that I get when I run this is:
AttributeError: 'ContractEvents' object has no attribute 'MergeEvent'
which makes sense because merge_contract
is a Handler
contract and not a MergeModule
contract.
Looks like I was right. Passing the abi
for the MergeModule
was the key. Here's a hacky function I wrote to get a reference to the merge module.
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