Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions docs/migration_guide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,14 @@ RPC related changes:

9. :class:`StarknetChainId` changed from ``Enum`` to ``IntEnum``.


Deprecations
------------
.. currentmodule:: starknet_py.net.client

1. `wait_for_accept` parameter in :meth:`Client.wait_for_tx` and :meth:`SentTransaction.wait_for_acceptance` have been deprecated.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
1. `wait_for_accept` parameter in :meth:`Client.wait_for_tx` and :meth:`SentTransaction.wait_for_acceptance` have been deprecated.
1. `wait_for_accept` parameter in :meth:`Client.wait_for_tx` and :meth:`SentTransaction.wait_for_acceptance` has been deprecated.



Bugfixes
--------

Expand Down
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ compile_contracts_v1 = "bash starknet_py/tests/e2e/mock/compile_contracts_v1.sh"
compile_contracts_v2 = "bash starknet_py/tests/e2e/mock/compile_contracts_v2.sh"
circular_imports_check.shell = "poetry run pytest circular.py"
ci = ["lint", "format_check", "typecheck", "test_ci"]
precommit.sequence = ["format", "lint", "typecheck"]
precommit.ignore_fail = true

[tool.poetry.build]
generate-setup-file = true
Expand Down
17 changes: 12 additions & 5 deletions starknet_py/contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import dataclasses
import json
import warnings
from dataclasses import dataclass
from functools import cached_property
from typing import Dict, List, Optional, Tuple, TypeVar, Union
Expand Down Expand Up @@ -115,18 +116,24 @@ class SentTransaction:

async def wait_for_acceptance(
self: TypeSentTransaction,
wait_for_accept: Optional[bool] = False,
check_interval=5,
wait_for_accept: Optional[bool] = None,
check_interval: float = 5,
retries: int = 200,
) -> TypeSentTransaction:
"""
Waits for transaction to be accepted on chain. By default, returns when status is ``PENDING`` -
use ``wait_for_accept`` to wait till ``ACCEPTED`` status.
Waits for transaction to be accepted on chain till ``ACCEPTED`` status.
Returns a new SentTransaction instance, **does not mutate original instance**.
"""
if wait_for_accept is not None:
warnings.warn(
"Parameter `wait_for_accept` has been deprecated - since Starknet 0.12.0, transactions in a PENDING"
" block have status ACCEPTED_ON_L2."
)

block_number, status = await self._client.wait_for_tx(
self.hash,
wait_for_accept=wait_for_accept,
check_interval=check_interval,
retries=retries,
)
return dataclasses.replace(
self,
Expand Down
52 changes: 34 additions & 18 deletions starknet_py/net/client.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

import asyncio
import warnings
from abc import ABC, abstractmethod
from typing import List, Optional, Tuple, Union

Expand Down Expand Up @@ -135,27 +136,39 @@ async def get_transaction_receipt(
async def wait_for_tx(
self,
tx_hash: Hash,
wait_for_accept: Optional[bool] = False, # pylint: disable=unused-argument
check_interval=5,
wait_for_accept: Optional[bool] = None, # pylint: disable=unused-argument
check_interval: float = 5,
retries: int = 200,
) -> Tuple[int, TransactionStatus]:
# pylint: disable=too-many-branches
"""
Awaits for transaction to get accepted or at least pending by polling its status.

:param tx_hash: Transaction's hash.
:param wait_for_accept: If true waits for at least ACCEPTED_ON_L2 status, otherwise waits for at least PENDING.
Defaults to false
:param wait_for_accept:
.. deprecated:: 0.17.0
Parameter `wait_for_accept` has been deprecated - since Starknet 0.12.0, transactions in a PENDING
block have status ACCEPTED_ON_L2.
:param check_interval: Defines interval between checks.
:param retries: Defines how many times the transaction is checked until an error is thrown.
:return: Tuple containing block number and transaction status.
"""
if check_interval <= 0:
raise ValueError("Argument check_interval has to be greater than 0.")

first_run = True
try:
while True:
if retries <= 0:
raise ValueError("Argument retries has to be greater than 0.")
if wait_for_accept is not None:
warnings.warn(
"Parameter `wait_for_accept` has been deprecated - since Starknet 0.12.0, transactions in a PENDING"
" block have status ACCEPTED_ON_L2."
)

while True:
try:
result = await self.get_transaction_receipt(tx_hash=tx_hash)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We would probably need to catch failed receipt fetch, and retry

status = result.status
if status is None:
raise ClientError(f"Unknown status in transaction {tx_hash}.")

if status in (
TransactionStatus.ACCEPTED_ON_L1,
Expand All @@ -168,24 +181,27 @@ async def wait_for_tx(
message=result.rejection_reason,
)
if status == TransactionStatus.NOT_RECEIVED:
if not first_run:
if retries == 0:
raise TransactionNotReceivedError()
elif status != TransactionStatus.RECEIVED:
# This will never get executed with current possible transactions statuses
raise TransactionFailedError(
message=result.rejection_reason,
)

first_run = False
retries -= 1
await asyncio.sleep(check_interval)
except asyncio.CancelledError as exc:
raise TransactionNotReceivedError from exc
except ClientError as exc:
if "Transaction hash not found" in exc.message:
raise ClientError(
"Nodes can't access pending transactions, try using parameter 'wait_for_accept=True'."
) from exc
raise exc
except asyncio.CancelledError as exc:
raise TransactionNotReceivedError from exc
except ClientError as exc:
if (
"Transaction hash not found" not in exc.message
and "Unknown status" not in exc.message
):
raise exc
retries -= 1
if retries == 0:
raise TransactionNotReceivedError from exc

@abstractmethod
async def estimate_fee(
Expand Down
2 changes: 1 addition & 1 deletion starknet_py/net/schemas/rpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ def make_dataclass(self, data, **kwargs) -> L2toL1Message:

class TransactionReceiptSchema(Schema):
hash = Felt(data_key="transaction_hash", required=True)
status = StatusField(data_key="status", required=True)
status = StatusField(data_key="status", load_default=None)
block_number = fields.Integer(data_key="block_number", load_default=None)
block_hash = Felt(data_key="block_hash", load_default=None)
actual_fee = Felt(data_key="actual_fee", required=True)
Expand Down
4 changes: 1 addition & 3 deletions starknet_py/tests/e2e/account/account_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -345,9 +345,7 @@ async def test_deploy_account(client, deploy_account_details_factory, map_contra
),
max_fee=MAX_FEE,
)
_, status = await account.client.wait_for_tx(
res.transaction_hash, wait_for_accept=True
)
_, status = await account.client.wait_for_tx(res.transaction_hash)

assert status in (
TransactionStatus.ACCEPTED_ON_L1,
Expand Down
4 changes: 1 addition & 3 deletions starknet_py/tests/e2e/client/client_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -369,9 +369,7 @@ async def test_wait_for_tx_cancelled(client, get_tx_receipt, request):
type=TransactionType.INVOKE,
)
client = request.getfixturevalue(client)
task = asyncio.create_task(
client.wait_for_tx(tx_hash=0x1, wait_for_accept=True)
)
task = asyncio.create_task(client.wait_for_tx(tx_hash=0x1))
await asyncio.sleep(1)
task.cancel()

Expand Down
9 changes: 0 additions & 9 deletions starknet_py/tests/e2e/client/full_node_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,15 +156,6 @@ async def test_get_storage_at_incorrect_address_full_node_client(full_node_clien
)


@pytest.mark.asyncio
async def test_wait_for_tx_invalid_tx_hash(full_node_client):
with pytest.raises(
ClientError,
match="Nodes can't access pending transactions, try using parameter 'wait_for_accept=True'.",
):
_ = await full_node_client.wait_for_tx(tx_hash=0x123456789)


@pytest.mark.run_on_devnet
@pytest.mark.asyncio
async def test_get_events_without_following_continuation_token(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ async def test_prepare_without_max_fee(map_contract):
@pytest.mark.parametrize("key, value", ((2, 13), (412312, 32134), (12345, 3567)))
async def test_invoke_and_call(key, value, map_contract):
invocation = await map_contract.functions["put"].invoke(key, value, max_fee=MAX_FEE)
await invocation.wait_for_acceptance(wait_for_accept=True)
await invocation.wait_for_acceptance()
(response,) = await map_contract.functions["get"].call(key)

assert response == value
Expand Down
4 changes: 1 addition & 3 deletions starknet_py/tests/e2e/declare/declare_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ async def test_declare_tx(account, map_compiled_contract):
)
result = await account.client.declare(declare_tx)

await account.client.wait_for_tx(
tx_hash=result.transaction_hash, wait_for_accept=True
)
await account.client.wait_for_tx(tx_hash=result.transaction_hash)


@pytest.mark.asyncio
Expand Down
2 changes: 1 addition & 1 deletion starknet_py/tests/e2e/fixtures/contracts_v1.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ async def declare_cairo1_contract(
assert declare_tx.version == 2

resp = await account.client.declare(declare_tx)
await account.client.wait_for_tx(resp.transaction_hash, wait_for_accept=True)
await account.client.wait_for_tx(resp.transaction_hash)

return resp.class_hash, resp.transaction_hash

Expand Down