Skip to content

Commit

Permalink
asyncify wait_for_transaction_receipt and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
pacrob committed Dec 16, 2021
1 parent 451e9b1 commit 4a311ec
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 47 deletions.
1 change: 1 addition & 0 deletions newsfragments/2265.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add async ``eth.get_transaction_receipt`` and ``eth.wait_for_transaction_receipt`` methods
24 changes: 13 additions & 11 deletions web3/_utils/module_testing/eth_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
InvalidAddress,
InvalidTransaction,
NameNotFound,
TimeExhausted,
TransactionNotFound,
TransactionTypeMismatch,
)
Expand Down Expand Up @@ -704,15 +705,15 @@ async def test_async_eth_mining(
) -> None:
mining = await async_w3.eth.mining # type: ignore
assert is_boolean(mining)

@pytest.mark.asyncio
async def test_async_eth_get_transaction_receipt_mined(
self,
async_w3: "Web3",
block_with_txn: BlockData,
mined_txn_hash: HexStr
) -> None:
receipt = await async_w3.eth.get_transaction_receipt(mined_txn_hash)
receipt = await async_w3.eth.get_transaction_receipt(mined_txn_hash) # type: ignore
assert is_dict(receipt)
assert receipt['blockNumber'] == block_with_txn['number']
assert receipt['blockHash'] == block_with_txn['hash']
Expand All @@ -730,7 +731,7 @@ async def test_async_eth_get_transaction_receipt_mined(
async def test_async_eth_get_transaction_receipt_unmined(
self, async_w3: "Web3", unlocked_account_dual_type: ChecksumAddress
) -> None:
txn_hash = await async_w3.eth.send_transaction({
txn_hash = await async_w3.eth.send_transaction({ # type: ignore
'from': unlocked_account_dual_type,
'to': unlocked_account_dual_type,
'value': Wei(1),
Expand All @@ -739,7 +740,7 @@ async def test_async_eth_get_transaction_receipt_unmined(
'maxPriorityFeePerGas': async_w3.toWei(1, 'gwei')
})
with pytest.raises(TransactionNotFound):
await async_w3.eth.get_transaction_receipt(txn_hash)
await async_w3.eth.get_transaction_receipt(txn_hash) # type: ignore

@pytest.mark.asyncio
async def test_async_eth_get_transaction_receipt_with_log_entry(
Expand All @@ -749,7 +750,7 @@ async def test_async_eth_get_transaction_receipt_with_log_entry(
emitter_contract: "Contract",
txn_hash_with_log: HexStr,
) -> None:
receipt = await async_w3.eth.wait_for_transaction_receipt(txn_hash_with_log)
receipt = await async_w3.eth.wait_for_transaction_receipt(txn_hash_with_log) # type: ignore
assert is_dict(receipt)
assert receipt['blockNumber'] == block_with_txn_with_log['number']
assert receipt['blockHash'] == block_with_txn_with_log['hash']
Expand All @@ -773,7 +774,7 @@ async def test_async_eth_wait_for_transaction_receipt_mined(
block_with_txn: BlockData,
mined_txn_hash: HexStr
) -> None:
receipt = await async_w3.eth.wait_for_transaction_receipt(mined_txn_hash)
receipt = await async_w3.eth.wait_for_transaction_receipt(mined_txn_hash) # type: ignore
assert is_dict(receipt)
assert receipt['blockNumber'] == block_with_txn['number']
assert receipt['blockHash'] == block_with_txn['hash']
Expand All @@ -791,26 +792,26 @@ async def test_async_eth_wait_for_transaction_receipt_mined(
async def test_async_eth_wait_for_transaction_receipt_unmined(
self, async_w3: "Web3", unlocked_account_dual_type: ChecksumAddress
) -> None:
txn_hash = await async_w3.eth.send_transaction({
txn_hash = await async_w3.eth.send_transaction({ # type: ignore
'from': unlocked_account_dual_type,
'to': unlocked_account_dual_type,
'value': Wei(1),
'gas': Wei(21000),
'maxFeePerGas': async_w3.toWei(3, 'gwei'),
'maxPriorityFeePerGas': async_w3.toWei(1, 'gwei')
})
with pytest.raises(TransactionNotFound):
await async_w3.eth.wait_for_transaction_receipt(txn_hash)
with pytest.raises(TimeExhausted):
await async_w3.eth.wait_for_transaction_receipt(txn_hash, timeout=2) # type: ignore

@pytest.mark.asyncio
async def test_async_eth_get_transaction_receipt_with_log_entry(
async def test_async_eth_wait_for_transaction_receipt_with_log_entry(
self,
async_w3: "Web3",
block_with_txn_with_log: BlockData,
emitter_contract: "Contract",
txn_hash_with_log: HexStr,
) -> None:
receipt = await async_w3.eth.get_transaction_receipt(txn_hash_with_log)
receipt = await async_w3.eth.wait_for_transaction_receipt(txn_hash_with_log) # type: ignore
assert is_dict(receipt)
assert receipt['blockNumber'] == block_with_txn_with_log['number']
assert receipt['blockHash'] == block_with_txn_with_log['hash']
Expand All @@ -827,6 +828,7 @@ async def test_async_eth_get_transaction_receipt_with_log_entry(
assert log_entry['transactionIndex'] == 0
assert log_entry['transactionHash'] == HexBytes(txn_hash_with_log)


class EthModuleTest:
def test_eth_protocol_version(self, web3: "Web3") -> None:
with pytest.warns(DeprecationWarning,
Expand Down
26 changes: 0 additions & 26 deletions web3/_utils/transactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,24 +21,17 @@
from web3._utils.compat import (
Literal,
)
from web3._utils.threads import (
Timeout,
)
from web3._utils.utility_methods import (
all_in_dict,
any_in_dict,
)
from web3.constants import (
DYNAMIC_FEE_TXN_PARAMS,
)
from web3.exceptions import (
TransactionNotFound,
)
from web3.types import (
BlockIdentifier,
TxData,
TxParams,
TxReceipt,
Wei,
_Hash32,
)
Expand Down Expand Up @@ -126,25 +119,6 @@ def fill_transaction_defaults(web3: "Web3", transaction: TxParams) -> TxParams:
return merge(defaults, transaction)


def wait_for_transaction_receipt(
web3: "Web3", txn_hash: _Hash32, timeout: float, poll_latency: float
) -> TxReceipt:
with Timeout(timeout) as _timeout:
while True:
try:
txn_receipt = web3.eth.get_transaction_receipt(txn_hash)
except TransactionNotFound:
txn_receipt = None
# FIXME: The check for a null `blockHash` is due to parity's
# non-standard implementation of the JSON-RPC API and should
# be removed once the formal spec for the JSON-RPC endpoints
# has been finalized.
if txn_receipt is not None and txn_receipt['blockHash'] is not None:
break
_timeout.sleep(poll_latency)
return txn_receipt


def get_block_gas_limit(web3: "Web3", block_identifier: Optional[BlockIdentifier] = None) -> Wei:
if block_identifier is None:
block_identifier = web3.eth.block_number
Expand Down
53 changes: 43 additions & 10 deletions web3/eth.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@
extract_valid_transaction_params,
get_required_transaction,
replace_transaction,
wait_for_transaction_receipt as utils_wait_for_transaction_receipt,
)
from web3.contract import (
ConciseContract,
Expand All @@ -71,6 +70,7 @@
)
from web3.exceptions import (
TimeExhausted,
TransactionNotFound,
)
from web3.iban import (
Iban,
Expand Down Expand Up @@ -282,6 +282,12 @@ def call_munger(
mungers=[default_root_munger]
)

# _get_transaction_receipt_awaitable: Method[Callable[[_Hash32],
# Awaitable[TxReceipt]]] = Method(
# RPC.eth_getTransactionReceipt,
# mungers=[default_root_munger]
# )


class AsyncEth(BaseEth):
is_async = True
Expand Down Expand Up @@ -411,13 +417,30 @@ async def get_transaction_count(
async def get_transaction_receipt(
self, transaction_hash: _Hash32
) -> TxReceipt:
return await self._get_transaction_receipt(transaction_hash)
return await self._get_transaction_receipt(transaction_hash) # type: ignore

async def wait_for_transaction_receipt(
self, transaction_hash: _Hash32, timeout: float = 120, poll_latency: float = 0.1
) -> TxReceipt:
try:
return await utils_wait_for_transaction_receipt(self.web3, transaction_hash, timeout, poll_latency)
with Timeout(timeout) as _timeout:
while True:
try:
tx_receipt = await self._get_transaction_receipt( # type: ignore
transaction_hash
)
except TransactionNotFound:
tx_receipt = None
# FIXME: The check for a null `blockHash` is due to parity's
# non-standard implementation of the JSON-RPC API and should
# be removed once the formal spec for the JSON-RPC endpoints
# has been finalized.
# if txn_receipt is not None and txn_receipt['blockHash'] is not None:
if tx_receipt is not None:
break
_timeout.sleep(poll_latency)
return tx_receipt

except Timeout:
raise TimeExhausted(
"Transaction {!r} is not in the chain, after {} seconds".format(
Expand Down Expand Up @@ -706,7 +729,22 @@ def wait_for_transaction_receipt(
self, transaction_hash: _Hash32, timeout: float = 120, poll_latency: float = 0.1
) -> TxReceipt:
try:
return utils_wait_for_transaction_receipt(self.web3, transaction_hash, timeout, poll_latency)
with Timeout(timeout) as _timeout:
while True:
try:
tx_receipt = self._get_transaction_receipt(transaction_hash)
except TransactionNotFound:
tx_receipt = None
# FIXME: The check for a null `blockHash` is due to parity's
# non-standard implementation of the JSON-RPC API and should
# be removed once the formal spec for the JSON-RPC endpoints
# has been finalized.
# if txn_receipt is not None and txn_receipt['blockHash'] is not None:
if tx_receipt is not None:
break
_timeout.sleep(poll_latency)
return tx_receipt

except Timeout:
raise TimeExhausted(
"Transaction {!r} is not in the chain, after {} seconds".format(
Expand All @@ -720,11 +758,6 @@ def get_transaction_receipt(
) -> TxReceipt:
return self._get_transaction_receipt(transaction_hash)

# get_transaction_receipt: Method[Callable[[_Hash32], TxReceipt]] = Method(
# RPC.eth_getTransactionReceipt,
# mungers=[default_root_munger]
# )

get_transaction_count: Method[Callable[..., Nonce]] = Method(
RPC.eth_getTransactionCount,
mungers=[BaseEth.block_id_munger],
Expand Down Expand Up @@ -952,7 +985,7 @@ def setGasPriceStrategy(self, gas_price_strategy: GasPriceStrategy) -> None:
sendRawTransaction = DeprecatedMethod(send_raw_transaction, # type: ignore
'sendRawTransaction',
'send_raw_transaction')
getTransactionReceipt = DeprecatedMethod(get_transaction_receipt,
getTransactionReceipt = DeprecatedMethod(get_transaction_receipt, # type: ignore
'getTransactionReceipt',
'get_transaction_receipt')
uninstallFilter = DeprecatedMethod(uninstall_filter, 'uninstallFilter', 'uninstall_filter')
Expand Down

0 comments on commit 4a311ec

Please sign in to comment.