Skip to content

Commit

Permalink
asyncify eth.get_logs (#2310)
Browse files Browse the repository at this point in the history
* asyncify eth.get_logs

* factor out `assert_contains_log` function

Co-authored-by: Paul Robinson <[email protected]>
  • Loading branch information
DefiDebauchery and Paul Robinson authored Jan 26, 2022
1 parent c85b7d9 commit 7723030
Show file tree
Hide file tree
Showing 4 changed files with 190 additions and 22 deletions.
1 change: 1 addition & 0 deletions docs/providers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,7 @@ Eth
- :meth:`web3.eth.get_balance() <web3.eth.Eth.get_balance>`
- :meth:`web3.eth.get_block() <web3.eth.Eth.get_block>`
- :meth:`web3.eth.get_code() <web3.eth.Eth.get_code>`
- :meth:`web3.eth.get_logs() <web3.eth.Eth.get_logs>`
- :meth:`web3.eth.get_raw_transaction() <web3.eth.Eth.get_raw_transaction>`
- :meth:`web3.eth.get_raw_transaction_by_block() <web3.eth.Eth.get_raw_transaction_by_block>`
- :meth:`web3.eth.get_transaction() <web3.eth.Eth.get_transaction>`
Expand Down
1 change: 1 addition & 0 deletions newsfragments/2310.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add async `eth.get_logs` method
199 changes: 177 additions & 22 deletions web3/_utils/module_testing/eth_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,22 @@ def mine_pending_block(web3: "Web3") -> None:
web3.geth.miner.stop() # type: ignore


def _assert_contains_log(
result: Sequence[LogReceipt],
block_with_txn_with_log: BlockData,
emitter_contract_address: ChecksumAddress,
txn_hash_with_log: HexStr,
) -> None:
assert len(result) == 1
log_entry = result[0]
assert log_entry['blockNumber'] == block_with_txn_with_log['number']
assert log_entry['blockHash'] == block_with_txn_with_log['hash']
assert log_entry['logIndex'] == 0
assert is_same_address(log_entry['address'], emitter_contract_address)
assert log_entry['transactionIndex'] == 0
assert log_entry['transactionHash'] == HexBytes(txn_hash_with_log)


class AsyncEthModuleTest:
@pytest.mark.asyncio
async def test_eth_gas_price(self, async_w3: "Web3") -> None:
Expand Down Expand Up @@ -848,6 +864,143 @@ async def test_async_eth_accounts(self, async_w3: "Web3") -> None:
))
assert await async_w3.eth.coinbase in accounts # type: ignore

@pytest.mark.asyncio
async def test_async_eth_get_logs_without_logs(
self, async_w3: "Web3", block_with_txn_with_log: BlockData
) -> None:
# Test with block range

filter_params: FilterParams = {
"fromBlock": BlockNumber(0),
"toBlock": BlockNumber(block_with_txn_with_log['number'] - 1),
}
result = await async_w3.eth.get_logs(filter_params) # type: ignore
assert len(result) == 0

# the range is wrong
filter_params = {
"fromBlock": block_with_txn_with_log['number'],
"toBlock": BlockNumber(block_with_txn_with_log['number'] - 1),
}
result = await async_w3.eth.get_logs(filter_params) # type: ignore
assert len(result) == 0

# Test with `address`

# filter with other address
filter_params = {
"fromBlock": BlockNumber(0),
"address": UNKNOWN_ADDRESS,
}
result = await async_w3.eth.get_logs(filter_params) # type: ignore
assert len(result) == 0

# Test with multiple `address`

# filter with other address
filter_params = {
"fromBlock": BlockNumber(0),
"address": [UNKNOWN_ADDRESS, UNKNOWN_ADDRESS],
}
result = await async_w3.eth.get_logs(filter_params) # type: ignore
assert len(result) == 0

@pytest.mark.asyncio
async def test_async_eth_get_logs_with_logs(
self,
async_w3: "Web3",
block_with_txn_with_log: BlockData,
emitter_contract_address: ChecksumAddress,
txn_hash_with_log: HexStr,
) -> None:

# Test with block range

# the range includes the block where the log resides in
filter_params: FilterParams = {
"fromBlock": block_with_txn_with_log['number'],
"toBlock": block_with_txn_with_log['number'],
}
result = await async_w3.eth.get_logs(filter_params) # type: ignore
_assert_contains_log(
result,
block_with_txn_with_log,
emitter_contract_address,
txn_hash_with_log
)

# specify only `from_block`. by default `to_block` should be 'latest'
filter_params = {
"fromBlock": BlockNumber(0),
}
result = await async_w3.eth.get_logs(filter_params) # type: ignore
_assert_contains_log(
result,
block_with_txn_with_log,
emitter_contract_address,
txn_hash_with_log
)

# Test with `address`

# filter with emitter_contract.address
filter_params = {
"fromBlock": BlockNumber(0),
"address": emitter_contract_address,
}

@pytest.mark.asyncio
async def test_async_eth_get_logs_with_logs_topic_args(
self,
async_w3: "Web3",
block_with_txn_with_log: BlockData,
emitter_contract_address: ChecksumAddress,
txn_hash_with_log: HexStr,
) -> None:

# Test with None event sig

filter_params: FilterParams = {
"fromBlock": BlockNumber(0),
"topics": [
None,
HexStr('0x000000000000000000000000000000000000000000000000000000000000d431')],
}

result = await async_w3.eth.get_logs(filter_params) # type: ignore
_assert_contains_log(
result,
block_with_txn_with_log,
emitter_contract_address,
txn_hash_with_log
)

# Test with None indexed arg
filter_params = {
"fromBlock": BlockNumber(0),
"topics": [
HexStr('0x057bc32826fbe161da1c110afcdcae7c109a8b69149f727fc37a603c60ef94ca'),
None],
}
result = await async_w3.eth.get_logs(filter_params) # type: ignore
_assert_contains_log(
result,
block_with_txn_with_log,
emitter_contract_address,
txn_hash_with_log
)

@pytest.mark.asyncio
async def test_async_eth_get_logs_with_logs_none_topic_args(self, async_w3: "Web3") -> None:
# Test with None overflowing
filter_params: FilterParams = {
"fromBlock": BlockNumber(0),
"topics": [None, None, None],
}

result = await async_w3.eth.get_logs(filter_params) # type: ignore
assert len(result) == 0

def test_async_provider_default_account(
self,
async_w3: "Web3",
Expand Down Expand Up @@ -2718,15 +2871,6 @@ def test_eth_get_logs_with_logs(
emitter_contract_address: ChecksumAddress,
txn_hash_with_log: HexStr,
) -> None:
def assert_contains_log(result: Sequence[LogReceipt]) -> None:
assert len(result) == 1
log_entry = result[0]
assert log_entry['blockNumber'] == block_with_txn_with_log['number']
assert log_entry['blockHash'] == block_with_txn_with_log['hash']
assert log_entry['logIndex'] == 0
assert is_same_address(log_entry['address'], emitter_contract_address)
assert log_entry['transactionIndex'] == 0
assert log_entry['transactionHash'] == HexBytes(txn_hash_with_log)

# Test with block range

Expand All @@ -2736,14 +2880,24 @@ def assert_contains_log(result: Sequence[LogReceipt]) -> None:
"toBlock": block_with_txn_with_log['number'],
}
result = web3.eth.get_logs(filter_params)
assert_contains_log(result)
_assert_contains_log(
result,
block_with_txn_with_log,
emitter_contract_address,
txn_hash_with_log
)

# specify only `from_block`. by default `to_block` should be 'latest'
filter_params = {
"fromBlock": BlockNumber(0),
}
result = web3.eth.get_logs(filter_params)
assert_contains_log(result)
_assert_contains_log(
result,
block_with_txn_with_log,
emitter_contract_address,
txn_hash_with_log
)

# Test with `address`

Expand All @@ -2760,15 +2914,6 @@ def test_eth_get_logs_with_logs_topic_args(
emitter_contract_address: ChecksumAddress,
txn_hash_with_log: HexStr,
) -> None:
def assert_contains_log(result: Sequence[LogReceipt]) -> None:
assert len(result) == 1
log_entry = result[0]
assert log_entry['blockNumber'] == block_with_txn_with_log['number']
assert log_entry['blockHash'] == block_with_txn_with_log['hash']
assert log_entry['logIndex'] == 0
assert is_same_address(log_entry['address'], emitter_contract_address)
assert log_entry['transactionIndex'] == 0
assert log_entry['transactionHash'] == HexBytes(txn_hash_with_log)

# Test with None event sig

Expand All @@ -2780,7 +2925,12 @@ def assert_contains_log(result: Sequence[LogReceipt]) -> None:
}

result = web3.eth.get_logs(filter_params)
assert_contains_log(result)
_assert_contains_log(
result,
block_with_txn_with_log,
emitter_contract_address,
txn_hash_with_log
)

# Test with None indexed arg
filter_params = {
Expand All @@ -2790,7 +2940,12 @@ def assert_contains_log(result: Sequence[LogReceipt]) -> None:
None],
}
result = web3.eth.get_logs(filter_params)
assert_contains_log(result)
_assert_contains_log(
result,
block_with_txn_with_log,
emitter_contract_address,
txn_hash_with_log
)

def test_eth_get_logs_with_logs_none_topic_args(self, web3: "Web3") -> None:
# Test with None overflowing
Expand Down
11 changes: 11 additions & 0 deletions web3/eth.py
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,17 @@ async def get_code(
) -> HexBytes:
return await self._get_code(account, block_identifier)

_get_logs: Method[Callable[[FilterParams], Awaitable[List[LogReceipt]]]] = Method(
RPC.eth_getLogs,
mungers=[default_root_munger]
)

async def get_logs(
self,
filter_params: FilterParams,
) -> List[LogReceipt]:
return await self._get_logs(filter_params)

_get_transaction_count: Method[Callable[..., Awaitable[Nonce]]] = Method(
RPC.eth_getTransactionCount,
mungers=[BaseEth.block_id_munger],
Expand Down

0 comments on commit 7723030

Please sign in to comment.