Skip to content

Commit

Permalink
Allow optional parens on events for sync and async contracts
Browse files Browse the repository at this point in the history
  • Loading branch information
reedsa committed Oct 18, 2024
1 parent fccc9dc commit 465def7
Show file tree
Hide file tree
Showing 5 changed files with 157 additions and 46 deletions.
46 changes: 46 additions & 0 deletions tests/core/contracts/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,28 @@ def non_strict_emitter(
return emitter_contract


@pytest.fixture
def emitter(
w3,
emitter_contract_data,
wait_for_transaction,
wait_for_block,
address_conversion_func,
):
emitter_contract_factory = w3.eth.contract(**emitter_contract_data)

wait_for_block(w3)
deploy_txn_hash = emitter_contract_factory.constructor().transact({"gas": 30029121})
deploy_receipt = wait_for_transaction(w3, deploy_txn_hash)
contract_address = address_conversion_func(deploy_receipt["contractAddress"])

bytecode = w3.eth.get_code(contract_address)
assert bytecode == emitter_contract_factory.bytecode_runtime
_emitter = emitter_contract_factory(address=contract_address)
assert _emitter.address == contract_address
return _emitter


# --- event contract --- #


Expand Down Expand Up @@ -760,3 +782,27 @@ def async_call(request):
@pytest.fixture
def async_estimate_gas(request):
return async_partial(async_invoke_contract, api_call_desig="estimate_gas")


@pytest_asyncio.fixture
async def async_emitter(
async_w3,
emitter_contract_data,
async_wait_for_transaction,
async_wait_for_block,
address_conversion_func,
):
async_emitter_contract_factory = async_w3.eth.contract(**emitter_contract_data)

await async_wait_for_block(async_w3)
deploy_txn_hash = await async_emitter_contract_factory.constructor().transact(
{"gas": 10000000}
)
deploy_receipt = await async_wait_for_transaction(async_w3, deploy_txn_hash)
contract_address = address_conversion_func(deploy_receipt["contractAddress"])

bytecode = await async_w3.eth.get_code(contract_address)
assert bytecode == async_emitter_contract_factory.bytecode_runtime
_emitter = async_emitter_contract_factory(address=contract_address)
assert _emitter.address == contract_address
return _emitter
101 changes: 78 additions & 23 deletions tests/core/contracts/test_extracting_event_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,28 +40,6 @@ def dup_txn_receipt(w3, indexed_event_contract, wait_for_transaction, event_cont
return wait_for_transaction(w3, dup_txn_hash)


@pytest.fixture
def emitter(
w3,
emitter_contract_data,
wait_for_transaction,
wait_for_block,
address_conversion_func,
):
emitter_contract_factory = w3.eth.contract(**emitter_contract_data)

wait_for_block(w3)
deploy_txn_hash = emitter_contract_factory.constructor().transact({"gas": 30029121})
deploy_receipt = wait_for_transaction(w3, deploy_txn_hash)
contract_address = address_conversion_func(deploy_receipt["contractAddress"])

bytecode = w3.eth.get_code(contract_address)
assert bytecode == emitter_contract_factory.bytecode_runtime
_emitter = emitter_contract_factory(address=contract_address)
assert _emitter.address == contract_address
return _emitter


@pytest.mark.parametrize(
"contract_fn,event_name,call_args,expected_args",
(
Expand Down Expand Up @@ -545,7 +523,7 @@ def test_contract_event_get_logs_sorted_by_log_index(w3, emitter, request_mocker
]

with request_mocker(w3, mock_results={"eth_getLogs": get_logs_response}):
logs = emitter.events.LogNoArguments.get_logs()
logs = emitter.events.LogNoArguments().get_logs()

sorted_logs = sorted(
emitter.events.LogNoArguments.get_logs(),
Expand All @@ -560,6 +538,83 @@ def test_contract_event_get_logs_sorted_by_log_index(w3, emitter, request_mocker
assert logs == sorted_logs


@pytest.mark.asyncio
async def test_async_contract_event_get_logs_sorted_by_log_index(
async_w3, async_emitter, request_mocker
):
get_logs_response = [
{
"type": "mined",
"logIndex": 10,
"transactionIndex": 0,
"transactionHash": "0xaef7f312d863780b861d8c38984b2a33f77e9508810735e2b042143f7f189f83", # noqa: E501
"blockHash": "0x2200ec3324fdaca4ee2f4629489d2d06fb28108dae61b63b84ef39702e2b64e7", # noqa: E501
"blockNumber": 3,
"address": "0xF2E246BB76DF876Cef8b38ae84130F4F55De395b",
"data": "0x",
"topics": [
"0x1e86022f78f8d04f8e3dfd13a2bdb280403e6632877c0dbee5e4eeb259908a5c"
],
},
{
"type": "mined",
"logIndex": 0,
"transactionIndex": 0,
"transactionHash": "0x61e57bb1b5af14ca1b0964a84fb640bf39927961f26311a6450475a749e00cbb", # noqa: E501
"blockHash": "0x73dd9a3b0f581689ebd67adea0debe05672a334c723379dc506fb71a666c1754", # noqa: E501
"blockNumber": 4,
"address": "0xF2E246BB76DF876Cef8b38ae84130F4F55De395b",
"data": "0x",
"topics": [
"0x1e86022f78f8d04f8e3dfd13a2bdb280403e6632877c0dbee5e4eeb259908a5c"
],
},
{
"type": "mined",
"logIndex": 123,
"transactionIndex": 0,
"transactionHash": "0x61e57bb1b5af14ca1b0964a84fb640bf39927961f26311a6450475a749e00cbb", # noqa: E501
"blockHash": "0x73dd9a3b0f581689ebd67adea0debe05672a334c723379dc506fb71a666c1754", # noqa: E501
"blockNumber": 1,
"address": "0xF2E246BB76DF876Cef8b38ae84130F4F55De395b",
"data": "0x",
"topics": [
"0x1e86022f78f8d04f8e3dfd13a2bdb280403e6632877c0dbee5e4eeb259908a5c"
],
},
{
"type": "mined",
"logIndex": 54,
"transactionIndex": 0,
"transactionHash": "0x61e57bb1b5af14ca1b0964a84fb640bf39927961f26311a6450475a749e00cbb", # noqa: E501
"blockHash": "0x73dd9a3b0f581689ebd67adea0debe05672a334c723379dc506fb71a666c1754", # noqa: E501
"blockNumber": 1,
"address": "0xF2E246BB76DF876Cef8b38ae84130F4F55De395b",
"data": "0x",
"topics": [
"0x1e86022f78f8d04f8e3dfd13a2bdb280403e6632877c0dbee5e4eeb259908a5c"
],
},
]

async with request_mocker(
async_w3, mock_results={"eth_getLogs": lambda *_: get_logs_response}
):
logs = await async_emitter.events.LogNoArguments().get_logs()

sorted_logs = sorted(
await async_emitter.events.LogNoArguments.get_logs(),
key=lambda log: log["logIndex"],
)
sorted_logs = sorted(
await async_emitter.events.LogNoArguments.get_logs(),
key=lambda log: log["blockNumber"],
)

assert len(logs) == 4
assert logs == sorted_logs


@pytest.mark.parametrize(
"contract_fn,event_name,call_args,expected_args,warning_msg,process_receipt",
(
Expand Down
22 changes: 0 additions & 22 deletions tests/core/contracts/test_extracting_event_data_old.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,6 @@
)


@pytest.fixture()
def emitter(
w3,
emitter_contract_data,
wait_for_transaction,
wait_for_block,
address_conversion_func,
):
emitter_contract_factory = w3.eth.contract(**emitter_contract_data)

wait_for_block(w3)
deploy_txn_hash = emitter_contract_factory.constructor().transact({"gas": 10000000})
deploy_receipt = wait_for_transaction(w3, deploy_txn_hash)
contract_address = address_conversion_func(deploy_receipt["contractAddress"])

bytecode = w3.eth.get_code(contract_address)
assert bytecode == emitter_contract_factory.bytecode_runtime
_emitter = emitter_contract_factory(address=contract_address)
assert _emitter.address == contract_address
return _emitter


@pytest.mark.parametrize(
"contract_fn,event_name,call_args,expected_args",
(
Expand Down
19 changes: 18 additions & 1 deletion web3/contract/async_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

from eth_typing import (
ABI,
ABIEvent,
ChecksumAddress,
)
from eth_utils import (
Expand Down Expand Up @@ -99,6 +100,9 @@
StateOverride,
TxParams,
)
from web3.utils.abi import (
get_abi_element,
)

if TYPE_CHECKING:
from ens import AsyncENS # noqa: F401
Expand All @@ -109,6 +113,17 @@ class AsyncContractEvent(BaseContractEvent):
# mypy types
w3: "AsyncWeb3"

def __call__(self) -> "AsyncContractEvent":
clone = copy.copy(self)

if not self.abi:
self.abi = cast(
ABIEvent,
get_abi_element(self.contract_abi, self.event_name),
)

return clone

@combomethod
async def get_logs(
self,
Expand Down Expand Up @@ -199,7 +214,9 @@ async def get_logs(
all_event_logs,
argument_filters,
)
return cast(Awaitable[Iterable[EventData]], filtered_logs)
sorted_logs = sorted(filtered_logs, key=lambda e: e["logIndex"])
sorted_logs = sorted(sorted_logs, key=lambda e: e["blockNumber"])
return cast(Awaitable[Iterable[EventData]], sorted_logs)

@combomethod
async def create_filter(
Expand Down
15 changes: 15 additions & 0 deletions web3/contract/contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

from eth_typing import (
ABI,
ABIEvent,
ChecksumAddress,
)
from eth_utils import (
Expand Down Expand Up @@ -96,6 +97,9 @@
StateOverride,
TxParams,
)
from web3.utils.abi import (
get_abi_element,
)

if TYPE_CHECKING:
from ens import ENS # noqa: F401
Expand All @@ -106,6 +110,17 @@ class ContractEvent(BaseContractEvent):
# mypy types
w3: "Web3"

def __call__(self) -> "ContractEvent":
clone = copy.copy(self)

if not self.abi:
self.abi = cast(
ABIEvent,
get_abi_element(self.contract_abi, self.event_name),
)

return clone

@combomethod
def get_logs(
self,
Expand Down

0 comments on commit 465def7

Please sign in to comment.