From a51a77786984003b085b29621527c94ca8088abe Mon Sep 17 00:00:00 2001 From: ddoktorski <45050160+ddoktorski@users.noreply.github.com> Date: Wed, 29 Nov 2023 18:33:17 +0100 Subject: [PATCH 1/7] Remove GatewayClient (#1225) * Remove Gateway client and related tests * Update test on networks command in checks.yml * Remove network parameter from get_deploy_account_transaction * Replace failing tests on integration with the same tests on testnet * Test simulate_transactions for pending block instead of latest * Remove Gateway from docs and update client models (#1226) * Remove GatewayClient from documentation * Remove gateway related classes from client_models * Remove net property * Fix typos and broken links in docs * Remove contract_address from DeployTransactionSchema * Remove old GatewayClient references in migration guide * Add initial info to migration guide * Rename l2_to_l1_messsages field in TransactionReceipt --- .github/workflows/checks.yml | 27 +- README.md | 8 +- docs/api.rst | 1 - docs/api/client.rst | 2 +- docs/api/gateway_client.rst | 8 - docs/guide/account_and_client.rst | 9 +- docs/guide/deploying_contracts.rst | 2 +- docs/guide/serialization.rst | 4 +- docs/migration_guide.rst | 24 +- docs/quickstart.rst | 25 - pyproject.toml | 36 +- starknet_py/conftest.py | 1 - starknet_py/net/account/account.py | 14 +- starknet_py/net/account/account_test.py | 28 +- starknet_py/net/client.py | 19 +- starknet_py/net/client_models.py | 216 +------ starknet_py/net/client_utils.py | 3 - starknet_py/net/full_node_client.py | 15 - starknet_py/net/gateway_client.py | 561 ------------------ starknet_py/net/http_client.py | 26 - starknet_py/net/networks.py | 17 +- starknet_py/net/schemas/gateway.py | 488 +-------------- starknet_py/net/schemas/rpc.py | 67 +-- starknet_py/tests/e2e/account/account_test.py | 75 +-- starknet_py/tests/e2e/block_test.py | 35 +- starknet_py/tests/e2e/client/client_test.py | 152 +---- .../e2e/client/fixtures/prepare_network.py | 8 +- .../tests/e2e/client/fixtures/transactions.py | 3 +- .../tests/e2e/client/full_node_test.py | 100 ++-- starknet_py/tests/e2e/client/gateway_test.py | 224 ------- .../contract_interaction/interaction_test.py | 38 +- starknet_py/tests/e2e/core/__init__.py | 0 starknet_py/tests/e2e/core/fixtures.py | 189 ------ starknet_py/tests/e2e/core/test_core.py | 108 ---- .../test_deploy_prefunded_account.py | 5 +- .../e2e/docs/code_examples/test_account.py | 14 +- .../e2e/docs/code_examples/test_contract.py | 20 - .../code_examples/test_full_node_client.py | 81 ++- .../docs/code_examples/test_gateway_client.py | 196 ------ .../test_account_sign_without_execute.py | 2 - .../e2e/docs/guide/test_cairo1_contract.py | 10 +- .../tests/e2e/docs/guide/test_custom_nonce.py | 8 +- .../docs/guide/test_executing_transactions.py | 2 +- .../e2e/docs/guide/test_full_node_client.py | 11 +- .../test_synchronous_full_node_client.py | 10 +- .../test_synchronous_gateway_client.py | 12 - .../quickstart/test_using_full_node_client.py | 10 +- .../quickstart/test_using_gateway_client.py | 26 - starknet_py/tests/e2e/fixtures/accounts.py | 70 +-- starknet_py/tests/e2e/fixtures/clients.py | 46 +- starknet_py/tests/e2e/fixtures/constants.py | 10 +- starknet_py/tests/e2e/fixtures/contracts.py | 62 +- .../tests/e2e/fixtures/contracts_v1.py | 4 +- starknet_py/tests/e2e/fixtures/misc.py | 13 +- starknet_py/tests/e2e/fixtures/proxy.py | 26 +- .../e2e/tests_on_networks/client_test.py | 169 +----- .../tests/e2e/tests_on_networks/fixtures.py | 99 +--- .../e2e/tests_on_networks/trace_api_test.py | 30 +- starknet_py/tests/e2e/utils.py | 21 +- 59 files changed, 434 insertions(+), 3056 deletions(-) delete mode 100644 docs/api/gateway_client.rst delete mode 100644 starknet_py/net/gateway_client.py delete mode 100644 starknet_py/tests/e2e/client/gateway_test.py delete mode 100644 starknet_py/tests/e2e/core/__init__.py delete mode 100644 starknet_py/tests/e2e/core/fixtures.py delete mode 100644 starknet_py/tests/e2e/core/test_core.py delete mode 100644 starknet_py/tests/e2e/docs/code_examples/test_gateway_client.py delete mode 100644 starknet_py/tests/e2e/docs/quickstart/test_synchronous_gateway_client.py delete mode 100644 starknet_py/tests/e2e/docs/quickstart/test_using_gateway_client.py diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 139e0238a..b8842651b 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -214,10 +214,8 @@ jobs: - name: Run tests run: | - poetry run poe test_ci_full_node_v2 - poetry run poe test_ci_full_node_v1 - poetry run poe test_ci_gateway_v2 - poetry run poe test_ci_gateway_v1 + poetry run poe test_ci_v2 + poetry run poe test_ci_v1 - name: Generate coverage in XML run: | @@ -288,8 +286,7 @@ jobs: - name: Run tests run: | - poetry run poe test_ci_on_networks_full_node - poetry run poe test_ci_on_networks_gateway + poetry run poe test_ci_on_networks - name: Generate coverage in XML run: | @@ -373,10 +370,8 @@ jobs: - name: Run tests run: | - poetry run poe test_ci_full_node_v2 - poetry run poe test_ci_full_node_v1 - poetry run poe test_ci_gateway_v2 - poetry run poe test_ci_gateway_v1 + poetry run poe test_ci_v2 + poetry run poe test_ci_v1 - name: Generate coverage in XML run: | @@ -438,10 +433,8 @@ jobs: - name: Run tests run: | - poetry run poe test_ci_docs_full_node_v2 - poetry run poe test_ci_docs_full_node_v1 - poetry run poe test_ci_docs_gateway_v2 - poetry run poe test_ci_docs_gateway_v1 + poetry run poe test_ci_docs_v2 + poetry run poe test_ci_docs_v1 - name: Generate coverage in XML run: | @@ -521,10 +514,8 @@ jobs: - name: Run tests run: | - poetry run poe test_ci_docs_full_node_v2 - poetry run poe test_ci_docs_full_node_v1 - poetry run poe test_ci_docs_gateway_v2 - poetry run poe test_ci_docs_gateway_v1 + poetry run poe test_ci_docs_v2 + poetry run poe test_ci_docs_v1 - name: Generate coverage in XML run: | diff --git a/README.md b/README.md index 23d467167..62b7e0ad3 100644 --- a/README.md +++ b/README.md @@ -37,11 +37,11 @@ This is the recommended way of using the SDK. ```python from starknet_py.contract import Contract -from starknet_py.net.gateway_client import GatewayClient +from starknet_py.net.full_node_client import FullNodeClient contract = await Contract.from_address( address="0x06689f1bf69af5b8e94e5ab9778c885b37c593d1156234eb423967621f596e73", - client=GatewayClient("testnet"), + client=FullNodeClient(node_url="https://your.node.url"), ) (value,) = await contract.functions["get_balance"].call() ``` @@ -52,11 +52,11 @@ You can access synchronous world with `_sync` postfix. ```python from starknet_py.contract import Contract -from starknet_py.net.gateway_client import GatewayClient +from starknet_py.net.full_node_client import FullNodeClient contract = Contract.from_address_sync( address="0x06689f1bf69af5b8e94e5ab9778c885b37c593d1156234eb423967621f596e73", - client=GatewayClient("testnet"), + client=FullNodeClient(node_url="https://your.node.url"), ) (value,) = contract.functions["get_balance"].call_sync() ``` diff --git a/docs/api.rst b/docs/api.rst index 37c7967c8..bce6ff682 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -3,7 +3,6 @@ API .. toctree:: api/client - api/gateway_client api/full_node_client api/account api/client_models diff --git a/docs/api/client.rst b/docs/api/client.rst index dbe751a99..d60a9e6ba 100644 --- a/docs/api/client.rst +++ b/docs/api/client.rst @@ -2,7 +2,7 @@ Client ====== Base class for clients interacting with Starknet. -Implemented by :ref:`GatewayClient` and :ref:`FullNodeClient`. +Implemented by :ref:`FullNodeClient`. .. py:module:: starknet_py.net.client diff --git a/docs/api/gateway_client.rst b/docs/api/gateway_client.rst deleted file mode 100644 index e26bf51f5..000000000 --- a/docs/api/gateway_client.rst +++ /dev/null @@ -1,8 +0,0 @@ -GatewayClient -============= - -.. py:module:: starknet_py.net.gateway_client - -.. autoclass-with-examples:: GatewayClient - :members: - :member-order: groupwise diff --git a/docs/guide/account_and_client.rst b/docs/guide/account_and_client.rst index c23d135b4..bef9c15a8 100644 --- a/docs/guide/account_and_client.rst +++ b/docs/guide/account_and_client.rst @@ -36,7 +36,7 @@ Note that the nonce will be bumped only by 1. .. warning:: - Do not pass arbitrarily large number of calls in one batch. Starknet rejects the transaction when it happens, like `here `_. + Do not pass arbitrarily large number of calls in one batch. Starknet rejects the transaction when it happens. @@ -49,9 +49,6 @@ like `Pathfinder `_, or `starknet-devnet `_. Using own full node allows for querying Starknet with better performance. -Since GatewayClient is deprecated and will be removed at some point in the future, having ``FullNodeClient`` -with interface uniform with that of ``GatewayClient`` will allow for simple migration for starknet.py users. - .. codesnippet:: ../../starknet_py/tests/e2e/docs/guide/test_full_node_client.py :language: python :dedent: 4 @@ -70,9 +67,9 @@ Custom nonce logic ------------------ By default, :ref:`Account` calls Starknet for nonce every time a new transaction is signed or executed. -This is okay for most users, but in case your applications needs to pre-sign multiple transactions +This is okay for most users, but in case your application needs to pre-sign multiple transactions for execution, deals with high amount of transactions or just needs to support different nonce -logic, it is possible to so with :ref:`Account`. Simply overwrite the +logic, it is possible to do so with :ref:`Account`. Simply overwrite the :meth:`~starknet_py.net.account.account.Account.get_nonce` method with your own logic. .. codesnippet:: ../../starknet_py/tests/e2e/docs/guide/test_custom_nonce.py diff --git a/docs/guide/deploying_contracts.rst b/docs/guide/deploying_contracts.rst index b84301bca..21336e48c 100644 --- a/docs/guide/deploying_contracts.rst +++ b/docs/guide/deploying_contracts.rst @@ -78,7 +78,7 @@ Declaring Cairo1 contracts | Starknet 0.11 introduced the ability to declare contracts written in Cairo1! To declare a new contract, Declare v2 transaction has to be sent. -You can see the structure of the new Declare transaction `here `_. +You can see the structure of the new Declare transaction `here `_. The main differences in the structure of the transaction from its previous version are: - ``contract_class`` field is a ``SierraContractClass`` diff --git a/docs/guide/serialization.rst b/docs/guide/serialization.rst index aeaac4cdb..6d292ba70 100644 --- a/docs/guide/serialization.rst +++ b/docs/guide/serialization.rst @@ -8,7 +8,7 @@ starknet.py **serializes** python values to Cairo values and **deserializes** Ca .. attention:: Serializing short strings to felts has been deprecated. Please use `starknet_py.cairo.felt.encode_shortstring` to - create numeric value from string _before_ passing value to serializer. + create numeric value from string **before** passing value to serializer. .. list-table:: Serialization from python to Cairo :widths: 25 25 25 25 @@ -61,7 +61,7 @@ starknet.py **serializes** python values to Cairo values and **deserializes** Ca - dict with keys matching struct * - pointer/array - list - * - unt256 + * - uint256 - int Working with shortstrings diff --git a/docs/migration_guide.rst b/docs/migration_guide.rst index f1638fe58..a56b1d867 100644 --- a/docs/migration_guide.rst +++ b/docs/migration_guide.rst @@ -1,11 +1,29 @@ Migration guide =============== +********************** +0.19.0 Migration guide +********************** + +0.19.0 Breaking changes +----------------------- + +.. currentmodule:: starknet_py.net.client_models + +1. :class:`GatewayClient` all related classes and fields have been removed. +2. Client ``net`` property has been removed. +3. :class:`TransactionReceipt` field ``execution_resources`` has been changed from ``dict`` to :class:`ExecutionResources`. + +0.19.0 Minor changes +-------------------- + +1. :class:`L1HandlerTransaction` field ``nonce`` is now required. + ********************** 0.18.3 Migration guide ********************** -Version 0.18.3 of **starknet.py** comes with support for RPC 0.5.0! +Version 0.18.3 of **starknet.py** comes with support for RPC 0.5.1! 0.18.3 Targeted versions @@ -960,7 +978,7 @@ Clients Client has been separated into two specialized modules. -* Use :ref:`GatewayClient` to interact with Starknet like you did in previous starknet.py versions +* Use ``GatewayClient`` to interact with Starknet like you did in previous starknet.py versions * Use :ref:`FullNodeClient` to interact with JSON-RPC .. note:: @@ -972,7 +990,7 @@ API Changes ----------- Client methods has had some of the parameters removed, so it provided uniform interface -for both gateway and rpc methods. Please refer to :ref:`GatewayClient` and :ref:`FullNodeClient` +for both gateway and rpc methods. Please refer to ``GatewayClient`` and :ref:`FullNodeClient` to see what has changed. There is no longer add_transaction method in the Client interface. It was renamed to send_transaction. diff --git a/docs/quickstart.rst b/docs/quickstart.rst index b4a2b77cd..6f9d4e96f 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -22,31 +22,6 @@ synchronous version. It might be helpful to play with Starknet directly in pytho You can check out all of the FullNodeClient's methods here: :ref:`FullNodeClient`. -Using GatewayClient -------------------- - -.. warning:: - - Gateway / Feeder Gateway API will become deprecated in the future. As a result, GatewayClient won't work and will - eventually be removed. Consider migrating to :ref:`FullNodeClient`. - -:ref:`GatewayClient` will make requests directly to Starknet sequencer through `gateway` or `feeder_gateway` endpoints. -It can be used to either query the blockchain state or add new transactions. -It requires information about used network: - -.. codesnippet:: ../starknet_py/tests/e2e/docs/quickstart/test_using_gateway_client.py - :language: python - :dedent: 4 - -It also has async/sync interface: - -.. codesnippet:: ../starknet_py/tests/e2e/docs/quickstart/test_synchronous_gateway_client.py - :language: python - :dedent: 4 - -You can check out all of the GatewayClient's methods here: :ref:`GatewayClient`. - - Creating Account ---------------------- diff --git a/pyproject.toml b/pyproject.toml index 2487d8b97..d39048a4f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -56,31 +56,23 @@ starknet-devnet = {version = "0.6.2", python = ">=3.9, <3.10"} [tool.poe.tasks] test = [ "clean_coverage", - "test_ci_gateway_v1 --disable-warnings -qq", - "test_ci_full_node_v1 --disable-warnings -qq", - "test_ci_gateway_v2 --disable-warnings -qq", - "test_ci_full_node_v2 --disable-warnings -qq", - "test_ci_on_networks_gateway --disable-warnings -qq", - "test_ci_on_networks_full_node --disable-warnings -qq", + "test_ci_v1 --disable-warnings -qq", + "test_ci_v2 --disable-warnings -qq", + "test_ci_on_networks --disable-warnings -qq", + "test_ci_docs_v1 --disable-warnings -qq", + "test_ci_docs_v2 --disable-warnings -qq", "test_report --skip-covered" ] -test_ci = ["test_ci_gateway_v1", "test_ci_full_node_v1", "test_ci_gateway_v2", "test_ci_full_node_v2"] -test_ci_gateway_v1 = "coverage run -a -m pytest --client=gateway --contract_dir=v1 starknet_py --ignore=starknet_py/tests/e2e/docs --ignore=starknet_py/tests/e2e/core --ignore=starknet_py/tests/e2e/tests_on_networks" -test_ci_full_node_v1 = "coverage run -a -m pytest --client=full_node --contract_dir=v1 starknet_py --ignore=starknet_py/tests/e2e/docs --ignore=starknet_py/tests/e2e/core --ignore=starknet_py/tests/e2e/tests_on_networks" -test_ci_gateway_v2 = "coverage run -a -m pytest --client=gateway --contract_dir=v2 starknet_py --ignore=starknet_py/tests/e2e/docs --ignore=starknet_py/tests/e2e/core --ignore=starknet_py/tests/e2e/tests_on_networks" -test_ci_full_node_v2 = "coverage run -a -m pytest --client=full_node --contract_dir=v2 starknet_py --ignore=starknet_py/tests/e2e/docs --ignore=starknet_py/tests/e2e/core --ignore=starknet_py/tests/e2e/tests_on_networks" - -# order of tests below is important, explanation in /tests_on_networks/client_test.py above 'test_wait_for_tx_reverted_full_node' -test_ci_on_networks = ["test_ci_on_networks_full_node", "test_ci_on_networks_gateway"] -test_ci_on_networks_gateway = "coverage run -a -m pytest --client=gateway starknet_py/tests/e2e/tests_on_networks" -test_ci_on_networks_full_node = "coverage run -a -m pytest --client=full_node starknet_py/tests/e2e/tests_on_networks" - -test_ci_docs = ["test_ci_docs_gateway_v1", "test_ci_docs_full_node_v1", "test_ci_docs_gateway_v2", "test_ci_docs_full_node_v2"] -test_ci_docs_gateway_v1 = "coverage run -a -m pytest --client=gateway --contract_dir=v1 starknet_py/tests/e2e/docs" -test_ci_docs_full_node_v1 = "coverage run -a -m pytest --client=full_node --contract_dir=v1 starknet_py/tests/e2e/docs" -test_ci_docs_gateway_v2 = "coverage run -a -m pytest --client=gateway --contract_dir=v2 starknet_py/tests/e2e/docs" -test_ci_docs_full_node_v2 = "coverage run -a -m pytest --client=full_node --contract_dir=v2 starknet_py/tests/e2e/docs" +test_ci = ["test_ci_v1", "test_ci_v2"] +test_ci_v1 = "coverage run -a -m pytest --contract_dir=v1 starknet_py --ignore=starknet_py/tests/e2e/docs --ignore=starknet_py/tests/e2e/tests_on_networks" +test_ci_v2 = "coverage run -a -m pytest --contract_dir=v2 starknet_py --ignore=starknet_py/tests/e2e/docs --ignore=starknet_py/tests/e2e/tests_on_networks" + +test_ci_on_networks = "coverage run -a -m pytest starknet_py/tests/e2e/tests_on_networks" + +test_ci_docs = ["test_ci_docs_v1", "test_ci_docs_v2"] +test_ci_docs_v1 = "coverage run -a -m pytest --contract_dir=v1 starknet_py/tests/e2e/docs" +test_ci_docs_v2 = "coverage run -a -m pytest --contract_dir=v2 starknet_py/tests/e2e/docs" test_report = "coverage report -m" test_html.shell = "coverage html && open ./htmlcov/index.html" diff --git a/starknet_py/conftest.py b/starknet_py/conftest.py index 107e392a8..396dad738 100644 --- a/starknet_py/conftest.py +++ b/starknet_py/conftest.py @@ -11,6 +11,5 @@ "starknet_py.tests.e2e.fixtures.proxy", "starknet_py.tests.e2e.client.fixtures.transactions", "starknet_py.tests.e2e.client.fixtures.prepare_network", - "starknet_py.tests.e2e.core.fixtures", "starknet_py.tests.e2e.tests_on_networks.fixtures", ] diff --git a/starknet_py/net/account/account.py b/starknet_py/net/account/account.py index 05b2310dd..e1319cb43 100644 --- a/starknet_py/net/account/account.py +++ b/starknet_py/net/account/account.py @@ -22,7 +22,6 @@ Tag, ) from starknet_py.net.full_node_client import FullNodeClient -from starknet_py.net.gateway_client import GatewayClient from starknet_py.net.models import AddressRepresentation, StarknetChainId, parse_address from starknet_py.net.models.transaction import ( AccountTransaction, @@ -103,15 +102,10 @@ def address(self) -> int: @property async def cairo_version(self) -> int: if self._cairo_version is None: - if isinstance(self._client, GatewayClient): - contract_class = await self._client.get_full_contract( - contract_address=self._address - ) - else: - assert isinstance(self._client, FullNodeClient) - contract_class = await self._client.get_class_at( - contract_address=self._address - ) + assert isinstance(self._client, FullNodeClient) + contract_class = await self._client.get_class_at( + contract_address=self._address + ) self._cairo_version = ( 1 if isinstance(contract_class, SierraContractClass) else 0 ) diff --git a/starknet_py/net/account/account_test.py b/starknet_py/net/account/account_test.py index 468e35d9d..1ee682728 100644 --- a/starknet_py/net/account/account_test.py +++ b/starknet_py/net/account/account_test.py @@ -5,7 +5,6 @@ from starknet_py.constants import FEE_CONTRACT_ADDRESS from starknet_py.net.account.account import Account from starknet_py.net.full_node_client import FullNodeClient -from starknet_py.net.gateway_client import GatewayClient from starknet_py.net.models import StarknetChainId, parse_address from starknet_py.net.networks import MAINNET, TESTNET from starknet_py.net.signer.stark_curve_signer import KeyPair, StarkCurveSigner @@ -14,19 +13,8 @@ @pytest.mark.asyncio @pytest.mark.parametrize("net", (TESTNET, MAINNET)) -@pytest.mark.parametrize( - "call_contract", - [ - "starknet_py.net.gateway_client.GatewayClient.call_contract", - "starknet_py.net.full_node_client.FullNodeClient.call_contract", - ], -) -async def test_get_balance_default_token_address(net, call_contract): - client = ( - GatewayClient(net=net) - if "gateway" in call_contract - else FullNodeClient(node_url=net + "/rpc") - ) +async def test_get_balance_default_token_address(net): + client = FullNodeClient(node_url=net + "/rpc") acc_client = Account( client=client, address="0x123", @@ -35,7 +23,7 @@ async def test_get_balance_default_token_address(net, call_contract): ) with patch( - call_contract, + f"{FullNodeClient.__module__}.FullNodeClient.call_contract", AsyncMock(), ) as mocked_call_contract: mocked_call_contract.return_value = [0, 0] @@ -68,7 +56,7 @@ def test_create_account(): key_pair = KeyPair.from_private_key(0x111) account = Account( address=0x1, - client=GatewayClient(net=TESTNET), + client=FullNodeClient(node_url=""), key_pair=key_pair, chain=StarknetChainId.TESTNET, ) @@ -83,7 +71,7 @@ def test_create_account_from_signer(): key_pair=KeyPair.from_private_key(0x111), chain_id=StarknetChainId.TESTNET, ) - account = Account(address=0x1, client=GatewayClient(net=TESTNET), signer=signer) + account = Account(address=0x1, client=FullNodeClient(node_url=""), signer=signer) assert account.address == 0x1 assert account.signer == signer @@ -93,7 +81,7 @@ def test_create_account_raises_on_no_chain_and_signer(): with pytest.raises(ValueError, match="One of chain or signer must be provided"): Account( address=0x1, - client=GatewayClient(net=TESTNET), + client=FullNodeClient(node_url=""), key_pair=KeyPair.from_private_key(0x111), ) @@ -105,7 +93,7 @@ def test_create_account_raises_on_no_keypair_and_signer(): ): Account( address=0x1, - client=GatewayClient(net=TESTNET), + client=FullNodeClient(node_url=""), chain=StarknetChainId.TESTNET, ) @@ -116,7 +104,7 @@ def test_create_account_raises_on_both_keypair_and_signer(): ): Account( address=0x1, - client=GatewayClient(net=TESTNET), + client=FullNodeClient(node_url=""), chain=StarknetChainId.TESTNET, key_pair=KeyPair.from_private_key(0x111), signer=StarkCurveSigner( diff --git a/starknet_py/net/client.py b/starknet_py/net/client.py index 77949c48d..7ac6f48b5 100644 --- a/starknet_py/net/client.py +++ b/starknet_py/net/client.py @@ -9,7 +9,6 @@ from starknet_py.net.client_models import ( BlockStateUpdate, BlockTransactionTrace, - BlockTransactionTraces, Call, ContractClass, DeclareTransactionResponse, @@ -33,7 +32,6 @@ DeployAccount, Invoke, ) -from starknet_py.net.networks import Network from starknet_py.transaction_errors import ( TransactionNotReceivedError, TransactionRejectedError, @@ -44,16 +42,6 @@ @add_sync_methods class Client(ABC): - @property - @abstractmethod - def net(self) -> Network: - """ - Network of the client. - - .. deprecated:: 0.15.0 - Property net of the Client interface is deprecated. - """ - @abstractmethod async def get_block( self, @@ -73,7 +61,7 @@ async def trace_block_transactions( self, block_hash: Optional[Union[Hash, Tag]] = None, block_number: Optional[Union[int, Tag]] = None, - ) -> Union[BlockTransactionTraces, List[BlockTransactionTrace]]: + ) -> List[BlockTransactionTrace]: """ Receive the traces of all the transactions within specified block @@ -179,10 +167,7 @@ async def wait_for_tx( raise TransactionRejectedError(message=tx_receipt.rejection_reason) if execution_status == TransactionExecutionStatus.REVERTED: - # TODO (#1047): message should be always revert_reason once GatewayClient is deprecated - raise TransactionRevertedError( - message=(tx_receipt.revert_reason or tx_receipt.revert_error) - ) + raise TransactionRevertedError(message=tx_receipt.revert_reason) if execution_status == TransactionExecutionStatus.SUCCEEDED: return tx_receipt diff --git a/starknet_py/net/client_models.py b/starknet_py/net/client_models.py index 518d36446..9315623cc 100644 --- a/starknet_py/net/client_models.py +++ b/starknet_py/net/client_models.py @@ -6,7 +6,7 @@ from abc import ABC from dataclasses import dataclass, field from enum import Enum -from typing import Any, Dict, Iterable, List, Optional, Union +from typing import Any, Iterable, List, Optional, Union from typing_extensions import Literal @@ -51,19 +51,6 @@ class EventsChunk: continuation_token: Optional[str] = None -@dataclass -class L1toL2Message: - """ - Dataclass representing a L1->L2 message. - """ - - payload: List[int] - nonce: int - selector: int - l1_address: int - l2_address: int - - @dataclass class L2toL1Message: """ @@ -158,7 +145,6 @@ class DeployTransaction(Transaction): Dataclass representing deploy transaction. """ - contract_address: Optional[int] # Gateway-only field, hence Optional contract_address_salt: int constructor_calldata: List[int] class_hash: int @@ -188,7 +174,7 @@ class L1HandlerTransaction(Transaction): contract_address: int calldata: List[int] entry_point_selector: int - nonce: Optional[int] = None + nonce: int class TransactionStatus(Enum): @@ -226,7 +212,26 @@ class TransactionFinalityStatus(Enum): ACCEPTED_ON_L1 = "ACCEPTED_ON_L1" -# TODO (#1047): split into PendingTransactionReceipt and TransactionReceipt? +@dataclass +class ExecutionResources: + """ + Dataclass representing the resources consumed by the transaction. + """ + + # pylint: disable=too-many-instance-attributes + + steps: int + range_check_builtin_applications: int + pedersen_builtin_applications: int + poseidon_builtin_applications: int + ec_op_builtin_applications: int + ecdsa_builtin_applications: int + bitwise_builtin_applications: int + keccak_builtin_applications: int + memory_holes: Optional[int] = None + + +# TODO (#1179): split into PendingTransactionReceipt and TransactionReceipt @dataclass class TransactionReceipt: """ @@ -237,12 +242,11 @@ class TransactionReceipt: transaction_hash: int events: List[Event] = field(default_factory=list) - l2_to_l1_messages: List[L2toL1Message] = field(default_factory=list) + messages_sent: List[L2toL1Message] = field(default_factory=list) - execution_status: Optional[ - TransactionExecutionStatus - ] = None # gateway/pending receipt field + execution_status: Optional[TransactionExecutionStatus] = None finality_status: Optional[TransactionFinalityStatus] = None + # TODO (#1179): remove status field once devnet is updated status: Optional[ TransactionStatus ] = None # replaced by execution and finality status in RPC v0.4.0-rc1 @@ -253,20 +257,13 @@ class TransactionReceipt: block_number: Optional[int] = None block_hash: Optional[int] = None actual_fee: int = 0 - # TODO (#1047): change that into ExecutionResources class after gateway removal - # (values of course differ for each client) # TODO (#1179): this field should be required - execution_resources: Optional[dict] = field(default_factory=dict) + execution_resources: Optional[ExecutionResources] = None message_hash: Optional[int] = None # L1_HANDLER_TXN_RECEIPT-only rejection_reason: Optional[str] = None - revert_reason: Optional[str] = None # full_node-only field - revert_error: Optional[str] = None # gateway-only field - - # gateway only - l1_to_l2_consumed_message: Optional[L1toL2Message] = None - transaction_index: Optional[int] = None + revert_reason: Optional[str] = None @dataclass @@ -347,7 +344,7 @@ class StarknetBlockCommon: Dataclass representing a block header. """ - # TODO (#1047): change that into composition (with all the breaking changes it will be a minor thing there) + # TODO (#1179): change that into composition # pylint: disable=too-many-instance-attributes block_hash: int @@ -382,44 +379,6 @@ class StarknetBlockWithTxHashes(StarknetBlockCommon): transactions: List[int] -@dataclass -class GatewayBlockTransactionReceipt: - # pylint: disable=too-many-instance-attributes - transaction_index: int - transaction_hash: int - l2_to_l1_messages: List[L2toL1Message] - events: List[Event] - actual_fee: int - execution_status: Optional[TransactionExecutionStatus] = None - finality_status: Optional[TransactionFinalityStatus] = None - execution_resources: Optional[dict] = None - l1_to_l2_consumed_message: Optional[L1toL2Message] = None - revert_error: Optional[str] = None - - -@dataclass -class GatewayBlock: - """ - Dataclass representing a block from the Starknet gateway. - """ - - # pylint: disable=too-many-instance-attributes - gas_price: int - status: BlockStatus - transactions: List[Transaction] - transaction_receipts: List[GatewayBlockTransactionReceipt] - - timestamp: int - parent_block_hash: int - - root: Optional[int] = None - block_number: Optional[int] = None - block_hash: Optional[int] = None - - sequencer_address: Optional[int] = None - starknet_version: Optional[str] = None - - @dataclass class BlockHashAndNumber: block_hash: int @@ -436,31 +395,6 @@ class SyncStatus: highest_block_num: int -@dataclass -class BlockSingleTransactionTrace: - """ - Dataclass representing a trace of transaction execution. - """ - - signature: List[int] - transaction_hash: int - function_invocation: Optional[dict] = None - validate_invocation: Optional[dict] = None - fee_transfer_invocation: Optional[dict] = None - constructor_invocation: Optional[dict] = None - # Gateway-only field, information about reversion in RPC spec is returned inside "execute_invocation" - revert_error: Optional[str] = None - - -@dataclass -class BlockTransactionTraces: - """ - Dataclass representing traces of all transactions in block. - """ - - traces: List[BlockSingleTransactionTrace] - - @dataclass class StorageEntry: """ @@ -546,16 +480,6 @@ class StateDiff: nonces: List[ContractsNonce] -@dataclass -class GatewayStateDiff: - storage_diffs: List[StorageDiffItem] - deployed_contracts: List[DeployedContract] - declared_contract_hashes: List[DeclaredContractHash] - nonces: List[ContractsNonce] - deprecated_declared_contract_hashes: List[int] = field(default_factory=list) - replaced_classes: List[ReplacedClass] = field(default_factory=list) - - @dataclass class BlockStateUpdate: """ @@ -565,7 +489,7 @@ class BlockStateUpdate: block_hash: int new_root: int old_root: int - state_diff: Union[StateDiff, GatewayStateDiff] + state_diff: StateDiff @dataclass @@ -578,26 +502,6 @@ class PendingBlockStateUpdate: state_diff: StateDiff -@dataclass -class StateUpdateWithBlock: - """ - Dataclass representing a change in state of a block with the block. - """ - - block: GatewayBlock - state_update: BlockStateUpdate - - -@dataclass -class ContractCode: - """ - Dataclass representing contract deployed to Starknet. - """ - - bytecode: List[int] - abi: List[Dict[str, Any]] - - @dataclass class EntryPoint: """ @@ -720,18 +624,6 @@ class CasmClass: entry_points_by_type: CasmClassEntryPointsByType -@dataclass -class GatewayTransactionStatusResponse: - """ - Dataclass representing transaction status for the GatewayClient. - """ - - block_hash: Optional[int] - transaction_status: TransactionStatus - finality_status: Optional[TransactionFinalityStatus] = None - execution_status: Optional[TransactionExecutionStatus] = None - - @dataclass class TransactionStatusResponse: """ @@ -742,56 +634,6 @@ class TransactionStatusResponse: execution_status: Optional[TransactionExecutionStatus] = None -@dataclass -class SignatureInput: - """ - Dataclass representing a signature input. - """ - - block_hash: int - state_diff_commitment: int - - -@dataclass -class SignatureOnStateDiff: - """ - Dataclass representing signature on state diff commitment and block hash. - """ - - block_number: int - signature: List[int] - signature_input: SignatureInput - - -class DAMode(Enum): - """ - Enum specifying a storage domain in Starknet. Each domain has different gurantess regarding availability. - """ - - L1 = "L1" - L2 = "L2" - - -@dataclass -class ExecutionResources: - """ - Dataclass representing the resources consumed by the transaction. - """ - - # pylint: disable=too-many-instance-attributes - - # For now the class and schema related to it is unused, it is waiting here for refactoring. - steps: int - range_check_builtin_applications: int - pedersen_builtin_applications: int - poseidon_builtin_applications: int - ec_op_builtin_applications: int - ecdsa_builtin_applications: int - bitwise_builtin_applications: int - keccak_builtin_applications: int - memory_holes: Optional[int] = None - - # ------------------------------- Trace API dataclasses ------------------------------- diff --git a/starknet_py/net/client_utils.py b/starknet_py/net/client_utils.py index b2d751e6c..320e827df 100644 --- a/starknet_py/net/client_utils.py +++ b/starknet_py/net/client_utils.py @@ -21,9 +21,6 @@ def is_block_identifier(value: Union[int, Hash, Tag]) -> bool: def encode_l1_message(tx: L1HandlerTransaction) -> bytes: - # TODO (#1047): remove this assert once GatewayClient is deprecated and nonce is always required - assert tx.nonce is not None - from_address = tx.calldata[0] # Pop first element to have in calldata the actual payload tx.calldata.pop(0) diff --git a/starknet_py/net/full_node_client.py b/starknet_py/net/full_node_client.py index 7793140fe..8f34692d9 100644 --- a/starknet_py/net/full_node_client.py +++ b/starknet_py/net/full_node_client.py @@ -1,5 +1,4 @@ import re -import warnings from typing import Dict, List, Optional, Tuple, Union, cast import aiohttp @@ -49,7 +48,6 @@ DeployAccount, Invoke, ) -from starknet_py.net.networks import Network from starknet_py.net.schemas.rpc import ( BlockHashAndNumberSchema, BlockStateUpdateSchema, @@ -83,7 +81,6 @@ class FullNodeClient(Client): def __init__( self, node_url: str, - net: Optional[Network] = None, session: Optional[aiohttp.ClientSession] = None, ): """ @@ -97,18 +94,6 @@ def __init__( self.url = node_url self._client = RpcHttpClient(url=node_url, session=session) - if net is not None: - warnings.warn("Parameter net is deprecated.", category=DeprecationWarning) - self._net = net - - @property - def net(self) -> Optional[Network]: - warnings.warn( - "Property net is deprecated in the FullNodeClient.", - category=DeprecationWarning, - ) - return self._net - async def get_block( self, block_hash: Optional[Union[Hash, Tag]] = None, diff --git a/starknet_py/net/gateway_client.py b/starknet_py/net/gateway_client.py deleted file mode 100644 index d0b626699..000000000 --- a/starknet_py/net/gateway_client.py +++ /dev/null @@ -1,561 +0,0 @@ -import warnings -from typing import Dict, List, Optional, Union, cast - -import aiohttp -from marshmallow import EXCLUDE - -from starknet_py.net.client import Client -from starknet_py.net.client_errors import ContractNotFoundError -from starknet_py.net.client_models import ( - BlockStateUpdate, - BlockTransactionTraces, - Call, - CasmClass, - ContractClass, - ContractCode, - DeclareTransactionResponse, - DeployAccountTransactionResponse, - EstimatedFee, - GatewayBlock, - GatewayTransactionStatusResponse, - Hash, - SentTransactionResponse, - SierraContractClass, - SignatureOnStateDiff, - StateUpdateWithBlock, - Tag, - Transaction, - TransactionReceipt, -) -from starknet_py.net.client_utils import hash_to_felt, is_block_identifier -from starknet_py.net.http_client import GatewayHttpClient -from starknet_py.net.models.transaction import ( - AccountTransaction, - Declare, - DeclareSchema, - DeclareV2, - DeclareV2Schema, - DeployAccount, - DeployAccountSchema, - Invoke, - InvokeSchema, - compress_program, -) -from starknet_py.net.networks import Network, net_address_from_net -from starknet_py.net.schemas.gateway import ( - BlockStateUpdateSchema, - BlockTransactionTracesSchema, - CasmClassSchema, - ContractCodeSchema, - DeclareTransactionResponseSchema, - DeployAccountTransactionResponseSchema, - EstimatedFeeSchema, - SentTransactionSchema, - SignatureOnStateDiffSchema, - StarknetBlockSchema, - StateUpdateWithBlockSchema, - TransactionReceiptSchema, - TransactionStatusSchema, - TypesOfContractClassSchema, - TypesOfTransactionsSchema, -) -from starknet_py.transaction_errors import TransactionNotReceivedError -from starknet_py.utils.sync import add_sync_methods - - -@add_sync_methods -class GatewayClient(Client): - # pylint: disable=too-many-public-methods - def __init__( - self, - net: Network, - session: Optional[aiohttp.ClientSession] = None, - ): - """ - .. deprecated:: 0.17.0 - Gateway / Feeder Gateway API will become deprecated in the future. As a result, GatewayClient won't work - and will eventually be removed. Consider migrating to FullNodeClient. - - - Client for interacting with Starknet gateway. - - :param net: Target network for the client. Can be a string with URL, one of ``"mainnet"``, ``"testnet"`` - or dict with ``"feeder_gateway_url"`` and ``"gateway_url"`` fields - :param session: Aiohttp session to be used for request. If not provided, client will create a session for - every request. When using a custom session, user is responsible for closing it manually. - """ - warnings.warn( - "Gateway / Feeder Gateway API will become deprecated in the future. As a result, GatewayClient won't work " - "and will eventually be removed. Consider migrating to FullNodeClient.", - PendingDeprecationWarning, - ) - - if isinstance(net, str): - host = net_address_from_net(net) - feeder_gateway_url = f"{host}/feeder_gateway" - gateway_url = f"{host}/gateway" - else: - feeder_gateway_url = net["feeder_gateway_url"] - gateway_url = net["gateway_url"] - - self._net = net - - self._feeder_gateway_client = GatewayHttpClient( - url=feeder_gateway_url, session=session - ) - self._gateway_client = GatewayHttpClient(url=gateway_url, session=session) - - @property - def net(self) -> Network: - warnings.warn( - "Property net is deprecated in the GatewayClient.", - category=DeprecationWarning, - ) - return self._net - - async def get_block( - self, - block_hash: Optional[Union[Hash, Tag]] = None, - block_number: Optional[Union[int, Tag]] = None, - ) -> GatewayBlock: - block_identifier = get_block_identifier( - block_hash=block_hash, block_number=block_number - ) - - res = await self._feeder_gateway_client.call( - method_name="get_block", params=block_identifier - ) - return StarknetBlockSchema().load(res, unknown=EXCLUDE) # pyright: ignore - - async def trace_block_transactions( - self, - block_hash: Optional[Union[Hash, Tag]] = None, - block_number: Optional[Union[int, Tag]] = None, - ) -> BlockTransactionTraces: - block_identifier = get_block_identifier( - block_hash=block_hash, block_number=block_number - ) - - res = await self._feeder_gateway_client.call( - method_name="get_block_traces", params=block_identifier - ) - return BlockTransactionTracesSchema().load( - res, unknown=EXCLUDE - ) # pyright: ignore - - async def get_state_update( - self, - block_hash: Optional[Union[Hash, Tag]] = None, - block_number: Optional[Union[int, Tag]] = None, - include_block: bool = False, - ) -> Union[BlockStateUpdate, StateUpdateWithBlock]: - """ - Get the information about the result of executing the requested block. - - :param block_hash: Block's hash. - :param block_number: Block's number (default "pending"). - :param include_block: Flag deciding whether to include the queried block. Defaults to false. - :return: BlockStateUpdate object representing changes in the requested block. - """ - block_identifier = get_block_identifier( - block_hash=block_hash, block_number=block_number - ) - params = { - **block_identifier, - "includeBlock": str(include_block).lower(), - } - res = await self._feeder_gateway_client.call( - method_name="get_state_update", params=params - ) - - if include_block: - return StateUpdateWithBlockSchema().load( - res, unknown=EXCLUDE - ) # pyright: ignore - return BlockStateUpdateSchema().load(res, unknown=EXCLUDE) # pyright: ignore - - async def get_storage_at( - self, - contract_address: Hash, - key: int, - block_hash: Optional[Union[Hash, Tag]] = None, - block_number: Optional[Union[int, Tag]] = None, - ) -> int: - """ - :param contract_address: Contract's address on Starknet - :param key: An address of the storage variable inside the contract. - :param block_hash: Fetches the value of the variable at given block hash - :param block_number: See above, uses block number instead of hash (default "pending") - :return: Storage value of given contract - """ - block_identifier = get_block_identifier( - block_hash=block_hash, block_number=block_number - ) - - res = await self._feeder_gateway_client.call( - method_name="get_storage_at", - params={ - **{ - "contractAddress": hash_to_felt(contract_address), - "key": key, - }, - **block_identifier, - }, - ) - res = cast(str, res) - return int(res, 16) - - async def get_transaction( - self, - tx_hash: Hash, - ) -> Transaction: - res = await self._feeder_gateway_client.call( - method_name="get_transaction", - params={"transactionHash": hash_to_felt(tx_hash)}, - ) - - if res["status"] in ("UNKNOWN", "NOT_RECEIVED"): - raise TransactionNotReceivedError() - - return cast( - Transaction, - TypesOfTransactionsSchema().load(res["transaction"], unknown=EXCLUDE), - ) - - async def get_transaction_receipt(self, tx_hash: Hash) -> TransactionReceipt: - res = await self._feeder_gateway_client.call( - method_name="get_transaction_receipt", - params={"transactionHash": hash_to_felt(tx_hash)}, - ) - - return TransactionReceiptSchema().load(res, unknown=EXCLUDE) # pyright: ignore - - async def estimate_fee( - self, - tx: Union[AccountTransaction, List[AccountTransaction]], - block_hash: Optional[Union[Hash, Tag]] = None, - block_number: Optional[Union[int, Tag]] = None, - ) -> Union[EstimatedFee, List[EstimatedFee]]: - if isinstance(tx, list): - return await self.estimate_fee_bulk( - transactions=tx, block_hash=block_hash, block_number=block_number - ) - - block_identifier = get_block_identifier( - block_hash=block_hash, block_number=block_number - ) - res = await self._feeder_gateway_client.post( - method_name="estimate_fee", - payload=_get_payload(tx), - params=block_identifier, - ) - - return EstimatedFeeSchema().load(res, unknown=EXCLUDE) # pyright: ignore - - async def estimate_fee_bulk( - self, - transactions: List[AccountTransaction], - block_hash: Optional[Union[Hash, Tag]] = None, - block_number: Optional[Union[int, Tag]] = None, - ) -> List[EstimatedFee]: - """ - Estimate how much Wei it will cost to run provided transactions. - - :param transactions: List of transactions to estimate. - :param block_hash: Block's hash or literals `"pending"` or `"latest"`. - :param block_number: Block's number or literals `"pending"` or `"latest"`. - :return: List of estimated amount of Wei executing specified transaction will cost. - """ - - block_identifier = get_block_identifier( - block_hash=block_hash, block_number=block_number - ) - res = await self._feeder_gateway_client.post( - method_name="estimate_fee_bulk", - payload=_get_payload(transactions), - params=block_identifier, - ) - - return EstimatedFeeSchema().load( - res, unknown=EXCLUDE, many=True - ) # pyright: ignore - - async def call_contract( - self, - call: Call, - block_hash: Optional[Union[Hash, Tag]] = None, - block_number: Optional[Union[int, Tag]] = None, - ) -> List[int]: - block_identifier = get_block_identifier( - block_hash=block_hash, block_number=block_number - ) - - res = await self._feeder_gateway_client.post( - method_name="call_contract", - params=block_identifier, - payload={ - "contract_address": hex(call.to_addr), - "entry_point_selector": hex(call.selector), - "calldata": [str(i) for i in call.calldata], - }, - ) - - return [int(v, 16) for v in res["result"]] - - async def send_transaction( - self, - transaction: Invoke, - token: Optional[str] = None, - ) -> SentTransactionResponse: - res = await self._add_transaction(transaction, token) - return SentTransactionSchema().load(res, unknown=EXCLUDE) # pyright: ignore - - async def deploy_account( - self, transaction: DeployAccount, token: Optional[str] = None - ) -> DeployAccountTransactionResponse: - res = await self._add_transaction(transaction, token) - return DeployAccountTransactionResponseSchema().load( - res, unknown=EXCLUDE - ) # pyright: ignore - - async def declare( - self, - transaction: Union[Declare, DeclareV2], - token: Optional[str] = None, - ) -> DeclareTransactionResponse: - res = await self._add_transaction(transaction, token) - return DeclareTransactionResponseSchema().load( - res, unknown=EXCLUDE - ) # pyright: ignore - - async def get_class_hash_at( - self, - contract_address: Hash, - block_hash: Optional[Union[Hash, Tag]] = None, - block_number: Optional[Union[int, Tag]] = None, - ) -> int: - block_identifier = get_block_identifier( - block_hash=block_hash, block_number=block_number - ) - res = await self._feeder_gateway_client.call( - method_name="get_class_hash_at", - params={ - "contractAddress": hash_to_felt(contract_address), - **block_identifier, - }, - ) - res = cast(str, res) - return int(res, 16) - - async def get_class_by_hash( - self, class_hash: Hash - ) -> Union[ContractClass, SierraContractClass]: - res = await self._feeder_gateway_client.call( - method_name="get_class_by_hash", - params={"classHash": hash_to_felt(class_hash)}, - ) - return TypesOfContractClassSchema().load( - res, unknown=EXCLUDE - ) # pyright: ignore - - # Only gateway methods - - async def get_compiled_class_by_class_hash(self, class_hash: Hash) -> CasmClass: - """ - Fetches CasmClass of a contract with given class hash. - - :param class_hash: Class hash of the contract. - :return: CasmClass of the contract. - """ - res = await self._feeder_gateway_client.call( - params={"classHash": hash_to_felt(class_hash)}, - method_name="get_compiled_class_by_class_hash", - ) - return cast(CasmClass, CasmClassSchema().load(res)) - - async def _add_transaction( - self, - tx: AccountTransaction, - token: Optional[str] = None, - ) -> dict: - res = await self._gateway_client.post( - method_name="add_transaction", - payload=_get_payload(tx), - params={"token": token} if token is not None else {}, - ) - return res - - async def get_transaction_status( - self, - tx_hash: Hash, - ) -> GatewayTransactionStatusResponse: - """ - Fetches the transaction's status and block number - - :param tx_hash: Transaction's hash representation - :return: An object containing transaction's status and optional block hash, if transaction was accepted - """ - res = await self._feeder_gateway_client.call( - params={"transactionHash": hash_to_felt(tx_hash)}, - method_name="get_transaction_status", - ) - if res["tx_status"] in ("UNKNOWN", "NOT_RECEIVED"): - raise TransactionNotReceivedError() - - return TransactionStatusSchema().load(res) # pyright: ignore - - async def get_contract_addresses(self) -> dict: - """ - Fetches the addresses of the Starknet system contracts - - :return: A dictionary indexed with contract name and a value of contract's address - """ - return await self._feeder_gateway_client.call( - method_name="get_contract_addresses", - ) - - async def get_code( - self, - contract_address: Hash, - block_hash: Optional[Union[Hash, Tag]] = None, - block_number: Optional[Union[int, Tag]] = None, - ) -> ContractCode: - block_identifier = get_block_identifier( - block_hash=block_hash, block_number=block_number - ) - params = { - **{"contractAddress": hash_to_felt(contract_address)}, - **block_identifier, - } - - res = await self._feeder_gateway_client.call( - method_name="get_code", params=params - ) - - if len(res["bytecode"]) == 0: - raise ContractNotFoundError( - address=contract_address, - block_hash=block_hash, - block_number=block_number, - ) - - return ContractCodeSchema().load(res, unknown=EXCLUDE) # pyright: ignore - - async def get_contract_nonce( - self, - contract_address: Hash, - block_hash: Optional[Union[Hash, Tag]] = None, - block_number: Optional[Union[int, Tag]] = None, - ) -> int: - block_identifier = get_block_identifier( - block_hash=block_hash, block_number=block_number - ) - params = { - **{"contractAddress": hash_to_felt(contract_address)}, - **block_identifier, - } - - nonce = await self._feeder_gateway_client.call( - method_name="get_nonce", params=params - ) - nonce = cast(str, nonce) - return int(nonce, 16) - - async def get_full_contract( - self, - contract_address: Hash, - ) -> Union[ContractClass, SierraContractClass]: - res = await self._feeder_gateway_client.call( - method_name="get_full_contract", - params={ - "contractAddress": hash_to_felt(contract_address), - }, - ) - return TypesOfContractClassSchema().load( - res, unknown=EXCLUDE - ) # pyright: ignore - - async def get_signature( - self, - block_hash: Optional[Union[Hash, Tag]] = None, - block_number: Optional[Union[int, Tag]] = None, - ) -> SignatureOnStateDiff: - """ - Information on what is this signature and how it is calulated here: - https://community.starknet.io/t/introducing-p2p-authentication-and-mismatch-resolution-in-v0-12-2/97993#signature-on-state-diff-commitment-and-block-hash-3 - - :param block_hash: Block's hash or literals `"pending"` or `"latest"`. - :param block_number: Block's number or literals `"pending"` or `"latest"`. - - :return: Signature on state diff. - """ - block_identifier = get_block_identifier( - block_hash=block_hash, block_number=block_number - ) - res = await self._feeder_gateway_client.call( - method_name="get_signature", - params={**block_identifier}, - ) - return SignatureOnStateDiffSchema().load( - res, unknown=EXCLUDE - ) # pyright: ignore - - async def get_public_key(self) -> str: - """ - Method returning current public key. - - :return: Public key. - """ - public_key = await self._feeder_gateway_client.call( - method_name="get_public_key" - ) - return cast(str, public_key) - - -def get_block_identifier( - block_hash: Optional[Union[Hash, Tag]] = None, - block_number: Optional[Union[int, Tag]] = None, -) -> dict: - if block_hash is not None and block_number is not None: - raise ValueError( - "Arguments block_hash and block_number are mutually exclusive." - ) - - if block_hash is not None: - if is_block_identifier(block_hash): - return {"blockNumber": block_hash} - return {"blockHash": hash_to_felt(block_hash)} - - if block_number is not None: - return {"blockNumber": block_number} - - return {"blockNumber": "pending"} - - -def _get_payload( - txs: Union[AccountTransaction, List[AccountTransaction]] -) -> Union[List, Dict]: - if single_transaction := isinstance(txs, AccountTransaction): - txs = [txs] - - payload_list = [] - for tx in txs: - payload = _tx_to_schema(tx).dump(obj=tx) - assert isinstance(payload, dict) - if isinstance(tx, DeclareV2): - payload = compress_program(data=payload, program_name="sierra_program") - payload_list.append(payload) - - return payload_list if not single_transaction else payload_list[0] - - -def _tx_to_schema(tx: AccountTransaction): - if isinstance(tx, Declare): - return DeclareSchema() - if isinstance(tx, DeclareV2): - return DeclareV2Schema() - if isinstance(tx, DeployAccount): - return DeployAccountSchema() - if isinstance(tx, Invoke): - return InvokeSchema() - raise ValueError("Invalid tx type.") diff --git a/starknet_py/net/http_client.py b/starknet_py/net/http_client.py index 2d3d5a117..15cef237a 100644 --- a/starknet_py/net/http_client.py +++ b/starknet_py/net/http_client.py @@ -58,32 +58,6 @@ async def handle_request_error(self, request: ClientResponse): """ -class GatewayHttpClient(HttpClient): - async def call(self, method_name: str, params: Optional[dict] = None) -> dict: - return await self.request( - http_method=HttpMethod.GET, address=self.address(method_name), params=params - ) - - async def post( - self, - method_name: str, - payload: Union[Dict[str, Any], List[Dict[str, Any]]], - params: Optional[dict] = None, - ) -> dict: - return await self.request( - http_method=HttpMethod.POST, - address=self.address(method_name), - payload=payload, - params=params, - ) - - def address(self, method_name): - return f"{self.url}/{method_name}" - - async def handle_request_error(self, request: ClientResponse): - await basic_error_handle(request) - - class RpcHttpClient(HttpClient): async def call(self, method_name: str, params: dict): payload = { diff --git a/starknet_py/net/networks.py b/starknet_py/net/networks.py index 979749c64..33118ad89 100644 --- a/starknet_py/net/networks.py +++ b/starknet_py/net/networks.py @@ -1,4 +1,4 @@ -from typing import Literal, TypedDict, Union +from typing import Literal, Union from starknet_py.constants import FEE_CONTRACT_ADDRESS @@ -6,20 +6,7 @@ TESTNET = "testnet" PredefinedNetwork = Literal["mainnet", "testnet"] - -class CustomGatewayUrls(TypedDict): - feeder_gateway_url: str - gateway_url: str - - -Network = Union[PredefinedNetwork, str, CustomGatewayUrls] - - -def net_address_from_net(net: str) -> str: - return { - MAINNET: "https://alpha-mainnet.starknet.io", - TESTNET: "https://alpha4.starknet.io", - }.get(net, net) +Network = Union[PredefinedNetwork, str] def default_token_address_for_network(net: Network) -> str: diff --git a/starknet_py/net/schemas/gateway.py b/starknet_py/net/schemas/gateway.py index b8f1af443..24803a45d 100644 --- a/starknet_py/net/schemas/gateway.py +++ b/starknet_py/net/schemas/gateway.py @@ -1,447 +1,25 @@ import json -from typing import Any, Dict, List -from marshmallow import EXCLUDE, Schema, ValidationError, fields, post_load -from marshmallow_oneofschema import OneOfSchema +from marshmallow import Schema, ValidationError, fields, post_load from starknet_py.net.client_models import ( - BlockSingleTransactionTrace, - BlockStateUpdate, - BlockTransactionTraces, CasmClass, CasmClassEntryPoint, CasmClassEntryPointsByType, CompiledContract, ContractClass, - ContractCode, - ContractsNonce, - DeclaredContractHash, - DeclareTransaction, - DeclareTransactionResponse, - DeployAccountTransaction, - DeployAccountTransactionResponse, - DeployedContract, - DeployTransaction, EntryPoint, EntryPointsByType, - EstimatedFee, - Event, - GatewayBlock, - GatewayBlockTransactionReceipt, - GatewayStateDiff, - GatewayTransactionStatusResponse, - InvokeTransaction, - L1HandlerTransaction, - L1toL2Message, - L2toL1Message, - ReplacedClass, - SentTransactionResponse, SierraCompiledContract, SierraContractClass, SierraEntryPoint, SierraEntryPointsByType, - SignatureInput, - SignatureOnStateDiff, - StateUpdateWithBlock, - StorageDiffItem, - TransactionReceipt, -) -from starknet_py.net.schemas.common import ( - BlockStatusField, - ExecutionStatusField, - Felt, - FinalityStatusField, - NonPrefixedHex, - StatusField, - StorageEntrySchema, -) -from starknet_py.net.schemas.utils import ( - _replace_invoke_contract_address_with_sender_address, ) +from starknet_py.net.schemas.common import Felt # pylint: disable=unused-argument, no-self-use -class EventSchema(Schema): - from_address = Felt(data_key="from_address", required=True) - keys = fields.List(Felt(), data_key="keys", required=True) - data = fields.List(Felt(), data_key="data", required=True) - - @post_load - def make_dataclass(self, data, **kwargs) -> Event: - return Event(**data) - - -class L1toL2MessageSchema(Schema): - nonce = Felt(data_key="nonce", required=True) - selector = Felt(data_key="selector", required=True) - l1_address = Felt(data_key="from_address", required=True) - l2_address = Felt(data_key="to_address", required=True) - payload = fields.List(Felt(), data_key="payload", required=True) - - @post_load - def make_dataclass(self, data, **kwargs) -> L1toL2Message: - return L1toL2Message(**data) - - -class L2toL1MessageSchema(Schema): - l2_address = Felt(data_key="from_address", required=True) - l1_address = Felt(data_key="to_address", required=True) - payload = fields.List(Felt(), data_key="payload", required=True) - - @post_load - def make_dataclass(self, data, **kwargs) -> L2toL1Message: - return L2toL1Message(**data) - - -class TransactionSchema(Schema): - hash = Felt(data_key="transaction_hash", required=True) - signature = fields.List(Felt(), data_key="signature", load_default=[]) - max_fee = Felt(data_key="max_fee", load_default=0) - version = Felt(data_key="version", required=True) - - -class InvokeTransactionSchema(TransactionSchema): - contract_address = Felt(data_key="contract_address", load_default=None) - sender_address = Felt(data_key="sender_address", load_default=None) - calldata = fields.List(Felt(), data_key="calldata", required=True) - entry_point_selector = Felt(data_key="entry_point_selector", load_default=None) - nonce = Felt(data_key="nonce", load_default=None) - - @post_load - def make_dataclass(self, data, **kwargs) -> InvokeTransaction: - _replace_invoke_contract_address_with_sender_address(data) - return InvokeTransaction(**data) - - -class DeployTransactionSchema(TransactionSchema): - contract_address = Felt(data_key="contract_address", required=True) - contract_address_salt = Felt(data_key="contract_address_salt", required=True) - constructor_calldata = fields.List( - Felt(), data_key="constructor_calldata", required=True - ) - class_hash = Felt(data_key="class_hash", load_default=0) - - @post_load - def make_dataclass(self, data, **kwargs) -> DeployTransaction: - return DeployTransaction(**data) - - -class DeclareTransactionSchema(TransactionSchema): - class_hash = Felt(data_key="class_hash", required=True) - sender_address = Felt(data_key="sender_address", required=True) - nonce = Felt(data_key="nonce", load_default=None) - compiled_class_hash = Felt(data_key="compiled_class_hash", load_default=None) - - @post_load - def make_dataclass(self, data, **kwargs) -> DeclareTransaction: - return DeclareTransaction(**data) - - -class DeployAccountTransactionSchema(TransactionSchema): - contract_address_salt = Felt(data_key="contract_address_salt", required=True) - class_hash = Felt(data_key="class_hash", required=True) - constructor_calldata = fields.List( - Felt(), data_key="constructor_calldata", required=True - ) - nonce = Felt(data_key="nonce", required=True) - - @post_load - def make_dataclass(self, data, **kwargs) -> DeployAccountTransaction: - return DeployAccountTransaction(**data) - - -class L1HandlerTransactionSchema(TransactionSchema): - contract_address = Felt(data_key="contract_address", required=True) - calldata = fields.List(Felt(), data_key="calldata", required=True) - entry_point_selector = Felt(data_key="entry_point_selector", required=True) - nonce = Felt(data_key="nonce", load_default=None) - - @post_load - def make_dataclass(self, data, **kwargs) -> L1HandlerTransaction: - return L1HandlerTransaction(**data) - - -class TypesOfTransactionsSchema(OneOfSchema): - type_field = "type" - type_schemas = { - "INVOKE_FUNCTION": InvokeTransactionSchema, - "DECLARE": DeclareTransactionSchema, - "DEPLOY": DeployTransactionSchema, - "DEPLOY_ACCOUNT": DeployAccountTransactionSchema, - "L1_HANDLER": L1HandlerTransactionSchema, - } - - -class TransactionReceiptSchema(Schema): - transaction_hash = Felt(data_key="transaction_hash", required=True) - - status = StatusField(data_key="status", load_default=None) - execution_status = ExecutionStatusField( - data_key="execution_status", load_default=None - ) - finality_status = FinalityStatusField(data_key="finality_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", allow_none=True) - revert_error = fields.String(data_key="revert_error", load_default=None) - rejection_reason = fields.Dict( - keys=fields.String(), - values=fields.Raw(), - data_key="transaction_failure_reason", - allow_none=True, - load_default=None, - ) - events = fields.List( - fields.Nested(EventSchema()), data_key="events", load_default=[] - ) - l1_to_l2_consumed_message = fields.Nested( - L1toL2MessageSchema(), data_key="l1_to_l2_consumed_message", load_default=None - ) - l2_to_l1_messages = fields.List( - fields.Nested(L2toL1MessageSchema()), - data_key="l2_to_l1_messages", - load_default=[], - ) - transaction_index = fields.Integer(data_key="transaction_index", load_default=None) - execution_resources = fields.Dict(data_key="execution_resources") - - @post_load - def make_dataclass(self, data, **kwargs) -> TransactionReceipt: - if data.get("rejection_reason", None) is not None: - rejection_reason = data["rejection_reason"]["error_message"] - del data["rejection_reason"] - - return TransactionReceipt(**data, rejection_reason=rejection_reason) - - return TransactionReceipt(**data) - - -class ContractCodeSchema(Schema): - bytecode = fields.List(Felt(), data_key="bytecode", required=True) - abi = fields.List( - fields.Dict(keys=fields.String(), values=fields.Raw()), - data_key="abi", - required=True, - ) - - @post_load - def make_dataclass(self, data, **kwargs): - return ContractCode(**data) - - -class GatewayBlockTransactionReceiptSchema(Schema): - transaction_index = fields.Integer(data_key="transaction_index", required=True) - transaction_hash = Felt(data_key="transaction_hash", required=True) - execution_status = ExecutionStatusField( - data_key="execution_status", load_default=None - ) - finality_status = FinalityStatusField(data_key="finality_status", load_default=None) - l2_to_l1_messages = fields.List( - fields.Nested(L2toL1MessageSchema()), - data_key="l2_to_l1_messages", - required=True, - ) - l1_to_l2_consumed_message = fields.Nested( - L1toL2MessageSchema(), data_key="l1_to_l2_consumed_message", load_default=None - ) - events = fields.List(fields.Nested(EventSchema()), data_key="events", required=True) - execution_resources = fields.Dict(data_key="execution_resources", load_default=None) - actual_fee = Felt(data_key="actual_fee", required=True) - revert_error = fields.String(data_key="revert_error", load_default=None) - - @post_load - def make_dataclass(self, data, **kwargs): - return GatewayBlockTransactionReceipt(**data) - - -class StarknetBlockSchema(Schema): - block_hash = Felt(data_key="block_hash") - parent_block_hash = Felt(data_key="parent_block_hash", required=True) - block_number = fields.Integer(data_key="block_number") - status = BlockStatusField(data_key="status", required=True) - root = Felt(data_key="state_root") - transactions = fields.List( - fields.Nested(TypesOfTransactionsSchema(unknown=EXCLUDE)), - data_key="transactions", - required=True, - ) - timestamp = fields.Integer(data_key="timestamp", required=True) - gas_price = Felt(data_key="gas_price", load_default=None) - sequencer_address = Felt(data_key="sequencer_address", load_default=None) - starknet_version = fields.String(data_key="starknet_version", load_default=None) - transaction_receipts = fields.List( - fields.Nested(GatewayBlockTransactionReceiptSchema()), - data_key="transaction_receipts", - required=True, - ) - - @post_load - def make_dataclass(self, data, **kwargs): - return GatewayBlock(**data) - - -class BlockSingleTransactionTraceSchema(Schema): - function_invocation = fields.Dict( - keys=fields.String(), - values=fields.Raw(), - data_key="function_invocation", - load_default=None, - ) - validate_invocation = fields.Dict( - keys=fields.String(), - values=fields.Raw(), - data_key="validate_invocation", - load_default=None, - ) - fee_transfer_invocation = fields.Dict( - keys=fields.String(), - values=fields.Raw(), - data_key="fee_transfer_invocation", - load_default=None, - ) - constructor_invocation = fields.Dict( - keys=fields.String(), - values=fields.Raw(), - data_key="constructor_invocation", - load_default=None, - ) - revert_error = fields.String(data_key="revert_error", load_default=None) - signature = fields.List(Felt(), data_key="signature", load_default=[]) - transaction_hash = Felt(data_key="transaction_hash", required=True) - - @post_load - def make_dataclass(self, data, **kwargs): - return BlockSingleTransactionTrace(**data) - - -class EstimatedFeeSchema(Schema): - overall_fee = fields.Integer(data_key="overall_fee", required=True) - gas_price = fields.Integer(data_key="gas_price", required=True) - gas_usage = fields.Integer(data_key="gas_usage", required=True) - - @post_load - def make_dataclass(self, data, **kwargs): - return EstimatedFee(**data) - - -class SentTransactionSchema(Schema): - transaction_hash = Felt(data_key="transaction_hash", required=True) - code = fields.String(data_key="code", required=True) - - @post_load - def make_dataclass(self, data, **kwargs): - return SentTransactionResponse(**data) - - -class DeclareTransactionResponseSchema(SentTransactionSchema): - class_hash = Felt(data_key="class_hash", required=True) - - @post_load - def make_dataclass(self, data, **kwargs): - return DeclareTransactionResponse(**data) - - -class DeployAccountTransactionResponseSchema(SentTransactionSchema): - address = Felt(data_key="address", required=True) - - @post_load - def make_dataclass(self, data, **kwargs): - return DeployAccountTransactionResponse(**data) - - -class DeployedContractSchema(Schema): - address = Felt(data_key="address", required=True) - class_hash = NonPrefixedHex(data_key="class_hash", required=True) - - @post_load - def make_dataclass(self, data, **kwargs): - return DeployedContract(**data) - - -class DeclaredContractHashSchema(Schema): - class_hash = Felt(data_key="class_hash", required=True) - compiled_class_hash = Felt(data_key="compiled_class_hash", required=True) - - @post_load - def make_dataclass(self, data, **kwargs) -> DeclaredContractHash: - return DeclaredContractHash(**data) - - -class ReplacedClassSchema(Schema): - contract_address = Felt(data_key="address", required=True) - class_hash = Felt(data_key="class_hash", required=True) - - @post_load - def make_dataclass(self, data, **kwargs) -> ReplacedClass: - return ReplacedClass(**data) - - -class StateDiffSchema(Schema): - deployed_contracts = fields.List( - fields.Nested(DeployedContractSchema()), - data_key="deployed_contracts", - required=True, - ) - deprecated_declared_contract_hashes = fields.List( - Felt(), - data_key="old_declared_contracts", - required=True, - ) - declared_contract_hashes = fields.List( - fields.Nested(DeclaredContractHashSchema()), - data_key="declared_classes", - required=True, - ) - storage_diffs = fields.Dict( - keys=fields.String(), - values=fields.List(fields.Nested(StorageEntrySchema())), - data_key="storage_diffs", - required=True, - ) - nonces = fields.Dict(keys=Felt(), values=Felt(), data_key="nonces", required=True) - replaced_classes = fields.List( - fields.Nested(ReplacedClassSchema()), data_key="replaced_classes", required=True - ) - - @post_load - def make_dataclass(self, data, **kwargs) -> GatewayStateDiff: - return GatewayStateDiff(**data) - - -class BlockStateUpdateSchema(Schema): - block_hash = Felt(data_key="block_hash", required=True) - new_root = Felt(data_key="new_root", required=True) - old_root = Felt(data_key="old_root", required=True) - state_diff = fields.Nested(StateDiffSchema(), data_key="state_diff", required=True) - - @post_load - def make_dataclass(self, data, **kwargs): - def fix_field(field: Dict, inner_class: Any) -> List[Any]: - return [inner_class(key, value) for key, value in field.items()] - - fixed_storage_diffs = fix_field( - data["state_diff"].storage_diffs, StorageDiffItem - ) - fixed_nonces = fix_field(data["state_diff"].nonces, ContractsNonce) - - data["state_diff"].storage_diffs = fixed_storage_diffs - data["state_diff"].nonces = fixed_nonces - return BlockStateUpdate(**data) - - -class StateUpdateWithBlockSchema(Schema): - block = fields.Nested(StarknetBlockSchema(), data_key="block", required=True) - state_update = fields.Nested( - BlockStateUpdateSchema(), data_key="state_update", required=True - ) - - @post_load - def make_dataclass(self, data, **kwargs) -> StateUpdateWithBlock: - return StateUpdateWithBlock(**data) - - class EntryPointSchema(Schema): offset = Felt(data_key="offset", required=True) selector = Felt(data_key="selector", required=True) @@ -545,19 +123,6 @@ def make_dataclass(self, data, **kwargs) -> SierraCompiledContract: return SierraCompiledContract(**data) -class TypesOfContractClassSchema(OneOfSchema): - type_schemas = { - "program": ContractClassSchema(), - "sierra_program": SierraContractClassSchema(), - } - - def get_data_type(self, data): - if "sierra_program" in data: - return "sierra_program" - - return "program" - - class CompiledContractSchema(ContractClassSchema): abi = fields.List(fields.Dict(), data_key="abi", required=True) @@ -613,52 +178,3 @@ class CasmClassSchema(Schema): @post_load def make_dataclass(self, data, **kwargs) -> CasmClass: return CasmClass(**data) - - -class TransactionStatusSchema(Schema): - transaction_status = StatusField(data_key="tx_status", required=True) - finality_status = FinalityStatusField(data_key="finality_status", load_default=None) - execution_status = ExecutionStatusField( - data_key="execution_status", load_default=None - ) - block_hash = Felt(data_key="block_hash", load_default=None) - - @post_load - def make_result(self, data, **kwargs) -> GatewayTransactionStatusResponse: - return GatewayTransactionStatusResponse(**data) - - -class SignatureInputSchema(Schema): - block_hash = Felt(data_key="block_hash", required=True) - state_diff_commitment = Felt(data_key="state_diff_commitment", required=True) - - @post_load - def make_dataclass(self, data, **kwargs) -> SignatureInput: - return SignatureInput(**data) - - -class SignatureOnStateDiffSchema(Schema): - block_number = Felt(data_key="block_number", required=True) - signature = fields.List(Felt(), data_key="signature", required=True) - signature_input = fields.Nested( - SignatureInputSchema(), data_key="signature_input", required=True - ) - - @post_load - def make_dataclass(self, data, **kwargs) -> SignatureOnStateDiff: - return SignatureOnStateDiff(**data) - - -# Trace API schemas - - -class BlockTransactionTracesSchema(Schema): - traces = fields.List( - fields.Nested(BlockSingleTransactionTraceSchema(unknown=EXCLUDE)), - data_key="traces", - required=True, - ) - - @post_load - def make_dataclass(self, data, **kwargs): - return BlockTransactionTraces(**data) diff --git a/starknet_py/net/schemas/rpc.py b/starknet_py/net/schemas/rpc.py index f40cb96b4..2d37d1b56 100644 --- a/starknet_py/net/schemas/rpc.py +++ b/starknet_py/net/schemas/rpc.py @@ -102,6 +102,36 @@ def make_dataclass(self, data, **kwargs) -> L2toL1Message: return L2toL1Message(**data) +class ExecutionResourcesSchema(Schema): + steps = Felt(data_key="steps", required=True) + range_check_builtin_applications = Felt( + data_key="range_check_builtin_applications", required=True + ) + pedersen_builtin_applications = Felt( + data_key="pedersen_builtin_applications", required=True + ) + poseidon_builtin_applications = Felt( + data_key="poseidon_builtin_applications", required=True + ) + ec_op_builtin_applications = Felt( + data_key="ec_op_builtin_applications", required=True + ) + ecdsa_builtin_applications = Felt( + data_key="ecdsa_builtin_applications", required=True + ) + bitwise_builtin_applications = Felt( + data_key="bitwise_builtin_applications", required=True + ) + keccak_builtin_applications = Felt( + data_key="keccak_builtin_applications", required=True + ) + memory_holes = Felt(data_key="memory_holes", load_default=None) + + @post_load + def make_dataclass(self, data, **kwargs) -> ExecutionResources: + return ExecutionResources(**data) + + class TransactionReceiptSchema(Schema): transaction_hash = Felt(data_key="transaction_hash", required=True) # replaced by execution and finality status in RPC v0.4.0-rc1 @@ -120,12 +150,14 @@ class TransactionReceiptSchema(Schema): events = fields.List( fields.Nested(EventSchema()), data_key="events", load_default=[] ) - l2_to_l1_messages = fields.List( + messages_sent = fields.List( fields.Nested(L2toL1MessageSchema()), data_key="messages_sent", load_default=[] ) message_hash = Felt(data_key="message_hash", load_default=None) # TODO (#1179): this field should be required - execution_resources = fields.Dict(data_key="execution_resources", load_default=None) + execution_resources = fields.Nested( + ExecutionResourcesSchema(), data_key="execution_resources", load_default=None + ) @post_load def make_dataclass(self, data, **kwargs) -> TransactionReceipt: @@ -194,7 +226,6 @@ def make_dataclass(self, data, **kwargs) -> DeclareTransaction: class DeployTransactionSchema(TransactionSchema): - contract_address = Felt(data_key="contract_address", load_default=None) contract_address_salt = Felt(data_key="contract_address_salt", required=True) constructor_calldata = fields.List( Felt(), data_key="constructor_calldata", required=True @@ -563,36 +594,6 @@ def make_dataclass(self, data, **kwargs) -> DeployAccountTransactionResponse: return DeployAccountTransactionResponse(**data) -class ExecutionResourcesSchema(Schema): - steps = Felt(data_key="steps", required=True) - range_check_builtin_applications = Felt( - data_key="range_check_builtin_applications", required=True - ) - pedersen_builtin_applications = Felt( - data_key="pedersen_builtin_applications", required=True - ) - poseidon_builtin_applications = Felt( - data_key="poseidon_builtin_applications", required=True - ) - ec_op_builtin_applications = Felt( - data_key="ec_op_builtin_applications", required=True - ) - ecdsa_builtin_applications = Felt( - data_key="ecdsa_builtin_applications", required=True - ) - bitwise_builtin_applications = Felt( - data_key="bitwise_builtin_applications", required=True - ) - keccak_builtin_applications = Felt( - data_key="keccak_builtin_applications", required=True - ) - memory_holes = Felt(data_key="memory_holes", load_default=None) - - @post_load - def make_dataclass(self, data, **kwargs) -> ExecutionResources: - return ExecutionResources(**data) - - # ------------------------------- Trace API ------------------------------- diff --git a/starknet_py/tests/e2e/account/account_test.py b/starknet_py/tests/e2e/account/account_test.py index 2a1db142c..a535bd5db 100644 --- a/starknet_py/tests/e2e/account/account_test.py +++ b/starknet_py/tests/e2e/account/account_test.py @@ -18,7 +18,6 @@ TransactionStatus, ) from starknet_py.net.full_node_client import FullNodeClient -from starknet_py.net.gateway_client import GatewayClient from starknet_py.net.models import StarknetChainId from starknet_py.net.models.transaction import Declare, DeclareV2 from starknet_py.net.signer.stark_curve_signer import KeyPair @@ -28,14 +27,10 @@ @pytest.mark.run_on_devnet @pytest.mark.asyncio -@pytest.mark.parametrize( - "client", - [GatewayClient(net="custom.net"), FullNodeClient(node_url="custom.net/rpc")], -) -async def test_get_balance_throws_when_token_not_specified(account, client): +async def test_get_balance_throws_when_token_not_specified(account): modified_account = Account( address=account.address, - client=client, + client=FullNodeClient(node_url="custom.net/rpc"), key_pair=KeyPair(1, 2), chain=cast(StarknetChainId, 1), ) @@ -54,7 +49,7 @@ async def test_balance_when_token_specified(account, erc20_contract): @pytest.mark.asyncio -async def test_estimated_fee_greater_than_zero(erc20_contract, account): +async def test_estimated_fee_greater_than_zero(account, erc20_contract): erc20_contract = Contract( address=erc20_contract.address, abi=erc20_contract.data.abi, provider=account ) @@ -102,18 +97,12 @@ async def test_sending_multicall(account, map_contract, key, val): assert value == val -@pytest.mark.run_on_devnet -@pytest.mark.asyncio -async def test_get_block_traces(gateway_account): - traces = await gateway_account.client.trace_block_transactions(block_number=2) - - assert traces.traces != [] - - @pytest.mark.asyncio async def test_rejection_reason_in_transaction_receipt(map_contract): - with pytest.raises(ClientError, match=r".*INSUFFICIENT_MAX_FEE.*"): - _ = await map_contract.functions["put"].invoke(key=10, value=20, max_fee=1) + with pytest.raises( + ClientError, match="Max fee is smaller than the minimal transaction cost" + ): + await map_contract.functions["put"].invoke(key=10, value=20, max_fee=1) def test_sign_and_verify_offchain_message_fail(account, typed_data): @@ -132,7 +121,7 @@ def test_sign_and_verify_offchain_message(account, typed_data): @pytest.mark.asyncio -async def test_get_class_hash_at(map_contract, account): +async def test_get_class_hash_at(account, map_contract): class_hash = await account.client.get_class_hash_at( map_contract.address, block_hash="latest" ) @@ -262,7 +251,7 @@ async def test_sign_declare_v2_transaction_auto_estimate( @pytest.mark.asyncio async def test_declare_contract_raises_on_sierra_contract_without_compiled_class_hash( - sierra_minimal_compiled_contract_and_class_hash, account + account, sierra_minimal_compiled_contract_and_class_hash ): compiled_contract, _ = sierra_minimal_compiled_contract_and_class_hash with pytest.raises( @@ -368,26 +357,14 @@ async def test_deploy_account_raises_on_incorrect_address( @pytest.mark.asyncio -@pytest.mark.parametrize( - "call_contract, client", - [ - ( - "starknet_py.net.gateway_client.GatewayClient.call_contract", - "gateway_client", - ), - ( - "starknet_py.net.full_node_client.FullNodeClient.call_contract", - "full_node_client", - ), - ], -) async def test_deploy_account_raises_on_no_enough_funds( - deploy_account_details_factory, call_contract, client, request + deploy_account_details_factory, client ): - client = request.getfixturevalue(client) address, key_pair, salt, class_hash = await deploy_account_details_factory.get() - with patch(call_contract, AsyncMock()) as mocked_balance: + with patch( + f"{FullNodeClient.__module__}.FullNodeClient.call_contract", AsyncMock() + ) as mocked_balance: mocked_balance.return_value = (0, 0) with pytest.raises( @@ -406,29 +383,15 @@ async def test_deploy_account_raises_on_no_enough_funds( @pytest.mark.asyncio -@pytest.mark.parametrize( - "client_method_path, client", - [ - ( - "starknet_py.net.gateway_client.GatewayClient", - "gateway_client", - ), - ( - "starknet_py.net.full_node_client.FullNodeClient", - "full_node_client", - ), - ], -) async def test_deploy_account_passes_on_enough_funds( - deploy_account_details_factory, client_method_path, client, request + deploy_account_details_factory, client ): address, key_pair, salt, class_hash = await deploy_account_details_factory.get() - client = request.getfixturevalue(client) with patch( - client_method_path + ".call_contract", AsyncMock() + f"{FullNodeClient.__module__}.FullNodeClient.call_contract", AsyncMock() ) as mocked_balance, patch( - client_method_path + ".deploy_account", AsyncMock() + f"{FullNodeClient.__module__}.FullNodeClient.deploy_account", AsyncMock() ) as mocked_deploy: mocked_balance.return_value = (0, 100) mocked_deploy.return_value = DeployAccountTransactionResponse( @@ -590,7 +553,7 @@ async def test_sign_transaction_custom_nonce(account, cairo1_hello_starknet_clas @pytest.mark.asyncio async def test_argent_cairo1_account_deploy( - full_node_client, + client, argent_cairo1_account_class_hash, deploy_account_details_factory, ): @@ -603,7 +566,7 @@ async def test_argent_cairo1_account_deploy( class_hash=class_hash, salt=salt, key_pair=key_pair, - client=full_node_client, + client=client, constructor_calldata=[key_pair.public_key, 0], chain=StarknetChainId.TESTNET, max_fee=int(1e16), @@ -614,7 +577,7 @@ async def test_argent_cairo1_account_deploy( assert isinstance(account, BaseAccount) assert await account.cairo_version == 1 - account_contract_class = await full_node_client.get_class_at( + account_contract_class = await client.get_class_at( contract_address=account.address, block_number="latest" ) diff --git a/starknet_py/tests/e2e/block_test.py b/starknet_py/tests/e2e/block_test.py index 71b6838cc..3a170d979 100644 --- a/starknet_py/tests/e2e/block_test.py +++ b/starknet_py/tests/e2e/block_test.py @@ -3,13 +3,11 @@ from starknet_py.contract import Contract from starknet_py.net.account.base_account import BaseAccount from starknet_py.net.client_models import ( - GatewayBlock, PendingStarknetBlock, PendingStarknetBlockWithTxHashes, StarknetBlock, StarknetBlockWithTxHashes, ) -from starknet_py.net.gateway_client import GatewayClient from starknet_py.tests.e2e.fixtures.constants import MAX_FEE @@ -28,11 +26,7 @@ async def test_pending_block(account, map_compiled_contract): blk = await account.client.get_block(block_number="pending") assert blk.transactions is not None - if isinstance(account.client, GatewayClient): - assert blk.block_hash - assert isinstance(blk, GatewayBlock) - else: - assert isinstance(blk, PendingStarknetBlock) + assert isinstance(blk, PendingStarknetBlock) @pytest.mark.asyncio @@ -42,19 +36,12 @@ async def test_latest_block(account, map_compiled_contract): blk = await account.client.get_block(block_number="latest") assert blk.block_hash assert blk.transactions is not None - if isinstance(account.client, GatewayClient): - assert isinstance(blk, GatewayBlock) - else: - assert isinstance(blk, StarknetBlock) + assert isinstance(blk, StarknetBlock) @pytest.mark.asyncio -async def test_block_with_tx_hashes_pending( - full_node_account, -): - blk = await full_node_account.client.get_block_with_tx_hashes( - block_number="pending" - ) +async def test_block_with_tx_hashes_pending(account): + blk = await account.client.get_block_with_tx_hashes(block_number="pending") assert isinstance(blk, PendingStarknetBlockWithTxHashes) assert isinstance(blk.transactions, list) @@ -62,10 +49,10 @@ async def test_block_with_tx_hashes_pending( @pytest.mark.asyncio async def test_block_with_tx_hashes_latest( - full_node_account, + account, map_contract_declare_hash, ): - blk = await full_node_account.client.get_block_with_tx_hashes(block_number="latest") + blk = await account.client.get_block_with_tx_hashes(block_number="latest") assert isinstance(blk, StarknetBlockWithTxHashes) assert isinstance(blk.transactions, list) @@ -79,10 +66,8 @@ async def test_block_with_tx_hashes_latest( @pytest.mark.asyncio -async def test_get_block_with_txs_pending( - full_node_account, -): - blk = await full_node_account.client.get_block_with_txs(block_number="pending") +async def test_get_block_with_txs_pending(account): + blk = await account.client.get_block_with_txs(block_number="pending") assert isinstance(blk, PendingStarknetBlock) assert isinstance(blk.transactions, list) @@ -90,10 +75,10 @@ async def test_get_block_with_txs_pending( @pytest.mark.asyncio async def test_get_block_with_txs_latest( - full_node_account, + account, map_contract_declare_hash, ): - blk = await full_node_account.client.get_block_with_txs(block_number="latest") + blk = await account.client.get_block_with_txs(block_number="latest") assert isinstance(blk, StarknetBlock) assert isinstance(blk.transactions, list) diff --git a/starknet_py/tests/e2e/client/client_test.py b/starknet_py/tests/e2e/client/client_test.py index 2dbbd6df0..c7326d698 100644 --- a/starknet_py/tests/e2e/client/client_test.py +++ b/starknet_py/tests/e2e/client/client_test.py @@ -16,7 +16,6 @@ DeclaredContractHash, DeclareTransaction, DeployAccountTransaction, - GatewayBlock, InvokeTransaction, L1HandlerTransaction, PendingBlockStateUpdate, @@ -28,7 +27,7 @@ TransactionType, ) from starknet_py.net.full_node_client import FullNodeClient -from starknet_py.net.gateway_client import GatewayClient +from starknet_py.net.http_client import RpcHttpClient from starknet_py.net.models.transaction import DeclareV2 from starknet_py.net.udc_deployer.deployer import Deployer from starknet_py.tests.e2e.fixtures.constants import CONTRACTS_COMPILED_V0_DIR, MAX_FEE @@ -93,9 +92,6 @@ async def test_get_block_by_hash( assert block.block_hash == block_with_declare_hash assert len(block.transactions) != 0 - if isinstance(block, GatewayBlock): - assert block.gas_price > 0 - @pytest.mark.asyncio async def test_get_block_by_number( @@ -109,9 +105,6 @@ async def test_get_block_by_number( assert block.block_hash == block_with_declare_hash assert len(block.transactions) != 0 - if isinstance(block, GatewayBlock): - assert block.gas_price > 0 - @pytest.mark.asyncio async def test_get_storage_at(client, contract_address): @@ -132,8 +125,7 @@ async def test_get_transaction_receipt( assert receipt.transaction_hash == invoke_transaction_hash assert receipt.block_number == block_with_invoke_number - if isinstance(client, FullNodeClient): - assert receipt.type == TransactionType.INVOKE + assert receipt.type == TransactionType.INVOKE @pytest.mark.asyncio @@ -235,8 +227,7 @@ async def test_add_transaction(map_contract, client, account): transaction_receipt = await client.get_transaction_receipt(result.transaction_hash) assert transaction_receipt.status != TransactionStatus.NOT_RECEIVED - if isinstance(client, FullNodeClient): - assert transaction_receipt.type == TransactionType.INVOKE + assert transaction_receipt.type == TransactionType.INVOKE @pytest.mark.asyncio @@ -258,24 +249,9 @@ async def test_get_class_by_hash(client, class_hash): @pytest.mark.asyncio -@pytest.mark.parametrize( - "client, get_tx_receipt", - [ - ( - "gateway_client", - "tx_receipt_gateway_path", - ), - ( - "full_node_client", - "tx_receipt_full_node_path", - ), - ], -) -async def test_wait_for_tx_accepted(client, get_tx_receipt, request): - get_tx_receipt = request.getfixturevalue(get_tx_receipt) - +async def test_wait_for_tx_accepted(client, get_tx_receipt_path): with patch( - get_tx_receipt, + get_tx_receipt_path, AsyncMock(), ) as mocked_receipt: mocked_receipt.return_value = TransactionReceipt( @@ -284,11 +260,11 @@ async def test_wait_for_tx_accepted(client, get_tx_receipt, request): block_number=1, type=TransactionType.INVOKE, ) - client = request.getfixturevalue(client) tx_receipt = await client.wait_for_tx(tx_hash=0x1) assert tx_receipt.status == TransactionStatus.ACCEPTED_ON_L2 +@pytest.mark.asyncio @pytest.mark.parametrize( "status, exception, exc_message", ( @@ -299,27 +275,11 @@ async def test_wait_for_tx_accepted(client, get_tx_receipt, request): ), ), ) -@pytest.mark.parametrize( - "client, get_tx_receipt", - [ - ( - "gateway_client", - "tx_receipt_gateway_path", - ), - ( - "full_node_client", - "tx_receipt_full_node_path", - ), - ], -) -@pytest.mark.asyncio async def test_wait_for_tx_rejected( - status, exception, exc_message, client, get_tx_receipt, request + status, exception, exc_message, client, get_tx_receipt_path ): - get_tx_receipt = request.getfixturevalue(get_tx_receipt) - with patch( - get_tx_receipt, + get_tx_receipt_path, AsyncMock(), ) as mocked_receipt: mocked_receipt.return_value = TransactionReceipt( @@ -329,7 +289,7 @@ async def test_wait_for_tx_rejected( rejection_reason=exc_message, type=TransactionType.INVOKE, ) - client = request.getfixturevalue(client) + with pytest.raises(exception) as err: await client.wait_for_tx(tx_hash=0x1) @@ -337,24 +297,9 @@ async def test_wait_for_tx_rejected( @pytest.mark.asyncio -@pytest.mark.parametrize( - "client, get_tx_receipt", - [ - ( - "gateway_client", - "tx_receipt_gateway_path", - ), - ( - "full_node_client", - "tx_receipt_full_node_path", - ), - ], -) -async def test_wait_for_tx_cancelled(client, get_tx_receipt, request): - get_tx_receipt = request.getfixturevalue(get_tx_receipt) - +async def test_wait_for_tx_cancelled(client, get_tx_receipt_path): with patch( - get_tx_receipt, + get_tx_receipt_path, AsyncMock(), ) as mocked_receipt: mocked_receipt.return_value = TransactionReceipt( @@ -363,7 +308,6 @@ async def test_wait_for_tx_cancelled(client, get_tx_receipt, request): block_number=1, type=TransactionType.INVOKE, ) - client = request.getfixturevalue(client) task = asyncio.create_task(client.wait_for_tx(tx_hash=0x1)) await asyncio.sleep(1) task.cancel() @@ -373,35 +317,19 @@ async def test_wait_for_tx_cancelled(client, get_tx_receipt, request): @pytest.mark.asyncio -@pytest.mark.parametrize( - "client, get_tx_receipt", - [ - ( - "gateway_client", - "tx_receipt_gateway_path", - ), - ( - "full_node_client", - "tx_receipt_full_node_path", - ), - ], -) -async def test_wait_for_tx_unknown_error(client, get_tx_receipt, request): - get_tx_receipt = request.getfixturevalue(get_tx_receipt) - +async def test_wait_for_tx_unknown_error(client, get_tx_receipt_path): with patch( - get_tx_receipt, + get_tx_receipt_path, AsyncMock(), ) as mocked_receipt: mocked_receipt.side_effect = ClientError(message="Unknown error") - client = request.getfixturevalue(client) with pytest.raises(ClientError, match="Unknown error"): await client.wait_for_tx(tx_hash="0x2137") @pytest.mark.asyncio -async def test_declare_contract(map_compiled_contract, account): +async def test_declare_contract(account, map_compiled_contract): declare_tx = await account.sign_declare_transaction( compiled_contract=map_compiled_contract, max_fee=MAX_FEE ) @@ -414,14 +342,12 @@ async def test_declare_contract(map_compiled_contract, account): assert transaction_receipt.status != TransactionStatus.NOT_RECEIVED assert transaction_receipt.transaction_hash assert 0 < transaction_receipt.actual_fee <= MAX_FEE - if isinstance(client, FullNodeClient): - assert transaction_receipt.type == TransactionType.DECLARE + assert transaction_receipt.type == TransactionType.DECLARE @pytest.mark.asyncio -@pytest.mark.parametrize("client_class", [GatewayClient, FullNodeClient]) -async def test_custom_session_gateway_client(map_contract, network, client_class): - # We must access protected `feeder_gateway_client` or `_client` to test session +async def test_custom_session_client(map_contract, network): + # We must access protected `_client` to test session # pylint: disable=protected-access session = ClientSession() @@ -434,26 +360,10 @@ async def test_custom_session_gateway_client(map_contract, network, client_class ).wait_for_acceptance() ).hash - client1 = ( - client_class(net=network, session=session) - if client_class is GatewayClient - else client_class(node_url=network + "/rpc", net=network, session=session) - ) - client2 = ( - client_class(net=network, session=session) - if client_class is GatewayClient - else client_class(node_url=network + "/rpc", net=network, session=session) - ) - internal_client1 = ( - client1._feeder_gateway_client - if isinstance(client1, GatewayClient) - else client1._client - ) - internal_client2 = ( - client2._feeder_gateway_client - if isinstance(client2, GatewayClient) - else client2._client - ) + client1 = FullNodeClient(node_url=network + "/rpc", session=session) + client2 = FullNodeClient(node_url=network + "/rpc", session=session) + internal_client1 = client1._client + internal_client2 = client2._client assert internal_client1.session is not None assert internal_client1.session == session @@ -478,9 +388,7 @@ async def test_custom_session_gateway_client(map_contract, network, client_class @pytest.mark.asyncio async def test_get_l1_handler_transaction(client): with patch( - "starknet_py.net.http_client.GatewayHttpClient.call", AsyncMock() - ) as mocked_transaction_call_gateway, patch( - "starknet_py.net.http_client.RpcHttpClient.call", AsyncMock() + f"{RpcHttpClient.__module__}.RpcHttpClient.call", AsyncMock() ) as mocked_transaction_call_rpc: return_value = { "status": "ACCEPTED_ON_L1", @@ -503,7 +411,6 @@ async def test_get_l1_handler_transaction(client): "type": "L1_HANDLER", }, } - mocked_transaction_call_gateway.return_value = return_value mocked_transaction_call_rpc.return_value = return_value["transaction"] transaction = await client.get_transaction(tx_hash=0x1) @@ -522,10 +429,7 @@ async def test_state_update_declared_contract_hashes( ): state_update = await client.get_state_update(block_number=block_with_declare_number) - if isinstance(client, FullNodeClient): - assert class_hash in state_update.state_diff.deprecated_declared_classes - else: - assert class_hash in state_update.state_diff.deprecated_declared_contract_hashes + assert class_hash in state_update.state_diff.deprecated_declared_classes @pytest.mark.run_on_devnet @@ -540,8 +444,7 @@ async def test_state_update_storage_diffs( state_update = await client.get_state_update() assert len(state_update.state_diff.storage_diffs) != 0 - if isinstance(client, FullNodeClient): - assert isinstance(state_update, PendingBlockStateUpdate) + assert isinstance(state_update, PendingBlockStateUpdate) @pytest.mark.run_on_devnet @@ -563,8 +466,7 @@ async def test_state_update_deployed_contracts( state_update = await account.client.get_state_update() assert len(state_update.state_diff.deployed_contracts) != 0 - if isinstance(account.client, FullNodeClient): - assert isinstance(state_update, PendingBlockStateUpdate) + assert isinstance(state_update, PendingBlockStateUpdate) @pytest.mark.asyncio @@ -648,9 +550,7 @@ async def test_get_new_state_update( class_hash=cairo1_hello_starknet_class_hash, compiled_class_hash=declare_v2_hello_starknet.compiled_class_hash, ) - in state_update.state_diff.declared_contract_hashes - if isinstance(client, GatewayClient) - else state_update.state_diff.declared_classes + in state_update.state_diff.declared_classes ) (block_number, contract_address, class_hash) = replaced_class diff --git a/starknet_py/tests/e2e/client/fixtures/prepare_network.py b/starknet_py/tests/e2e/client/fixtures/prepare_network.py index 5e9aa4304..713e71881 100644 --- a/starknet_py/tests/e2e/client/fixtures/prepare_network.py +++ b/starknet_py/tests/e2e/client/fixtures/prepare_network.py @@ -17,7 +17,7 @@ async def prepare_network( - gateway_account: Account, + account: Account, deploy_account_details: AccountToBeDeployedDetails, ) -> PreparedNetworkData: contract_compiled = read_contract( @@ -25,7 +25,7 @@ async def prepare_network( ) prepared_data = await prepare_net_for_tests( - gateway_account, + account, compiled_contract=contract_compiled, deploy_account_details=deploy_account_details, ) @@ -145,7 +145,7 @@ def fixture_class_hash(prepare_network: Tuple[str, PreparedNetworkData]) -> int: @pytest_asyncio.fixture(name="prepare_network", scope="package") async def fixture_prepare_network( network: str, - gateway_account: Account, + account: Account, deploy_account_details_factory: AccountToBeDeployedDetailsFactory, ) -> AsyncGenerator[Tuple[str, PreparedNetworkData], None]: """ @@ -153,5 +153,5 @@ async def fixture_prepare_network( """ net = network details = await deploy_account_details_factory.get() - prepared_data = await prepare_network(gateway_account, details) + prepared_data = await prepare_network(account, details) yield net, prepared_data diff --git a/starknet_py/tests/e2e/client/fixtures/transactions.py b/starknet_py/tests/e2e/client/fixtures/transactions.py index 7dcdbc6ea..c0b501770 100644 --- a/starknet_py/tests/e2e/client/fixtures/transactions.py +++ b/starknet_py/tests/e2e/client/fixtures/transactions.py @@ -6,6 +6,7 @@ from starknet_py.contract import Contract from starknet_py.net.account.account import Account +from starknet_py.net.full_node_client import FullNodeClient from starknet_py.net.models.transaction import DeployAccount from starknet_py.net.udc_deployer.deployer import Deployer from starknet_py.tests.e2e.client.fixtures.prepare_net_for_gateway_test import ( @@ -34,7 +35,7 @@ async def deploy_account_transaction( key_pair=key_pair, class_hash=class_hash, salt=salt, - network=network, + client=FullNodeClient(network), ) diff --git a/starknet_py/tests/e2e/client/full_node_test.py b/starknet_py/tests/e2e/client/full_node_test.py index 833a965a6..309ca7a31 100644 --- a/starknet_py/tests/e2e/client/full_node_test.py +++ b/starknet_py/tests/e2e/client/full_node_test.py @@ -47,9 +47,9 @@ def _parse_event_name(event: str) -> str: @pytest.mark.run_on_devnet @pytest.mark.asyncio async def test_node_get_declare_transaction_by_block_number_and_index( - declare_transaction_hash, block_with_declare_number, full_node_client, class_hash + declare_transaction_hash, block_with_declare_number, client, class_hash ): - tx = await full_node_client.get_transaction_by_block_id( + tx = await client.get_transaction_by_block_id( block_number=block_with_declare_number, index=0 ) @@ -62,9 +62,9 @@ async def test_node_get_declare_transaction_by_block_number_and_index( @pytest.mark.run_on_devnet @pytest.mark.asyncio async def test_get_class_at( - full_node_client, contract_address, hello_starknet_deploy_transaction_address + client, contract_address, hello_starknet_deploy_transaction_address ): - declared_contract = await full_node_client.get_class_at( + declared_contract = await client.get_class_at( contract_address=contract_address, block_hash="latest" ) @@ -73,7 +73,7 @@ async def test_get_class_at( assert declared_contract.entry_points_by_type is not None assert declared_contract.abi is not None - declared_contract = await full_node_client.get_class_at( + declared_contract = await client.get_class_at( contract_address=hello_starknet_deploy_transaction_address, block_hash="latest" ) assert isinstance(declared_contract, SierraContractClass) @@ -84,20 +84,20 @@ async def test_get_class_at( @pytest.mark.run_on_devnet @pytest.mark.asyncio -async def test_get_class_at_throws_on_wrong_address(full_node_client): +async def test_get_class_at_throws_on_wrong_address(client): with pytest.raises( ClientError, match="Client failed with code 20: Contract not found." ): - await full_node_client.get_class_at(contract_address=0, block_hash="latest") + await client.get_class_at(contract_address=0, block_hash="latest") @pytest.mark.run_on_devnet @pytest.mark.asyncio -async def test_block_transaction_count(full_node_client): - latest_block = await full_node_client.get_block("latest") +async def test_block_transaction_count(client): + latest_block = await client.get_block("latest") for block_number in range(1, latest_block.block_number + 1): - transaction_count = await full_node_client.get_block_transaction_count( + transaction_count = await client.get_block_transaction_count( block_number=block_number ) @@ -105,17 +105,17 @@ async def test_block_transaction_count(full_node_client): @pytest.mark.asyncio -async def test_method_raises_on_both_block_hash_and_number(full_node_client): +async def test_method_raises_on_both_block_hash_and_number(client): with pytest.raises( ValueError, match="Arguments block_hash and block_number are mutually exclusive.", ): - await full_node_client.get_block(block_number=0, block_hash="0x0") + await client.get_block(block_number=0, block_hash="0x0") @pytest.mark.asyncio async def test_get_transaction_receipt_deploy_account( - full_node_client, deploy_account_details_factory + client, deploy_account_details_factory ): address, key_pair, salt, class_hash = await deploy_account_details_factory.get() deploy_result = await Account.deploy_account( @@ -123,22 +123,22 @@ async def test_get_transaction_receipt_deploy_account( class_hash=class_hash, salt=salt, key_pair=key_pair, - client=full_node_client, + client=client, chain=StarknetChainId.TESTNET, max_fee=int(1e16), ) await deploy_result.wait_for_acceptance() - receipt = await full_node_client.get_transaction_receipt(tx_hash=deploy_result.hash) + receipt = await client.get_transaction_receipt(tx_hash=deploy_result.hash) assert receipt.type == TransactionType.DEPLOY_ACCOUNT assert receipt.contract_address == deploy_result.account.address @pytest.mark.run_on_devnet @pytest.mark.asyncio -async def test_get_storage_at_incorrect_address_full_node_client(full_node_client): +async def test_get_storage_at_incorrect_address_full_node_client(client): with pytest.raises(ClientError, match="Contract not found"): - await full_node_client.get_storage_at( + await client.get_storage_at( contract_address=0x1111, key=get_storage_var_address("balance"), block_hash="latest", @@ -148,7 +148,7 @@ async def test_get_storage_at_incorrect_address_full_node_client(full_node_clien @pytest.mark.run_on_devnet @pytest.mark.asyncio async def test_get_events_without_following_continuation_token( - full_node_client, + client, simple_storage_with_event_contract: Contract, ): for i in range(4): @@ -157,7 +157,7 @@ async def test_get_events_without_following_continuation_token( ) chunk_size = 3 - events_response = await full_node_client.get_events( + events_response = await client.get_events( from_block_number=0, to_block_hash="latest", address=simple_storage_with_event_contract.address, @@ -173,7 +173,7 @@ async def test_get_events_without_following_continuation_token( @pytest.mark.run_on_devnet @pytest.mark.asyncio async def test_get_events_follow_continuation_token( - full_node_client, + client, simple_storage_with_event_contract: Contract, ): total_invokes = 2 @@ -182,7 +182,7 @@ async def test_get_events_follow_continuation_token( i, i + 1, auto_estimate=True ) - events_response = await full_node_client.get_events( + events_response = await client.get_events( from_block_number=0, to_block_hash="latest", address=simple_storage_with_event_contract.address, @@ -198,14 +198,14 @@ async def test_get_events_follow_continuation_token( @pytest.mark.run_on_devnet @pytest.mark.asyncio async def test_get_events_nonexistent_event_name( - full_node_client, + client, simple_storage_with_event_contract: Contract, ): await simple_storage_with_event_contract.functions[FUNCTION_ONE_NAME].invoke( 1, 1, auto_estimate=True ) - events_response = await full_node_client.get_events( + events_response = await client.get_events( from_block_number=0, to_block_hash="latest", address=simple_storage_with_event_contract.address, @@ -221,7 +221,7 @@ async def test_get_events_nonexistent_event_name( @pytest.mark.run_on_devnet @pytest.mark.asyncio async def test_get_events_with_two_events( - full_node_client, + client, simple_storage_with_event_contract: Contract, ): invokes_of_one = 1 @@ -235,21 +235,21 @@ async def test_get_events_with_two_events( i, i + 1, auto_estimate=True ) - event_one_events_response = await full_node_client.get_events( + event_one_events_response = await client.get_events( from_block_number=0, to_block_hash="latest", address=simple_storage_with_event_contract.address, keys=[[EVENT_ONE_PARSED_NAME]], follow_continuation_token=True, ) - event_two_events_response = await full_node_client.get_events( + event_two_events_response = await client.get_events( from_block_number=0, to_block_hash="latest", address=simple_storage_with_event_contract.address, keys=[[EVENT_TWO_PARSED_NAME]], follow_continuation_token=True, ) - event_one_two_events_response = await full_node_client.get_events( + event_one_two_events_response = await client.get_events( from_block_number=0, to_block_hash="latest", address=simple_storage_with_event_contract.address, @@ -270,7 +270,7 @@ async def test_get_events_with_two_events( @pytest.mark.run_on_devnet @pytest.mark.asyncio async def test_get_events_start_from_continuation_token( - full_node_client, + client, simple_storage_with_event_contract: Contract, ): for i in range(5): @@ -280,7 +280,7 @@ async def test_get_events_start_from_continuation_token( chunk_size = 2 continuation_token = "1" - events_response = await full_node_client.get_events( + events_response = await client.get_events( from_block_number=0, to_block_hash="latest", address=simple_storage_with_event_contract.address, @@ -297,7 +297,7 @@ async def test_get_events_start_from_continuation_token( @pytest.mark.run_on_devnet @pytest.mark.asyncio async def test_get_events_no_params( - full_node_client, + client, simple_storage_with_event_contract: Contract, ): default_chunk_size = 1 @@ -308,7 +308,7 @@ async def test_get_events_no_params( await simple_storage_with_event_contract.functions[FUNCTION_TWO_NAME].invoke( i, i + 1, auto_estimate=True ) - events_response = await full_node_client.get_events() + events_response = await client.get_events() assert len(events_response.events) == default_chunk_size @@ -316,11 +316,11 @@ async def test_get_events_no_params( @pytest.mark.run_on_devnet @pytest.mark.asyncio async def test_get_events_nonexistent_starting_block( - full_node_client, + client, simple_storage_with_event_contract: Contract, ): with pytest.raises(ClientError, match="Block not found"): - await full_node_client.get_events( + await client.get_events( from_block_number=10000, to_block_hash="latest", address=simple_storage_with_event_contract.address, @@ -331,26 +331,26 @@ async def test_get_events_nonexistent_starting_block( @pytest.mark.asyncio -async def test_get_block_number(full_node_client): - block_number = await full_node_client.get_block_number() +async def test_get_block_number(client): + block_number = await client.get_block_number() # pylint: disable=protected-access - await create_empty_block(full_node_client._client) + await create_empty_block(client._client) - new_block_number = await full_node_client.get_block_number() + new_block_number = await client.get_block_number() assert new_block_number == block_number + 1 @pytest.mark.asyncio -async def test_get_block_hash_and_number(full_node_client): - block_hash_and_number = await full_node_client.get_block_hash_and_number() +async def test_get_block_hash_and_number(client): + block_hash_and_number = await client.get_block_hash_and_number() assert isinstance(block_hash_and_number, BlockHashAndNumber) # pylint: disable=protected-access - await create_empty_block(full_node_client._client) + await create_empty_block(client._client) - new_block_hash_and_number = await full_node_client.get_block_hash_and_number() + new_block_hash_and_number = await client.get_block_hash_and_number() assert ( new_block_hash_and_number.block_number == block_hash_and_number.block_number + 1 @@ -359,21 +359,21 @@ async def test_get_block_hash_and_number(full_node_client): @pytest.mark.asyncio -async def test_get_chain_id(full_node_client): - chain_id = await full_node_client.get_chain_id() +async def test_get_chain_id(client): + chain_id = await client.get_chain_id() assert chain_id == hex(StarknetChainId.TESTNET.value) @pytest.mark.asyncio -async def test_get_syncing_status_false(full_node_client): - sync_status = await full_node_client.get_syncing_status() +async def test_get_syncing_status_false(client): + sync_status = await client.get_syncing_status() assert sync_status is False @pytest.mark.asyncio -async def test_get_syncing_status(full_node_client): +async def test_get_syncing_status(client): with patch( "starknet_py.net.http_client.RpcHttpClient.call", AsyncMock() ) as mocked_status: @@ -386,7 +386,7 @@ async def test_get_syncing_status(full_node_client): "highest_block_hash": "0x79abcb48e71524ad2e123624b0ee3d5f69f99759a23441f6f363794d0687a66", } - sync_status = await full_node_client.get_syncing_status() + sync_status = await client.get_syncing_status() assert isinstance(sync_status, SyncStatus) @@ -552,7 +552,7 @@ async def test_simulate_transactions_two_txs( @pytest.mark.skip(reason="Old devnet without RPC 0.5.0") @pytest.mark.asyncio async def test_simulate_transactions_deploy_account( - full_node_client, deploy_account_details_factory + client, deploy_account_details_factory ): address, key_pair, salt, class_hash = await deploy_account_details_factory.get() address = compute_address( @@ -563,7 +563,7 @@ async def test_simulate_transactions_deploy_account( ) account = Account( address=address, - client=full_node_client, + client=client, key_pair=key_pair, chain=StarknetChainId.TESTNET, ) @@ -574,7 +574,7 @@ async def test_simulate_transactions_deploy_account( max_fee=int(1e16), ) - simulated_txs = await full_node_client.simulate_transactions( + simulated_txs = await client.simulate_transactions( transactions=[deploy_account_tx], block_number="latest" ) diff --git a/starknet_py/tests/e2e/client/gateway_test.py b/starknet_py/tests/e2e/client/gateway_test.py deleted file mode 100644 index e3828d77b..000000000 --- a/starknet_py/tests/e2e/client/gateway_test.py +++ /dev/null @@ -1,224 +0,0 @@ -from unittest.mock import AsyncMock, patch - -import pytest - -from starknet_py.hash.storage import get_storage_var_address -from starknet_py.net.client_errors import ContractNotFoundError -from starknet_py.net.client_models import ( - CasmClass, - CasmClassEntryPointsByType, - ContractClass, - DeployTransaction, - GatewayTransactionStatusResponse, - L1HandlerTransaction, - SierraContractClass, - TransactionStatus, -) -from starknet_py.net.gateway_client import GatewayClient -from starknet_py.net.networks import MAINNET, TESTNET, CustomGatewayUrls - - -@pytest.mark.asyncio -async def test_gateway_raises_on_both_block_hash_and_number( - block_with_declare_number, gateway_client -): - with pytest.raises( - ValueError, - match="Arguments block_hash and block_number are mutually exclusive.", - ): - await gateway_client.get_block( - block_hash="0x0", block_number=block_with_declare_number - ) - - -@pytest.mark.asyncio -async def test_get_class_hash_at(contract_address, gateway_client, class_hash): - class_hash_resp = await gateway_client.get_class_hash_at( - contract_address=contract_address - ) - - assert class_hash_resp == class_hash - - -@pytest.mark.asyncio -async def test_get_compiled_class_by_class_hash( - gateway_client, cairo1_hello_starknet_class_hash -): - compiled_class = await gateway_client.get_compiled_class_by_class_hash( - class_hash=cairo1_hello_starknet_class_hash - ) - - assert isinstance(compiled_class, CasmClass) - assert isinstance(compiled_class.prime, int) - assert isinstance(compiled_class.bytecode, list) - assert isinstance(compiled_class.hints, list) - assert isinstance(compiled_class.pythonic_hints, list) - assert isinstance(compiled_class.compiler_version, str) - assert isinstance(compiled_class.entry_points_by_type, CasmClassEntryPointsByType) - - -@pytest.mark.asyncio -async def test_get_code(contract_address, gateway_client): - code = await gateway_client.get_code(contract_address=contract_address) - - assert code.abi is not None - assert len(code.abi) != 0 - assert code.bytecode is not None - assert len(code.bytecode) != 0 - - -@pytest.mark.asyncio -async def test_get_code_invalid_address(gateway_client): - with pytest.raises( - ContractNotFoundError, - match="^Client failed: No contract with address 123 found.$", - ): - await gateway_client.get_code(contract_address=123) - - with pytest.raises( - ContractNotFoundError, - match="^Client failed: No contract with address 123 found for block with block_number: latest.$", - ): - await gateway_client.get_code(contract_address=123, block_number="latest") - - -@pytest.mark.asyncio -async def test_get_contract_nonce(gateway_client): - nonce = await gateway_client.get_contract_nonce( - contract_address=0x1111, - block_hash="latest", - ) - assert nonce == 0 - - -@pytest.mark.asyncio -async def test_get_transaction_status(invoke_transaction_hash, gateway_client): - tx_status_resp = await gateway_client.get_transaction_status( - invoke_transaction_hash - ) - assert isinstance(tx_status_resp, GatewayTransactionStatusResponse) - assert tx_status_resp.transaction_status == TransactionStatus.ACCEPTED_ON_L2 - - -def test_gateway_client_warn_deprecation(): - with pytest.warns(PendingDeprecationWarning): - _ = GatewayClient(net="mainnet") - - -# pylint: disable=protected-access -@pytest.mark.parametrize( - "net, net_address", - ( - (TESTNET, "https://alpha4.starknet.io"), - (MAINNET, "https://alpha-mainnet.starknet.io"), - ), -) -def test_creating_client_from_predefined_network(net, net_address): - gateway_client = GatewayClient(net=net) - - assert gateway_client._feeder_gateway_client.url == f"{net_address}/feeder_gateway" - assert gateway_client._gateway_client.url == f"{net_address}/gateway" - - -def test_creating_client_with_custom_net(): - custom_net = "custom.net" - gateway_client = GatewayClient(net=custom_net) - - assert gateway_client._feeder_gateway_client.url == f"{custom_net}/feeder_gateway" - assert gateway_client._gateway_client.url == f"{custom_net}/gateway" - - -def test_creating_client_with_custom_net_dict(): - custom_net = "custom.net" - net = CustomGatewayUrls( - feeder_gateway_url=f"{custom_net}/feeder_gateway", - gateway_url=f"{custom_net}/gateway", - ) - - gateway_client = GatewayClient(net=net) - - assert gateway_client._feeder_gateway_client.url == net["feeder_gateway_url"] - assert gateway_client._gateway_client.url == net["gateway_url"] - - -@pytest.mark.asyncio -async def test_get_storage_at_incorrect_address_gateway_client(gateway_client): - storage = await gateway_client.get_storage_at( - contract_address=0x1111, - key=get_storage_var_address("balance"), - block_hash="latest", - ) - assert storage == 0 - - -@pytest.mark.asyncio -async def test_get_l1_handler_transaction_without_nonce(gateway_client): - with patch( - "starknet_py.net.http_client.GatewayHttpClient.call", AsyncMock() - ) as mocked_transaction_call: - mocked_transaction_call.return_value = { - "status": "ACCEPTED_ON_L1", - "block_hash": "0x38ce7678420eaff5cd62597643ca515d0887579a8be69563067fe79a624592b", - "block_number": 370459, - "transaction_index": 9, - "transaction": { - "version": "0x0", - "contract_address": "0x278f24c3e74cbf7a375ec099df306289beb0605a346277d200b791a7f811a19", - "entry_point_selector": "0x2d757788a8d8d6f21d1cd40bce38a8222d70654214e96ff95d8086e684fbee5", - "calldata": [ - "0xd8beaa22894cd33f24075459cfba287a10a104e4", - "0x3f9c67ef1d31e24b386184b4ede63a869c4659de093ef437ee235cae4daf2be", - "0x3635c9adc5dea00000", - "0x0", - "0x7cb4539b69a2371f75d21160026b76a7a7c1cacb", - ], - "transaction_hash": "0x7e1ed66dbccf915857c6367fc641c24292c063e54a5dd55947c2d958d94e1a9", - "type": "L1_HANDLER", - }, - } - - transaction = await gateway_client.get_transaction(tx_hash=0x1) - - assert isinstance(transaction, L1HandlerTransaction) - assert transaction.nonce is None - - -# Check if the `Deploy` transaction is fetched correctly -@pytest.mark.asyncio -async def test_get_deploy_tx(): - client = GatewayClient(net=TESTNET) - deploy_tx = await client.get_transaction( - tx_hash="0x068d6145cb99622cc930f9b26034c6f5127c348e8c21a5e232e36540a48622bb" - ) - - assert deploy_tx == DeployTransaction( - hash=2963673878706802757881372249643497924351429288158219425664578299882910393019, - signature=[], - max_fee=0, - version=0, - contract_address=3201539328574232511583948975549924874632298555514040085947217389204344560301, - contract_address_salt=1755481054165712359795659576392952180676068046985196641715115837975005192835, - constructor_calldata=[ - 2977964825119970114006147768568360818918965859196023865674869683232138769290, - 1295919550572838631247819983596733806859788957403169325509326258146877103642, - 1, - 1755481054165712359795659576392952180676068046985196641715115837975005192835, - ], - class_hash=1390726910323976264396851446996494490757233897803493337751952271375342730526, - ) - - -@pytest.mark.asyncio -async def test_get_full_contract(gateway_client, map_contract): - res = await gateway_client.get_full_contract(contract_address=map_contract.address) - - assert isinstance(res, ContractClass) - - -@pytest.mark.asyncio -async def test_get_full_contract_v1(gateway_client, cairo1_hello_starknet_deploy): - res = await gateway_client.get_full_contract( - contract_address=cairo1_hello_starknet_deploy.address - ) - - assert isinstance(res, SierraContractClass) diff --git a/starknet_py/tests/e2e/contract_interaction/interaction_test.py b/starknet_py/tests/e2e/contract_interaction/interaction_test.py index 4c78bc7b1..012618699 100644 --- a/starknet_py/tests/e2e/contract_interaction/interaction_test.py +++ b/starknet_py/tests/e2e/contract_interaction/interaction_test.py @@ -5,9 +5,7 @@ from starknet_py.hash.selector import get_selector_from_name from starknet_py.net.client_errors import ClientError from starknet_py.net.client_models import Call -from starknet_py.net.gateway_client import GatewayClient from starknet_py.tests.e2e.fixtures.constants import MAX_FEE -from starknet_py.transaction_errors import TransactionRevertedError @pytest.mark.asyncio @@ -119,11 +117,9 @@ async def test_invoke_and_call(key, value, map_contract): @pytest.mark.asyncio -async def test_call_uninitialized_contract(gateway_client): - with pytest.raises( - ClientError, match="Requested contract address 0x1 is not deployed." - ) as err: - await gateway_client.call_contract( +async def test_call_uninitialized_contract(client): + with pytest.raises(ClientError, match="Contract not found"): + await client.call_contract( Call( to_addr=1, selector=get_selector_from_name("get_nonce"), @@ -132,8 +128,6 @@ async def test_call_uninitialized_contract(gateway_client): block_hash="latest", ) - assert "400" in str(err.value) - @pytest.mark.asyncio async def test_wait_for_tx(client, map_contract): @@ -144,24 +138,8 @@ async def test_wait_for_tx(client, map_contract): @pytest.mark.asyncio -async def test_wait_for_tx_throws_on_transaction_reverted(gateway_client, map_contract): - client = gateway_client - invoke = map_contract.functions["put"].prepare(key=0x1, value=0x1, max_fee=MAX_FEE) - - # modify selector so that transaction will get rejected - invoke.selector = 0x0123 - transaction = await invoke.invoke() - - with pytest.raises(TransactionRevertedError) as err: - await client.wait_for_tx(transaction.hash) - - if isinstance(client, GatewayClient): - assert "Entry point 0x123 not found in contract" in err.value.message - - -@pytest.mark.asyncio -async def test_error_when_invoking_without_account(gateway_client, map_contract): - contract = await Contract.from_address(map_contract.address, gateway_client) +async def test_error_when_invoking_without_account(client, map_contract): + contract = await Contract.from_address(map_contract.address, client) with pytest.raises( ValueError, @@ -173,10 +151,8 @@ async def test_error_when_invoking_without_account(gateway_client, map_contract) @pytest.mark.asyncio -async def test_error_when_estimating_fee_while_not_using_account( - gateway_client, map_contract -): - contract = await Contract.from_address(map_contract.address, gateway_client) +async def test_error_when_estimating_fee_while_not_using_account(client, map_contract): + contract = await Contract.from_address(map_contract.address, client) with pytest.raises( ValueError, diff --git a/starknet_py/tests/e2e/core/__init__.py b/starknet_py/tests/e2e/core/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/starknet_py/tests/e2e/core/fixtures.py b/starknet_py/tests/e2e/core/fixtures.py deleted file mode 100644 index 1cf50af32..000000000 --- a/starknet_py/tests/e2e/core/fixtures.py +++ /dev/null @@ -1,189 +0,0 @@ -# pylint: disable=redefined-outer-name -import pytest -import pytest_asyncio - -from starknet_py.common import create_compiled_contract -from starknet_py.constants import FEE_CONTRACT_ADDRESS -from starknet_py.contract import Contract -from starknet_py.hash.address import compute_address -from starknet_py.net.account.account import Account -from starknet_py.net.account.account_deployment_result import AccountDeploymentResult -from starknet_py.net.client_models import DeclareTransactionResponse -from starknet_py.net.gateway_client import GatewayClient -from starknet_py.net.models import StarknetChainId -from starknet_py.net.signer.stark_curve_signer import KeyPair -from starknet_py.net.udc_deployer.deployer import Deployer -from starknet_py.tests.e2e.fixtures.constants import ( - CONTRACTS_COMPILED_V0_DIR, - INTEGRATION_ACCOUNT_ADDRESS, - INTEGRATION_ACCOUNT_PRIVATE_KEY, - MAX_FEE, -) -from starknet_py.tests.e2e.fixtures.misc import read_contract -from starknet_py.tests.e2e.utils import _get_random_private_key_unsafe - - -@pytest.fixture(scope="package") -def core_gateway_client(pytestconfig) -> GatewayClient: - net = pytestconfig.getoption("--net") - - return ( - GatewayClient(net="testnet") - if net == "testnet" - else GatewayClient(net="https://external.integration.starknet.io") - ) - - -@pytest.fixture(scope="package") -def core_pre_deployed_account(pytestconfig, core_gateway_client) -> Account: - net = pytestconfig.getoption("--net") - - account_detals = { - "testnet": ( - "0x6D3432AD39755B1B49ECBD896B928FADAA5DE6703CEE0BE4111124C9A327821", - "0x53262B95AE54005C9BE3ECC743008BAB81C0D7DED641FFDC27F253E7E6D2872", - ), - "integration": ( - # because TESTNET and INTEGRATION constants are lambdas - INTEGRATION_ACCOUNT_ADDRESS(), - INTEGRATION_ACCOUNT_PRIVATE_KEY(), - ), - } - - return Account( - address=account_detals[net][0], - client=core_gateway_client, - key_pair=KeyPair.from_private_key(int(account_detals[net][1], 16)), - chain=StarknetChainId.TESTNET, - ) - - -async def declare_contract( - file_name: str, account: Account -) -> DeclareTransactionResponse: - compiled_contract = read_contract(file_name, directory=CONTRACTS_COMPILED_V0_DIR) - - declare_tx = await account.sign_declare_transaction( - compiled_contract, max_fee=MAX_FEE - ) - resp = await account.client.declare(declare_tx) - - return resp - - -@pytest_asyncio.fixture(scope="package") -async def core_declare_account_response( - core_pre_deployed_account, -) -> DeclareTransactionResponse: - resp = await declare_contract( - "account_with_validate_deploy_compiled.json", core_pre_deployed_account - ) - await core_pre_deployed_account.client.wait_for_tx(resp.transaction_hash) - return resp - - -@pytest_asyncio.fixture(scope="package") -async def core_declare_map_response( - core_pre_deployed_account, -) -> DeclareTransactionResponse: - resp = await declare_contract("map_compiled.json", core_pre_deployed_account) - await core_pre_deployed_account.client.wait_for_tx(resp.transaction_hash) - return resp - - -@pytest_asyncio.fixture(scope="package") -async def core_deploy_account_response( - core_declare_account_response, core_fee_contract, core_gateway_client -) -> AccountDeploymentResult: - class_hash = core_declare_account_response.class_hash - private_key = _get_random_private_key_unsafe() - key_pair = KeyPair.from_private_key(private_key) - salt = 1 - - address = compute_address( - class_hash=class_hash, - constructor_calldata=[key_pair.public_key], - salt=salt, - deployer_address=0, - ) - - invoke_resp = await core_fee_contract.functions["transfer"].invoke( - recipient=address, amount=int(1e16), max_fee=MAX_FEE - ) - await invoke_resp.wait_for_acceptance() - - deploy_result = await Account.deploy_account( - address=address, - class_hash=class_hash, - salt=salt, - key_pair=key_pair, - client=core_gateway_client, - chain=StarknetChainId.TESTNET, - max_fee=int(1e16), - ) - - return deploy_result - - -@pytest_asyncio.fixture(scope="package") -async def core_account( - core_deploy_account_response: AccountDeploymentResult, -) -> Account: - await core_deploy_account_response.wait_for_acceptance() - - return core_deploy_account_response.account - - -@pytest.fixture(scope="package") -def core_fee_contract(core_pre_deployed_account: Account) -> Contract: - """ - Returns an instance of the fee contract. It is used to transfer tokens. - """ - abi = [ - { - "inputs": [ - {"name": "recipient", "type": "felt"}, - {"name": "amount", "type": "Uint256"}, - ], - "name": "transfer", - "outputs": [{"name": "success", "type": "felt"}], - "type": "function", - }, - { - "members": [ - {"name": "low", "offset": 0, "type": "felt"}, - {"name": "high", "offset": 1, "type": "felt"}, - ], - "name": "Uint256", - "size": 2, - "type": "struct", - }, - ] - - return Contract( - address=FEE_CONTRACT_ADDRESS, - abi=abi, - provider=core_pre_deployed_account, - ) - - -@pytest_asyncio.fixture(scope="package") -async def core_map_contract( - core_pre_deployed_account: Account, - core_declare_map_response: DeclareTransactionResponse, -): - deployer = Deployer() - call, address = deployer.create_contract_deployment( - core_declare_map_response.class_hash - ) - - resp = await core_pre_deployed_account.execute(call, max_fee=MAX_FEE) - await core_pre_deployed_account.client.wait_for_tx(resp.transaction_hash) - - abi = create_compiled_contract( - compiled_contract=read_contract( - "map_compiled.json", directory=CONTRACTS_COMPILED_V0_DIR - ) - ).abi - - return Contract(address, abi, core_pre_deployed_account) diff --git a/starknet_py/tests/e2e/core/test_core.py b/starknet_py/tests/e2e/core/test_core.py deleted file mode 100644 index 83e1d24f9..000000000 --- a/starknet_py/tests/e2e/core/test_core.py +++ /dev/null @@ -1,108 +0,0 @@ -from typing import cast - -import pytest - -from starknet_py.contract import Contract, PreparedFunctionCall -from starknet_py.net.account.account import Account -from starknet_py.net.account.account_deployment_result import AccountDeploymentResult -from starknet_py.net.client_models import ( - DeclareTransaction, - DeclareTransactionResponse, - EstimatedFee, -) -from starknet_py.tests.e2e.fixtures.constants import MAX_FEE - - -@pytest.mark.asyncio -async def test_declare_account( - core_pre_deployed_account, - core_declare_account_response: DeclareTransactionResponse, -): - await core_pre_deployed_account.client.wait_for_tx( - core_declare_account_response.transaction_hash - ) - - declare_tx = cast( - DeclareTransaction, - await core_pre_deployed_account.client.get_transaction( - core_declare_account_response.transaction_hash - ), - ) - - assert core_declare_account_response.class_hash != 0 - assert core_declare_account_response.class_hash == declare_tx.class_hash - - -@pytest.mark.asyncio -async def test_deploy_account( - core_deploy_account_response: AccountDeploymentResult, -): - await core_deploy_account_response.wait_for_acceptance() - - assert core_deploy_account_response.account.address != 0 - - -@pytest.mark.asyncio -async def test_declare_v1(core_declare_map_response: DeclareTransactionResponse): - assert core_declare_map_response.class_hash != 0 - - -@pytest.mark.asyncio -async def test_declare_v2( - core_pre_deployed_account, sierra_minimal_compiled_contract_and_class_hash -): - ( - compiled_contract, - compiled_class_hash, - ) = sierra_minimal_compiled_contract_and_class_hash - - declare_tx = await core_pre_deployed_account.sign_declare_v2_transaction( - compiled_contract=compiled_contract, - compiled_class_hash=compiled_class_hash, - max_fee=MAX_FEE, - ) - resp = await core_pre_deployed_account.client.declare(declare_tx) - await core_pre_deployed_account.client.wait_for_tx(resp.transaction_hash) - - assert resp.class_hash != 0 - - -@pytest.mark.asyncio -async def test_deployer(core_map_contract: Contract): - assert core_map_contract.address != 0 - - -@pytest.mark.asyncio -async def test_contract(core_map_contract: Contract): - prepared_tx: PreparedFunctionCall = core_map_contract.functions["put"].prepare( - key=10, value=20 - ) - - estimated_fee = await prepared_tx.estimate_fee() - - assert isinstance(estimated_fee, EstimatedFee) - assert estimated_fee.overall_fee > 0 - - resp = await prepared_tx.invoke(max_fee=int(estimated_fee.overall_fee * 1.5)) - await resp.wait_for_acceptance() - - (value,) = await core_map_contract.functions["get"].call(key=10) - - assert value == 20 - - -@pytest.mark.asyncio -async def test_multicall( - core_map_contract: Contract, core_pre_deployed_account: Account -): - calls = [ - core_map_contract.functions["put"].prepare(key=i, value=i * 10) - for i in range(5) - ] - - resp = await core_pre_deployed_account.execute(calls, max_fee=MAX_FEE) - await core_pre_deployed_account.client.wait_for_tx(resp.transaction_hash) - - for i in range(5): - (value,) = await core_map_contract.functions["get"].call(key=i) - assert value == i * 10 diff --git a/starknet_py/tests/e2e/docs/account_creation/test_deploy_prefunded_account.py b/starknet_py/tests/e2e/docs/account_creation/test_deploy_prefunded_account.py index 2f83c86c5..7195c57ab 100644 --- a/starknet_py/tests/e2e/docs/account_creation/test_deploy_prefunded_account.py +++ b/starknet_py/tests/e2e/docs/account_creation/test_deploy_prefunded_account.py @@ -12,9 +12,10 @@ async def test_deploy_prefunded_account( account_with_validate_deploy_class_hash: int, network: str, fee_contract: Contract, - full_node_client: Client, + client: Client, ): # pylint: disable=import-outside-toplevel, too-many-locals + full_node_client_fixture = client # docs: start from starknet_py.hash.address import compute_address from starknet_py.net.account.account import Account @@ -53,7 +54,7 @@ async def test_deploy_prefunded_account( chain = StarknetChainId.TESTNET # docs: end - client = full_node_client + client = full_node_client_fixture chain = chain_from_network(net=network, chain=StarknetChainId.TESTNET) # docs: start diff --git a/starknet_py/tests/e2e/docs/code_examples/test_account.py b/starknet_py/tests/e2e/docs/code_examples/test_account.py index af89cd432..75b49d541 100644 --- a/starknet_py/tests/e2e/docs/code_examples/test_account.py +++ b/starknet_py/tests/e2e/docs/code_examples/test_account.py @@ -8,11 +8,9 @@ from starknet_py.net.account.account import Account from starknet_py.net.client_models import Call from starknet_py.net.full_node_client import FullNodeClient -from starknet_py.net.gateway_client import GatewayClient from starknet_py.net.models import StarknetChainId from starknet_py.net.models.typed_data import TypedData -from starknet_py.net.networks import TESTNET -from starknet_py.net.signer.stark_curve_signer import KeyPair, StarkCurveSigner +from starknet_py.net.signer.stark_curve_signer import KeyPair def test_init(): @@ -23,16 +21,6 @@ def test_init(): key_pair=KeyPair(12, 34), chain=StarknetChainId.TESTNET, ) - # or (not recommended, soon GatewayClient will be removed) - account = Account( - address=0x123, - client=GatewayClient(net=TESTNET), - signer=StarkCurveSigner( - account_address=0x123, - key_pair=KeyPair(12, 34), - chain_id=StarknetChainId.TESTNET, - ), - ) # docs-end: init diff --git a/starknet_py/tests/e2e/docs/code_examples/test_contract.py b/starknet_py/tests/e2e/docs/code_examples/test_contract.py index a1d8e5956..fd386f10e 100644 --- a/starknet_py/tests/e2e/docs/code_examples/test_contract.py +++ b/starknet_py/tests/e2e/docs/code_examples/test_contract.py @@ -4,9 +4,7 @@ from starknet_py.contract import Contract from starknet_py.net.account.account import Account from starknet_py.net.full_node_client import FullNodeClient -from starknet_py.net.gateway_client import GatewayClient from starknet_py.net.models import StarknetChainId -from starknet_py.net.networks import TESTNET from starknet_py.net.signer.stark_curve_signer import KeyPair @@ -29,24 +27,6 @@ def test_init(): chain=StarknetChainId.TESTNET, ), ) - # or (not recommended, soon GatewayClient will be removed) - contract = Contract( - address=0x123, - abi=[ - { - "inputs": [{"name": "amount", "type": "felt"}], - "name": "increase_balance", - "outputs": [], - "type": "function", - }, - ], - provider=Account( - address=0x321, - client=GatewayClient(TESTNET), - key_pair=KeyPair(12, 34), - chain=StarknetChainId.TESTNET, - ), - ) # docs-end: init diff --git a/starknet_py/tests/e2e/docs/code_examples/test_full_node_client.py b/starknet_py/tests/e2e/docs/code_examples/test_full_node_client.py index 2d96445f3..9db3ac3cb 100644 --- a/starknet_py/tests/e2e/docs/code_examples/test_full_node_client.py +++ b/starknet_py/tests/e2e/docs/code_examples/test_full_node_client.py @@ -10,44 +10,44 @@ def test_init(): # docs-start: init - full_node_client = FullNodeClient(node_url="https://your.node.url") + client = FullNodeClient(node_url="https://your.node.url") # docs-end: init @pytest.mark.asyncio -async def test_get_block(full_node_client): +async def test_get_block(client): # docs-start: get_block - block = await full_node_client.get_block(block_number="latest") - block = await full_node_client.get_block(block_number=0) + block = await client.get_block(block_number="latest") + block = await client.get_block(block_number=0) # or - block = await full_node_client.get_block(block_hash="0x0") + block = await client.get_block(block_hash="0x0") # docs-end: get_block @pytest.mark.asyncio async def test_get_state_update( - full_node_client, declare_transaction_hash + client, declare_transaction_hash ): # pylint: disable=unused-argument # parameter `declare_transaction_hash` is needed because devnet blockchain is empty without it # and methods return invalid data # docs-start: get_state_update - state_update = await full_node_client.get_state_update(block_number="latest") - state_update = await full_node_client.get_state_update(block_number=1) + state_update = await client.get_state_update(block_number="latest") + state_update = await client.get_state_update(block_number=1) # or block_hash = "0x0" # docs-end: get_state_update block_hash = state_update.block_hash # docs-start: get_state_update - state_update = await full_node_client.get_state_update(block_hash=block_hash) + state_update = await client.get_state_update(block_hash=block_hash) # docs-end: get_state_update @pytest.mark.asyncio -async def test_get_storage_at(full_node_client, map_contract): +async def test_get_storage_at(client, map_contract): address = map_contract.address # docs-start: get_storage_at - storage_value = await full_node_client.get_storage_at( + storage_value = await client.get_storage_at( contract_address=address, key=get_storage_var_address("storage_var name"), block_number="latest", @@ -56,42 +56,39 @@ async def test_get_storage_at(full_node_client, map_contract): @pytest.mark.asyncio -async def test_get_transaction(full_node_client, declare_transaction_hash): +async def test_get_transaction(client, declare_transaction_hash): # docs-start: get_transaction transaction_hash = 0x1 or 1 or "0x1" # docs-end: get_transaction transaction_hash = declare_transaction_hash # docs-start: get_transaction - transaction = await full_node_client.get_transaction(tx_hash=transaction_hash) + transaction = await client.get_transaction(tx_hash=transaction_hash) # docs-end: get_transaction @pytest.mark.asyncio -async def test_get_transaction_receipt(full_node_client, declare_transaction_hash): +async def test_get_transaction_receipt(client, declare_transaction_hash): transaction_hash = declare_transaction_hash # docs-start: get_transaction_receipt - transaction_receipt = await full_node_client.get_transaction_receipt( - tx_hash=transaction_hash - ) + transaction_receipt = await client.get_transaction_receipt(tx_hash=transaction_hash) # docs-end: get_transaction_receipt @pytest.mark.asyncio -async def test_estimate_fee(full_node_account, deploy_account_transaction): - full_node_client = full_node_account.client +async def test_estimate_fee(client, deploy_account_transaction): transaction = deploy_account_transaction # docs-start: estimate_fee # a single transaction - estimated_fee = await full_node_client.estimate_fee(tx=transaction) + estimated_fee = await client.estimate_fee(tx=transaction) # or a list of transactions - estimated_fee = await full_node_client.estimate_fee(tx=[transaction]) + estimated_fee = await client.estimate_fee(tx=[transaction]) # docs-end: estimate_fee @pytest.mark.asyncio -async def test_call_contract(full_node_client, contract_address): +async def test_call_contract(client, contract_address): # docs-start: call_contract - response = await full_node_client.call_contract( + response = await client.call_contract( call=Call( to_addr=contract_address, selector=get_selector_from_name("increase_balance"), @@ -103,81 +100,81 @@ async def test_call_contract(full_node_client, contract_address): @pytest.mark.asyncio -async def test_get_class_hash_at(full_node_client, contract_address): +async def test_get_class_hash_at(client, contract_address): # docs-start: get_class_hash_at address = 0x1 or 1 or "0x1" # docs-end: get_class_hash_at address = contract_address # docs-start: get_class_hash_at - class_hash = await full_node_client.get_class_hash_at( + class_hash = await client.get_class_hash_at( contract_address=address, block_number="latest" ) # docs-end: get_class_hash_at @pytest.mark.asyncio -async def test_get_class_by_hash(full_node_client, class_hash): +async def test_get_class_by_hash(client, class_hash): # docs-start: get_class_by_hash hash_ = 0x1 or 1 or "0x1" # docs-end: get_class_by_hash hash_ = class_hash # docs-start: get_class_by_hash - contract_class = await full_node_client.get_class_by_hash(class_hash=hash_) + contract_class = await client.get_class_by_hash(class_hash=hash_) # docs-end: get_class_by_hash @pytest.mark.asyncio -async def test_get_transaction_by_block_id(full_node_client): +async def test_get_transaction_by_block_id(client): # docs-start: get_transaction_by_block_id - transaction = await full_node_client.get_transaction_by_block_id( + transaction = await client.get_transaction_by_block_id( index=0, block_number="latest" ) # docs-end: get_transaction_by_block_id @pytest.mark.asyncio -async def test_get_block_transaction_count(full_node_client): +async def test_get_block_transaction_count(client): # docs-start: get_block_transaction_count - num_of_transactions = await full_node_client.get_block_transaction_count( + num_of_transactions = await client.get_block_transaction_count( block_number="latest" ) # docs-end: get_block_transaction_count @pytest.mark.asyncio -async def test_get_class_at(full_node_client, contract_address): +async def test_get_class_at(client, contract_address): # docs-start: get_class_at address = 0x1 or 1 or "0x1" # docs-end: get_class_at address = contract_address # docs-start: get_class_at - contract_class = await full_node_client.get_class_at( + contract_class = await client.get_class_at( contract_address=address, block_number="latest" ) # docs-end: get_class_at @pytest.mark.asyncio -async def test_get_contract_nonce(full_node_client, contract_address): +async def test_get_contract_nonce(client, contract_address): # docs-start: get_contract_nonce address = 0x1 or 1 or "0x1" # docs-end: get_contract_nonce address = contract_address # docs-start: get_contract_nonce - nonce = await full_node_client.get_contract_nonce( + nonce = await client.get_contract_nonce( contract_address=address, block_number="latest" ) # docs-end: get_contract_nonce @pytest.mark.asyncio -async def test_get_events(full_node_client, contract_address): +async def test_get_events(client, contract_address): # docs-start: get_events address = 0x1 or 1 or "0x1" # docs-end: get_events address = contract_address # docs-start: get_events - events_response = await full_node_client.get_events( + events_response = await client.get_events( address=address, keys=[[1, 2], [], [3]], from_block_number=0, @@ -193,10 +190,10 @@ async def test_get_events(full_node_client, contract_address): reason="Passing devnet client without implemented methods - test simply for a code example." ) @pytest.mark.asyncio -async def test_trace_block_transactions(full_node_client): +async def test_trace_block_transactions(client): # docs-start: trace_block_transactions block_number = 800002 - block_transaction_traces = await full_node_client.trace_block_transactions( + block_transaction_traces = await client.trace_block_transactions( block_number=block_number ) # docs-end: trace_block_transactions @@ -207,7 +204,7 @@ async def test_trace_block_transactions(full_node_client): reason="Passing devnet client without implemented methods - test simply for a code example." ) @pytest.mark.asyncio -async def test_trace_transaction(full_node_client): +async def test_trace_transaction(client): # docs-start: trace_transaction transaction_hash = "0x123" # docs-end: trace_transaction @@ -215,9 +212,7 @@ async def test_trace_transaction(full_node_client): "0x31e9adddefb28fab4d2ef9a6907e5805f5f793f5198618119a5347e6fc4af57" ) # docs-start: trace_transaction - transaction_trace = await full_node_client.trace_transaction( - tx_hash=transaction_hash - ) + transaction_trace = await client.trace_transaction(tx_hash=transaction_hash) # docs-end: trace_transaction diff --git a/starknet_py/tests/e2e/docs/code_examples/test_gateway_client.py b/starknet_py/tests/e2e/docs/code_examples/test_gateway_client.py deleted file mode 100644 index 4035f8b3c..000000000 --- a/starknet_py/tests/e2e/docs/code_examples/test_gateway_client.py +++ /dev/null @@ -1,196 +0,0 @@ -# pylint: disable=unused-variable -import pytest - -from starknet_py.hash.selector import get_selector_from_name -from starknet_py.hash.storage import get_storage_var_address -from starknet_py.net.client_models import Call -from starknet_py.net.gateway_client import GatewayClient -from starknet_py.net.networks import TESTNET -from starknet_py.tests.e2e.fixtures.constants import MAX_FEE - - -def test_init(): - # docs-start: init - gateway_client = GatewayClient(net=TESTNET) - # or when using custom urls - gateway_client = GatewayClient( - net={ - "feeder_gateway_url": "https://alpha4.starknet.io/feeder_gateway", - "gateway_url": "https://alpha4.starknet.io/gateway", - } - ) - # docs-end: init - - -# TODO (#1154): remove line below -@pytest.mark.xfail( - reason="0.12.2 returns Felts in state_root, devnet returns NonPrefixedHex", -) -@pytest.mark.asyncio -async def test_get_block(gateway_client): - # docs-start: get_block - block = await gateway_client.get_block(block_number="latest") - block = await gateway_client.get_block(block_number=0) - # or - block = await gateway_client.get_block(block_hash="0x0") - # docs-end: get_block - - -@pytest.mark.asyncio -async def test_get_block_traces(gateway_client): - # docs-start: get_block_traces - block_traces = await gateway_client.trace_block_transactions(block_number="latest") - block_traces = await gateway_client.trace_block_transactions(block_number=0) - # or - block_traces = await gateway_client.trace_block_transactions(block_hash="0x0") - # docs-end: get_block_traces - - -@pytest.mark.xfail(reason="Devnet doesn't support `include_block` parameter") -@pytest.mark.asyncio -async def test_get_state_update(gateway_client): - # docs-start: get_state_update - state_update = await gateway_client.get_state_update(block_number="latest") - state_update = await gateway_client.get_state_update(block_number=0) - # or - state_update = await gateway_client.get_state_update(block_hash="0x0") - # You can also return it together with the corresponding block - state_update = await gateway_client.get_state_update( - block_number=0, include_block=True - ) - # docs-end: get_state_update - - -@pytest.mark.asyncio -async def test_get_storage_at(gateway_client, map_contract): - address = map_contract.address - # docs-start: get_storage_at - storage_value = await gateway_client.get_storage_at( - contract_address=address, - key=get_storage_var_address("name_of_storage_var"), - block_number="latest", - ) - # docs-end: get_storage_at - - -@pytest.mark.asyncio -async def test_get_transaction(gateway_client, declare_transaction_hash): - # docs-start: get_transaction - transaction_hash = 0x1 or 1 or "0x1" - # docs-end: get_transaction - transaction_hash = declare_transaction_hash - # docs-start: get_transaction - transaction = await gateway_client.get_transaction(tx_hash=transaction_hash) - # docs-end: get_transaction - - -@pytest.mark.asyncio -async def test_get_transaction_receipt(gateway_client, declare_transaction_hash): - transaction_hash = declare_transaction_hash - # docs-start: get_transaction_receipt - transaction_receipt = await gateway_client.get_transaction_receipt( - tx_hash=transaction_hash - ) - # docs-end: get_transaction_receipt - - -@pytest.mark.asyncio -async def test_estimate_fee(gateway_account, deploy_account_transaction): - gateway_client = gateway_account.client - transaction = deploy_account_transaction - # docs-start: estimate_fee - estimated_fee = await gateway_client.estimate_fee(tx=transaction) - # docs-end: estimate_fee - - -@pytest.mark.asyncio -async def test_estimate_fee_bulk( - gateway_account, deploy_account_transaction, map_compiled_contract -): - gateway_client = gateway_account.client - transaction1 = deploy_account_transaction - transaction2 = await gateway_account.sign_declare_transaction( - compiled_contract=map_compiled_contract, max_fee=MAX_FEE - ) - # docs-start: estimate_fee_bulk - list_of_estimated_fees = await gateway_client.estimate_fee_bulk( - transactions=[transaction1, transaction2] - ) - # docs-end: estimate_fee_bulk - - -@pytest.mark.asyncio -async def test_call_contract(gateway_client, contract_address): - # docs-start: call_contract - response = await gateway_client.call_contract( - call=Call( - to_addr=contract_address, - selector=get_selector_from_name("increase_balance"), - calldata=[123], - ), - block_number="latest", - ) - # docs-end: call_contract - - -@pytest.mark.asyncio -async def test_get_class_hash_at(gateway_client, contract_address): - # docs-start: get_class_hash_at - address = 0x1 or 1 or "0x1" - # docs-end: get_class_hash_at - address = contract_address - # docs-start: get_class_hash_at - class_hash = await gateway_client.get_class_hash_at( - contract_address=address, block_number="latest" - ) - # docs-end: get_class_hash_at - - -@pytest.mark.asyncio -async def test_get_class_by_hash(gateway_client, class_hash): - # docs-start: get_class_by_hash - hash_ = 0x1 or 1 or "0x1" - # docs-end: get_class_by_hash - hash_ = class_hash - # docs-start: get_class_by_hash - contract_class = await gateway_client.get_class_by_hash(class_hash=hash_) - # docs-end: get_class_by_hash - - -@pytest.mark.asyncio -async def test_get_transaction_status(gateway_client, invoke_transaction_hash): - # docs-start: get_transaction_status - transaction_hash = 0x1 or 1 or "0x1" - # docs-end: get_transaction_status - transaction_hash = invoke_transaction_hash - # docs-start: get_transaction_status - status_response = await gateway_client.get_transaction_status( - tx_hash=transaction_hash - ) - # docs-end: get_transaction_status - - -@pytest.mark.asyncio -async def test_get_code(gateway_client, contract_address): - # docs-start: get_code - address = 0x1 or 1 or "0x1" - # docs-end: get_code - address = contract_address - # docs-start: get_code - code = await gateway_client.get_code( - contract_address=address, block_number="latest" - ) - # docs-end: get_code - - -@pytest.mark.asyncio -async def test_get_contract_nonce(gateway_client, contract_address): - # docs-start: get_contract_nonce - address = 0x1 or 1 or "0x1" - # docs-end: get_contract_nonce - address = contract_address - # docs-start: get_contract_nonce - nonce = await gateway_client.get_contract_nonce( - contract_address=address, block_number="latest" - ) - # docs-end: get_contract_nonce diff --git a/starknet_py/tests/e2e/docs/guide/test_account_sign_without_execute.py b/starknet_py/tests/e2e/docs/guide/test_account_sign_without_execute.py index 8b594bf4e..6311bca9e 100644 --- a/starknet_py/tests/e2e/docs/guide/test_account_sign_without_execute.py +++ b/starknet_py/tests/e2e/docs/guide/test_account_sign_without_execute.py @@ -19,8 +19,6 @@ async def test_account_sign_without_execute(account, map_compiled_contract): # Create a signed Invoke transaction call = Call(to_addr=address, selector=selector, calldata=calldata) invoke_transaction = await account.sign_invoke_transaction(call, max_fee=max_fee) - # Or if you're using Cairo1 account with new calldata encoding - invoke_transaction = await account.sign_invoke_transaction(call, max_fee=max_fee) # Create a signed Declare transaction declare_transaction = await account.sign_declare_transaction( diff --git a/starknet_py/tests/e2e/docs/guide/test_cairo1_contract.py b/starknet_py/tests/e2e/docs/guide/test_cairo1_contract.py index dd7b3a0e7..59b1da49d 100644 --- a/starknet_py/tests/e2e/docs/guide/test_cairo1_contract.py +++ b/starknet_py/tests/e2e/docs/guide/test_cairo1_contract.py @@ -2,7 +2,7 @@ import pytest -from starknet_py.net.client_models import CasmClass +from starknet_py.net.client_models import SierraContractClass from starknet_py.net.udc_deployer.deployer import _get_random_salt from starknet_py.tests.e2e.fixtures.constants import MAX_FEE from starknet_py.tests.e2e.fixtures.misc import read_contract @@ -10,9 +10,7 @@ @pytest.mark.asyncio async def test_cairo1_contract( - account, - sierra_minimal_compiled_contract_and_class_hash, - gateway_client, + account, sierra_minimal_compiled_contract_and_class_hash ): # pylint: disable=too-many-locals # pylint: disable=import-outside-toplevel @@ -85,7 +83,7 @@ async def test_cairo1_contract( assert isinstance(contract_deployment.address, int) assert contract_deployment.address != 0 - compiled_class = await gateway_client.get_compiled_class_by_class_hash( + compiled_class = await account.client.get_class_by_hash( class_hash=sierra_class_hash ) - assert isinstance(compiled_class, CasmClass) + assert isinstance(compiled_class, SierraContractClass) diff --git a/starknet_py/tests/e2e/docs/guide/test_custom_nonce.py b/starknet_py/tests/e2e/docs/guide/test_custom_nonce.py index b4334d1ce..45c6e2801 100644 --- a/starknet_py/tests/e2e/docs/guide/test_custom_nonce.py +++ b/starknet_py/tests/e2e/docs/guide/test_custom_nonce.py @@ -6,11 +6,11 @@ @pytest.mark.asyncio -async def test_custom_nonce(full_node_account): +async def test_custom_nonce(account): # pylint: disable=import-outside-toplevel - client = full_node_account.client - address = full_node_account.address - private_key = full_node_account.signer.key_pair.private_key + client = account.client + address = account.address + private_key = account.signer.key_pair.private_key # docs: start from starknet_py.net.account.account import Account diff --git a/starknet_py/tests/e2e/docs/guide/test_executing_transactions.py b/starknet_py/tests/e2e/docs/guide/test_executing_transactions.py index ae960dd8e..2dc0892df 100644 --- a/starknet_py/tests/e2e/docs/guide/test_executing_transactions.py +++ b/starknet_py/tests/e2e/docs/guide/test_executing_transactions.py @@ -4,8 +4,8 @@ @pytest.mark.asyncio async def test_executing_transactions(account, map_contract): address = map_contract.address - # docs: start # pylint: disable=import-outside-toplevel + # docs: start from starknet_py.hash.selector import get_selector_from_name from starknet_py.net.client_models import Call diff --git a/starknet_py/tests/e2e/docs/guide/test_full_node_client.py b/starknet_py/tests/e2e/docs/guide/test_full_node_client.py index 680996277..82817111f 100644 --- a/starknet_py/tests/e2e/docs/guide/test_full_node_client.py +++ b/starknet_py/tests/e2e/docs/guide/test_full_node_client.py @@ -2,23 +2,22 @@ @pytest.mark.asyncio -async def test_full_node_client(full_node_client, map_contract): +async def test_full_node_client(client, map_contract): # pylint: disable=import-outside-toplevel, unused-variable - full_node_client_fixture = full_node_client - + full_node_client_fixture = client # docs: start from starknet_py.net.full_node_client import FullNodeClient node_url = "https://your.node.url" - full_node_client = FullNodeClient(node_url=node_url) + client = FullNodeClient(node_url=node_url) # docs: end await map_contract.functions["put"].prepare(key=10, value=10).invoke( max_fee=int(1e20) ) - full_node_client = full_node_client_fixture + client = full_node_client_fixture # docs: start - call_result = await full_node_client.get_block(block_number=0) + call_result = await client.get_block(block_number=0) # docs: end diff --git a/starknet_py/tests/e2e/docs/quickstart/test_synchronous_full_node_client.py b/starknet_py/tests/e2e/docs/quickstart/test_synchronous_full_node_client.py index 781e40302..4e5c8be57 100644 --- a/starknet_py/tests/e2e/docs/quickstart/test_synchronous_full_node_client.py +++ b/starknet_py/tests/e2e/docs/quickstart/test_synchronous_full_node_client.py @@ -2,19 +2,19 @@ def test_synchronous_full_node_client( - full_node_client, + client, map_contract_declare_hash, # pylint: disable=unused-argument ): # pylint: disable=unused-variable - fixture_client = full_node_client + fixture_client = client # docs: start node_url = "https://your.node.url" - full_node_client = FullNodeClient(node_url=node_url) + client = FullNodeClient(node_url=node_url) # docs: end - full_node_client = fixture_client + client = fixture_client # docs: start - call_result = full_node_client.get_block_sync(block_number=1) + call_result = client.get_block_sync(block_number=1) # docs: end diff --git a/starknet_py/tests/e2e/docs/quickstart/test_synchronous_gateway_client.py b/starknet_py/tests/e2e/docs/quickstart/test_synchronous_gateway_client.py deleted file mode 100644 index b7cc41188..000000000 --- a/starknet_py/tests/e2e/docs/quickstart/test_synchronous_gateway_client.py +++ /dev/null @@ -1,12 +0,0 @@ -from starknet_py.net.gateway_client import GatewayClient -from starknet_py.net.networks import TESTNET - - -def test_synchronous_gateway_client(): - # pylint: disable=no-member, unused-variable - # docs: start - synchronous_testnet_client = GatewayClient(TESTNET) - call_result = synchronous_testnet_client.get_block_sync( - "0x495c670c53e4e76d08292524299de3ba078348d861dd7b2c7cc4933dbc27943" - ) - # docs: end diff --git a/starknet_py/tests/e2e/docs/quickstart/test_using_full_node_client.py b/starknet_py/tests/e2e/docs/quickstart/test_using_full_node_client.py index d99d9d658..89ba993c4 100644 --- a/starknet_py/tests/e2e/docs/quickstart/test_using_full_node_client.py +++ b/starknet_py/tests/e2e/docs/quickstart/test_using_full_node_client.py @@ -5,25 +5,25 @@ @pytest.mark.run_on_devnet @pytest.mark.asyncio -async def test_using_full_node_client(full_node_client, map_contract): +async def test_using_full_node_client(client, map_contract): # pylint: disable=import-outside-toplevel, unused-variable - full_node_client_fixture = full_node_client + full_node_client_fixture = client # docs: start from starknet_py.net.full_node_client import FullNodeClient node_url = "https://your.node.url" - full_node_client = FullNodeClient(node_url=node_url) + client = FullNodeClient(node_url=node_url) # docs: end await map_contract.functions["put"].prepare(key=10, value=10).invoke( max_fee=int(1e20) ) - full_node_client = full_node_client_fixture + client = full_node_client_fixture # docs: start - call_result = await full_node_client.get_block(block_number=1) + call_result = await client.get_block(block_number=1) # docs: end assert isinstance(call_result, StarknetBlock) assert len(call_result.transactions) == 1 diff --git a/starknet_py/tests/e2e/docs/quickstart/test_using_gateway_client.py b/starknet_py/tests/e2e/docs/quickstart/test_using_gateway_client.py deleted file mode 100644 index 5b216f642..000000000 --- a/starknet_py/tests/e2e/docs/quickstart/test_using_gateway_client.py +++ /dev/null @@ -1,26 +0,0 @@ -import pytest - - -@pytest.mark.asyncio -async def test_using_gateway_client(): - # pylint: disable=import-outside-toplevel, unused-variable - # docs: start - from starknet_py.net.gateway_client import GatewayClient - from starknet_py.net.networks import MAINNET, TESTNET - - # Use testnet for playing with Starknet - testnet_client = GatewayClient(TESTNET) - # or - testnet_client = GatewayClient("testnet") - - mainnet_client = GatewayClient(MAINNET) - # or - mainnet_client = GatewayClient("mainnet") - - # Local network - local_network_client = GatewayClient("http://localhost:5000") - - call_result = await testnet_client.get_block( - "0x495c670c53e4e76d08292524299de3ba078348d861dd7b2c7cc4933dbc27943" - ) - # docs: end diff --git a/starknet_py/tests/e2e/fixtures/accounts.py b/starknet_py/tests/e2e/fixtures/accounts.py index 1bf787531..1728ab3f6 100644 --- a/starknet_py/tests/e2e/fixtures/accounts.py +++ b/starknet_py/tests/e2e/fixtures/accounts.py @@ -1,8 +1,7 @@ # pylint: disable=redefined-outer-name -import sys from dataclasses import dataclass -from typing import List, Optional, Tuple +from typing import Optional, Tuple import pytest import pytest_asyncio @@ -12,8 +11,7 @@ from starknet_py.net.account.account import Account from starknet_py.net.account.base_account import BaseAccount from starknet_py.net.full_node_client import FullNodeClient -from starknet_py.net.gateway_client import GatewayClient -from starknet_py.net.http_client import GatewayHttpClient +from starknet_py.net.http_client import HttpMethod, RpcHttpClient from starknet_py.net.models import StarknetChainId from starknet_py.net.signer.stark_curve_signer import KeyPair from starknet_py.tests.e2e.fixtures.constants import ( @@ -40,6 +38,7 @@ async def devnet_account_details( """ Deploys an Account and adds fee tokens to its balance (only on devnet). """ + # TODO (#1179): consider using predeployed account with initial balance private_key = _get_random_private_key_unsafe() key_pair = KeyPair.from_private_key(private_key) salt = 1 @@ -51,9 +50,10 @@ async def devnet_account_details( deployer_address=0, ) - http_client = GatewayHttpClient(network) - await http_client.post( - method_name="mint", + http_client = RpcHttpClient(network) + await http_client.request( + http_method=HttpMethod.POST, + address=f"{network}/mint", payload={ "address": hex(address), "amount": int(1e30), @@ -65,7 +65,7 @@ async def devnet_account_details( key_pair=key_pair, salt=salt, class_hash=class_hash, - network=network, + client=account.client, ) account = Account( @@ -111,26 +111,9 @@ async def address_and_private_key( return exact_account_details[0](), exact_account_details[1]() -@pytest.fixture(scope="package") -def gateway_account( - address_and_private_key: Tuple[str, str], gateway_client: GatewayClient -) -> Account: - """ - Returns a new Account created with GatewayClient. - """ - address, private_key = address_and_private_key - - return Account( - address=address, - client=gateway_client, - key_pair=KeyPair.from_private_key(int(private_key, 0)), - chain=StarknetChainId.TESTNET, - ) - - -@pytest.fixture(scope="package") +@pytest.fixture(name="account", scope="package") def full_node_account( - address_and_private_key: Tuple[str, str], full_node_client: FullNodeClient + address_and_private_key: Tuple[str, str], client: FullNodeClient ) -> BaseAccount: """ Returns a new Account created with FullNodeClient. @@ -139,37 +122,12 @@ def full_node_account( return Account( address=address, - client=full_node_client, + client=client, key_pair=KeyPair.from_private_key(int(private_key, 0)), chain=StarknetChainId.TESTNET, ) -def net_to_base_accounts() -> List[str]: - if "--client=gateway" in sys.argv: - return ["gateway_account"] - if "--client=full_node" in sys.argv: - return ["full_node_account"] - - accounts = ["gateway_account"] - nets = ["--net=integration", "--net=testnet", "testnet", "integration"] - - if set(nets).isdisjoint(sys.argv): - accounts.extend(["full_node_account"]) - return accounts - - -@pytest.fixture( - scope="package", - params=net_to_base_accounts(), -) -def account(request) -> BaseAccount: - """ - This parametrized fixture returns all new Accounts, one by one. - """ - return request.getfixturevalue(request.param) - - @dataclass class AccountToBeDeployedDetailsFactory: class_hash: int @@ -232,7 +190,7 @@ def pre_deployed_account_with_validate_deploy( return Account( address=address, - client=GatewayClient(net=network), + client=FullNodeClient(node_url=network + "/rpc"), key_pair=KeyPair.from_private_key(int(private_key, 16)), chain=StarknetChainId.TESTNET, ) @@ -242,7 +200,7 @@ def pre_deployed_account_with_validate_deploy( async def argent_cairo1_account( argent_cairo1_account_class_hash, deploy_account_details_factory: AccountToBeDeployedDetailsFactory, - full_node_client, + client, ) -> BaseAccount: address, key_pair, salt, class_hash = await deploy_account_details_factory.get( class_hash=argent_cairo1_account_class_hash, @@ -253,7 +211,7 @@ async def argent_cairo1_account( class_hash=class_hash, salt=salt, key_pair=key_pair, - client=full_node_client, + client=client, constructor_calldata=[key_pair.public_key, 0], chain=StarknetChainId.TESTNET, max_fee=int(1e16), diff --git a/starknet_py/tests/e2e/fixtures/clients.py b/starknet_py/tests/e2e/fixtures/clients.py index dab831b25..3d0584d0a 100644 --- a/starknet_py/tests/e2e/fixtures/clients.py +++ b/starknet_py/tests/e2e/fixtures/clients.py @@ -1,52 +1,8 @@ -import sys -from typing import List - import pytest -from starknet_py.net.client import Client from starknet_py.net.full_node_client import FullNodeClient -from starknet_py.net.gateway_client import GatewayClient - - -@pytest.fixture(name="gateway_client", scope="package") -def create_gateway_client(network: str) -> GatewayClient: - """ - Creates and returns GatewayClient. - """ - return GatewayClient(net=network) -@pytest.fixture(name="full_node_client", scope="package") +@pytest.fixture(name="client", scope="package") def create_full_node_client(network: str) -> FullNodeClient: - """ - Creates and returns FullNodeClient. - """ return FullNodeClient(node_url=network + "/rpc") - - -def net_to_clients() -> List[str]: - """ - Return client fixture names based on network in sys.argv. - """ - if "--client=gateway" in sys.argv: - return ["gateway_client"] - if "--client=full_node" in sys.argv: - return ["full_node_client"] - - clients = ["gateway_client"] - nets = ["--net=integration", "--net=testnet", "testnet", "integration"] - - if set(nets).isdisjoint(sys.argv): - clients.append("full_node_client") - return clients - - -@pytest.fixture( - scope="package", - params=net_to_clients(), -) -def client(request) -> Client: - """ - Returns Client instances. - """ - return request.getfixturevalue(request.param) diff --git a/starknet_py/tests/e2e/fixtures/constants.py b/starknet_py/tests/e2e/fixtures/constants.py index 40b959ff7..b2d544172 100644 --- a/starknet_py/tests/e2e/fixtures/constants.py +++ b/starknet_py/tests/e2e/fixtures/constants.py @@ -29,6 +29,10 @@ def _get_env_lambda(env_name): TESTNET_RPC_URL = _get_env_lambda("TESTNET_RPC_URL") +EMPTY_CONTRACT_ADDRESS_TESTNET = ( + "0x01de0e8ec5303c4624b96733bed7e4261724df4aecedae6305efa35931a4f0e6" +) + # -------------------------------- INTEGRATION --------------------------------- INTEGRATION_ACCOUNT_PRIVATE_KEY = _get_env_lambda("INTEGRATION_ACCOUNT_PRIVATE_KEY") @@ -37,13 +41,11 @@ def _get_env_lambda(env_name): INTEGRATION_RPC_URL = _get_env_lambda("INTEGRATION_RPC_URL") -INTEGRATION_GATEWAY_URL = "https://external.integration.starknet.io" - -PREDEPLOYED_EMPTY_CONTRACT_ADDRESS = ( +EMPTY_CONTRACT_ADDRESS_INTEGRATION = ( "0x0751cb46C364E912b6CB9221A857D8f90B1F6995A0e902997df774631432970E" ) -PREDEPLOYED_MAP_CONTRACT_ADDRESS = ( +MAP_CONTRACT_ADDRESS_INTEGRATION = ( "0x05cd21d6b3952a869fda11fa9a5bd2657bd68080d3da255655ded47a81c8bd53" ) diff --git a/starknet_py/tests/e2e/fixtures/contracts.py b/starknet_py/tests/e2e/fixtures/contracts.py index 668d502bc..a6284cc67 100644 --- a/starknet_py/tests/e2e/fixtures/contracts.py +++ b/starknet_py/tests/e2e/fixtures/contracts.py @@ -140,14 +140,14 @@ async def deploy_v1_contract( @pytest_asyncio.fixture(scope="package") async def deployed_balance_contract( - gateway_account: BaseAccount, + account: BaseAccount, balance_contract: str, ) -> Contract: """ Declares, deploys a new balance contract and returns its instance. """ declare_result = await Contract.declare( - account=gateway_account, + account=account, compiled_contract=balance_contract, max_fee=int(1e16), ) @@ -161,7 +161,7 @@ async def deployed_balance_contract( @pytest_asyncio.fixture(scope="package") async def map_contract( - gateway_account: BaseAccount, + account: BaseAccount, map_compiled_contract: str, map_class_hash: int, ) -> Contract: @@ -169,16 +169,16 @@ async def map_contract( Deploys map contract and returns its instance. """ abi = create_compiled_contract(compiled_contract=map_compiled_contract).abi - return await deploy_contract(gateway_account, map_class_hash, abi) + return await deploy_contract(account, map_class_hash, abi) @pytest_asyncio.fixture(scope="package") async def map_contract_declare_hash( - full_node_account: BaseAccount, + account: BaseAccount, map_compiled_contract: str, ): declare_result = await Contract.declare( - account=full_node_account, + account=account, compiled_contract=map_compiled_contract, max_fee=MAX_FEE, ) @@ -188,7 +188,7 @@ async def map_contract_declare_hash( @pytest_asyncio.fixture(scope="function") async def simple_storage_with_event_contract( - gateway_account: BaseAccount, + account: BaseAccount, simple_storage_with_event_compiled_contract: str, simple_storage_with_event_class_hash: int, ) -> Contract: @@ -198,14 +198,12 @@ async def simple_storage_with_event_contract( abi = create_compiled_contract( compiled_contract=simple_storage_with_event_compiled_contract ).abi - return await deploy_contract( - gateway_account, simple_storage_with_event_class_hash, abi - ) + return await deploy_contract(account, simple_storage_with_event_class_hash, abi) @pytest_asyncio.fixture(name="erc20_contract", scope="package") async def deploy_erc20_contract( - gateway_account: BaseAccount, + account: BaseAccount, erc20_compiled_contract: str, erc20_class_hash: int, ) -> Contract: @@ -213,11 +211,11 @@ async def deploy_erc20_contract( Deploys erc20 contract and returns its instance. """ abi = create_compiled_contract(compiled_contract=erc20_compiled_contract).abi - return await deploy_contract(gateway_account, erc20_class_hash, abi) + return await deploy_contract(account, erc20_class_hash, abi) @pytest.fixture(scope="package") -def fee_contract(gateway_account: BaseAccount) -> Contract: +def fee_contract(account: BaseAccount) -> Contract: """ Returns an instance of the fee contract. It is used to transfer tokens. """ @@ -245,7 +243,7 @@ def fee_contract(gateway_account: BaseAccount) -> Contract: return Contract( address=FEE_CONTRACT_ADDRESS, abi=abi, - provider=gateway_account, + provider=account, ) @@ -324,50 +322,46 @@ async def argent_cairo1_account_class_hash( @pytest_asyncio.fixture(scope="package") -async def map_class_hash( - gateway_account: BaseAccount, map_compiled_contract: str -) -> int: +async def map_class_hash(account: BaseAccount, map_compiled_contract: str) -> int: """ Returns class_hash of the map.cairo. """ - declare = await gateway_account.sign_declare_transaction( + declare = await account.sign_declare_transaction( compiled_contract=map_compiled_contract, max_fee=int(1e16), ) - res = await gateway_account.client.declare(declare) - await gateway_account.client.wait_for_tx(res.transaction_hash) + res = await account.client.declare(declare) + await account.client.wait_for_tx(res.transaction_hash) return res.class_hash @pytest_asyncio.fixture(scope="package") async def simple_storage_with_event_class_hash( - gateway_account: BaseAccount, simple_storage_with_event_compiled_contract: str + account: BaseAccount, simple_storage_with_event_compiled_contract: str ): """ Returns class_hash of the simple_storage_with_event.cairo """ - declare = await gateway_account.sign_declare_transaction( + declare = await account.sign_declare_transaction( compiled_contract=simple_storage_with_event_compiled_contract, max_fee=int(1e16), ) - res = await gateway_account.client.declare(declare) - await gateway_account.client.wait_for_tx(res.transaction_hash) + res = await account.client.declare(declare) + await account.client.wait_for_tx(res.transaction_hash) return res.class_hash @pytest_asyncio.fixture(scope="package") -async def erc20_class_hash( - gateway_account: BaseAccount, erc20_compiled_contract: str -) -> int: +async def erc20_class_hash(account: BaseAccount, erc20_compiled_contract: str) -> int: """ Returns class_hash of the erc20.cairo. """ - declare = await gateway_account.sign_declare_transaction( + declare = await account.sign_declare_transaction( compiled_contract=erc20_compiled_contract, max_fee=int(1e16), ) - res = await gateway_account.client.declare(declare) - await gateway_account.client.wait_for_tx(res.transaction_hash) + res = await account.client.declare(declare) + await account.client.wait_for_tx(res.transaction_hash) return res.class_hash @@ -403,15 +397,15 @@ def constructor_with_arguments_compiled() -> str: @pytest_asyncio.fixture(scope="package") async def constructor_with_arguments_class_hash( - gateway_account: BaseAccount, constructor_with_arguments_compiled + account: BaseAccount, constructor_with_arguments_compiled ) -> int: """ Returns a class_hash of the constructor_with_arguments.cairo. """ - declare = await gateway_account.sign_declare_transaction( + declare = await account.sign_declare_transaction( compiled_contract=constructor_with_arguments_compiled, max_fee=int(1e16), ) - res = await gateway_account.client.declare(declare) - await gateway_account.client.wait_for_tx(res.transaction_hash) + res = await account.client.declare(declare) + await account.client.wait_for_tx(res.transaction_hash) return res.class_hash diff --git a/starknet_py/tests/e2e/fixtures/contracts_v1.py b/starknet_py/tests/e2e/fixtures/contracts_v1.py index 85fb49ad8..59b90481d 100644 --- a/starknet_py/tests/e2e/fixtures/contracts_v1.py +++ b/starknet_py/tests/e2e/fixtures/contracts_v1.py @@ -121,10 +121,10 @@ async def cairo1_token_bridge_class_hash(account: BaseAccount) -> int: @pytest_asyncio.fixture(scope="package") async def cairo1_hello_starknet_deploy( - gateway_account: BaseAccount, cairo1_hello_starknet_class_hash + account: BaseAccount, cairo1_hello_starknet_class_hash ): return await deploy_v1_contract( - account=gateway_account, + account=account, contract_file_name="hello_starknet", class_hash=cairo1_hello_starknet_class_hash, ) diff --git a/starknet_py/tests/e2e/fixtures/misc.py b/starknet_py/tests/e2e/fixtures/misc.py index 0d8270974..8d63c83ae 100644 --- a/starknet_py/tests/e2e/fixtures/misc.py +++ b/starknet_py/tests/e2e/fixtures/misc.py @@ -23,12 +23,6 @@ def pytest_addoption(parser): default="devnet", help="Network to run tests on: possible 'testnet', 'devnet', 'all'", ) - parser.addoption( - "--client", - action="store", - default="", - help="Client to run tests with: possible 'gateway', 'full_node'", - ) parser.addoption( "--contract_dir", action="store", @@ -89,16 +83,11 @@ def typed_data(request) -> TypedData: return typed_data -@pytest.fixture(name="tx_receipt_full_node_path", scope="package") +@pytest.fixture(name="get_tx_receipt_path", scope="package") def get_tx_receipt_full_node_client(): return "starknet_py.net.full_node_client.FullNodeClient.get_transaction_receipt" -@pytest.fixture(name="tx_receipt_gateway_path", scope="package") -def get_tx_receipt_gateway_client(): - return "starknet_py.net.gateway_client.GatewayClient.get_transaction_receipt" - - def read_contract(file_name: str, *, directory: Optional[Path] = None) -> str: """ Return contents of file_name from directory. diff --git a/starknet_py/tests/e2e/fixtures/proxy.py b/starknet_py/tests/e2e/fixtures/proxy.py index ac9c9af1a..c9cfc79fd 100644 --- a/starknet_py/tests/e2e/fixtures/proxy.py +++ b/starknet_py/tests/e2e/fixtures/proxy.py @@ -57,29 +57,27 @@ def old_proxy(request) -> str: ], ) async def deploy_proxy_to_contract_oz_argent_eth( - request, gateway_account: Account + request, account: Account ) -> DeployResult: """ Declares a contract and deploys a proxy (OZ, Argent, Eth) pointing to that contract. """ compiled_proxy_name, compiled_contract_name = request.param return await deploy_proxy_to_contract( - compiled_proxy_name, compiled_contract_name, gateway_account + compiled_proxy_name, compiled_contract_name, account ) @pytest_asyncio.fixture( name="proxy_custom", params=[("oz_proxy_custom_compiled.json", "map_compiled.json")] ) -async def deploy_proxy_to_contract_custom( - request, gateway_account: Account -) -> DeployResult: +async def deploy_proxy_to_contract_custom(request, account: Account) -> DeployResult: """ Declares a contract and deploys a custom proxy pointing to that contract. """ compiled_proxy_name, compiled_contract_name = request.param return await deploy_proxy_to_contract( - compiled_proxy_name, compiled_contract_name, gateway_account + compiled_proxy_name, compiled_contract_name, account ) @@ -87,22 +85,20 @@ async def deploy_proxy_to_contract_custom( name="proxy_impl_func", params=[("oz_proxy_impl_func_compiled.json", "map_compiled.json")], ) -async def deploy_proxy_to_contract_impl_func( - request, gateway_account: Account -) -> DeployResult: +async def deploy_proxy_to_contract_impl_func(request, account: Account) -> DeployResult: """ Declares a contract and deploys a custom proxy pointing to that contract. """ compiled_proxy_name, compiled_contract_name = request.param return await deploy_proxy_to_contract( - compiled_proxy_name, compiled_contract_name, gateway_account + compiled_proxy_name, compiled_contract_name, account ) async def deploy_proxy_to_contract( compiled_proxy_name: str, compiled_contract_name: str, - gateway_account: Account, + account: Account, ) -> DeployResult: """ Declares a contract and deploys a proxy pointing to that contract. @@ -114,14 +110,14 @@ async def deploy_proxy_to_contract( compiled_contract_name, directory=CONTRACTS_COMPILED_V0_DIR ) - declare_tx = await gateway_account.sign_declare_transaction( + declare_tx = await account.sign_declare_transaction( compiled_contract=compiled_contract, max_fee=MAX_FEE ) - declare_result = await gateway_account.client.declare(declare_tx) - await gateway_account.client.wait_for_tx(declare_result.transaction_hash) + declare_result = await account.client.declare(declare_tx) + await account.client.wait_for_tx(declare_result.transaction_hash) declare_proxy_result = await Contract.declare( - account=gateway_account, + account=account, compiled_contract=compiled_proxy, max_fee=MAX_FEE, ) diff --git a/starknet_py/tests/e2e/tests_on_networks/client_test.py b/starknet_py/tests/e2e/tests_on_networks/client_test.py index f15187176..de2ad23f0 100644 --- a/starknet_py/tests/e2e/tests_on_networks/client_test.py +++ b/starknet_py/tests/e2e/tests_on_networks/client_test.py @@ -8,15 +8,12 @@ from starknet_py.net.client_models import ( Call, EstimatedFee, - SignatureOnStateDiff, - StateUpdateWithBlock, TransactionExecutionStatus, TransactionFinalityStatus, TransactionReceipt, TransactionStatus, ) -from starknet_py.net.gateway_client import GatewayClient -from starknet_py.tests.e2e.fixtures.constants import PREDEPLOYED_EMPTY_CONTRACT_ADDRESS +from starknet_py.tests.e2e.fixtures.constants import EMPTY_CONTRACT_ADDRESS_TESTNET from starknet_py.transaction_errors import TransactionRevertedError @@ -31,30 +28,22 @@ ), ) @pytest.mark.asyncio -async def test_get_transaction_receipt(client_integration, transaction_hash): - receipt = await client_integration.get_transaction_receipt(tx_hash=transaction_hash) +async def test_get_transaction_receipt(full_node_client_integration, transaction_hash): + receipt = await full_node_client_integration.get_transaction_receipt( + tx_hash=transaction_hash + ) assert isinstance(receipt, TransactionReceipt) assert receipt.execution_status is not None assert receipt.finality_status is not None -# There is a chance that the full node test fails with a reason: "Transaction with the same hash already exists -# in the mempool" (or something like that). This is because gateway has instant access to pending nodes, but nodes -# do not. If, somehow, gateway test gets executed before the full_node one, the transaction will still be in the PENDING -# block and the next one with the same hash will be rejected (you could artificially add more items to 'calldata' array, -# but you would need to change the nonce and tests depending on each other is a bad idea). -# Same thing could happen when you run tests locally and then push to run them on CI. -@pytest.mark.skipif( - condition="--client=gateway" in sys.argv, - reason="Gateway client has been disabled on integration network.", -) @pytest.mark.asyncio -async def test_wait_for_tx_reverted_full_node(full_node_account_integration): - account = full_node_account_integration +async def test_wait_for_tx_reverted(full_node_account_testnet): + account = full_node_account_testnet # Calldata too long for the function (it has no parameters) to trigger REVERTED status call = Call( - to_addr=int(PREDEPLOYED_EMPTY_CONTRACT_ADDRESS, 0), + to_addr=int(EMPTY_CONTRACT_ADDRESS_TESTNET, 0), selector=get_selector_from_name("empty"), calldata=[0x1, 0x2, 0x3, 0x4, 0x5], ) @@ -65,16 +54,11 @@ async def test_wait_for_tx_reverted_full_node(full_node_account_integration): await account.client.wait_for_tx(tx_hash=invoke.transaction_hash) -# Same here as in comment above 'test_wait_for_tx_reverted_full_node' -@pytest.mark.skipif( - condition="--client=gateway" in sys.argv, - reason="Gateway client has been disabled on integration network.", -) @pytest.mark.asyncio -async def test_wait_for_tx_full_node_accepted(full_node_account_integration): - account = full_node_account_integration +async def test_wait_for_tx_accepted(full_node_account_testnet): + account = full_node_account_testnet call = Call( - to_addr=int(PREDEPLOYED_EMPTY_CONTRACT_ADDRESS, 0), + to_addr=int(EMPTY_CONTRACT_ADDRESS_TESTNET, 0), selector=get_selector_from_name("empty"), calldata=[], ) @@ -87,57 +71,39 @@ async def test_wait_for_tx_full_node_accepted(full_node_account_integration): assert result.finality_status == TransactionFinalityStatus.ACCEPTED_ON_L2 -@pytest.mark.skipif( - condition="--client=gateway" in sys.argv, - reason="Gateway client has been disabled on integration network.", -) @pytest.mark.asyncio -async def test_transaction_not_received_max_fee_too_small(account_integration): - account = account_integration +async def test_transaction_not_received_max_fee_too_small(full_node_account_testnet): + account = full_node_account_testnet call = Call( - to_addr=int(PREDEPLOYED_EMPTY_CONTRACT_ADDRESS, 0), + to_addr=int(EMPTY_CONTRACT_ADDRESS_TESTNET, 0), selector=get_selector_from_name("empty"), calldata=[], ) - ARBITRARILY_SMALL_NONCE = 1 - sign_invoke = await account.sign_invoke_transaction( - calls=call, max_fee=ARBITRARILY_SMALL_NONCE - ) + sign_invoke = await account.sign_invoke_transaction(calls=call, max_fee=1) with pytest.raises(ClientError, match=r".*Max fee.*"): - _ = await account.client.send_transaction(sign_invoke) + await account.client.send_transaction(sign_invoke) -@pytest.mark.skipif( - condition="--client=gateway" in sys.argv, - reason="Gateway client has been disabled on integration network.", -) @pytest.mark.asyncio -async def test_transaction_not_received_max_fee_too_big(account_integration): - account = account_integration +async def test_transaction_not_received_max_fee_too_big(full_node_account_testnet): + account = full_node_account_testnet call = Call( - to_addr=int(PREDEPLOYED_EMPTY_CONTRACT_ADDRESS, 0), + to_addr=int(EMPTY_CONTRACT_ADDRESS_TESTNET, 0), selector=get_selector_from_name("empty"), calldata=[], ) - ARBITRARILY_BIG_FEE_WE_WILL_NEVER_HAVE_SO_MUCH_ETH = sys.maxsize - sign_invoke = await account.sign_invoke_transaction( - calls=call, max_fee=ARBITRARILY_BIG_FEE_WE_WILL_NEVER_HAVE_SO_MUCH_ETH - ) + sign_invoke = await account.sign_invoke_transaction(calls=call, max_fee=sys.maxsize) with pytest.raises(ClientError, match=r".*max_fee.*"): - _ = await account.client.send_transaction(sign_invoke) + await account.client.send_transaction(sign_invoke) -@pytest.mark.skipif( - condition="--client=gateway" in sys.argv, - reason="Gateway client has been disabled on integration network.", -) @pytest.mark.asyncio -async def test_transaction_not_received_invalid_nonce(account_integration): - account = account_integration +async def test_transaction_not_received_invalid_nonce(full_node_account_testnet): + account = full_node_account_testnet call = Call( - to_addr=int(PREDEPLOYED_EMPTY_CONTRACT_ADDRESS, 0), + to_addr=int(EMPTY_CONTRACT_ADDRESS_TESTNET, 0), selector=get_selector_from_name("empty"), calldata=[], ) @@ -146,27 +112,22 @@ async def test_transaction_not_received_invalid_nonce(account_integration): ) with pytest.raises(ClientError, match=r".*nonce.*"): - _ = await account.client.send_transaction(sign_invoke) + await account.client.send_transaction(sign_invoke) -@pytest.mark.skipif( - condition="--client=gateway" in sys.argv, - reason="Gateway client has been disabled on integration network.", -) @pytest.mark.asyncio -async def test_transaction_not_received_invalid_signature(account_integration): - account = account_integration +async def test_transaction_not_received_invalid_signature(full_node_account_testnet): + account = full_node_account_testnet call = Call( - to_addr=int(PREDEPLOYED_EMPTY_CONTRACT_ADDRESS, 0), + to_addr=int(EMPTY_CONTRACT_ADDRESS_TESTNET, 0), selector=get_selector_from_name("empty"), calldata=[], ) sign_invoke = await account.sign_invoke_transaction(calls=call, max_fee=int(1e16)) sign_invoke = dataclasses.replace(sign_invoke, signature=[0x21, 0x37]) - # first one for gateway, second for pathfinder node with pytest.raises(ClientError, match=r"(.*Signature.*)|(.*An unexpected error.*)"): - _ = await account.client.send_transaction(sign_invoke) + await account.client.send_transaction(sign_invoke) # ------------------------------------ FULL_NODE_CLIENT TESTS ------------------------------------ @@ -174,10 +135,6 @@ async def test_transaction_not_received_invalid_signature(account_integration): # TODO (#1142): move tests below to full_node_test.py once devnet releases rust version supporting RPC v0.4.0 -@pytest.mark.skipif( - condition="--client=gateway" in sys.argv, - reason="Separate FullNode tests from Gateway ones.", -) @pytest.mark.asyncio async def test_estimate_message_fee(full_node_client_integration): client = full_node_client_integration @@ -202,10 +159,6 @@ async def test_estimate_message_fee(full_node_client_integration): assert estimated_message.gas_usage > 0 -@pytest.mark.skipif( - condition="--client=gateway" in sys.argv, - reason="Separate FullNode tests from Gateway ones.", -) @pytest.mark.asyncio async def test_estimate_message_fee_invalid_eth_address_assertion_error( full_node_client_integration, @@ -232,10 +185,6 @@ async def test_estimate_message_fee_invalid_eth_address_assertion_error( ) -@pytest.mark.skipif( - condition="--client=gateway" in sys.argv, - reason="Separate FullNode tests from Gateway ones.", -) @pytest.mark.parametrize( "from_address, to_address", ( @@ -268,8 +217,8 @@ async def test_estimate_message_fee_throws( @pytest.mark.asyncio -async def test_get_tx_receipt_reverted(client_integration): - client = client_integration +async def test_get_tx_receipt_reverted(full_node_client_integration): + client = full_node_client_integration reverted_tx_hash = ( "0x4a3c389816f8544d05db964957eb41a9e3f8c660e8487695cb75438ef983181" ) @@ -278,16 +227,9 @@ async def test_get_tx_receipt_reverted(client_integration): assert res.execution_status == TransactionExecutionStatus.REVERTED assert res.finality_status == TransactionFinalityStatus.ACCEPTED_ON_L1 - if isinstance(client, GatewayClient): - assert "Input too long for arguments" in res.revert_error - else: - assert "Input too long for arguments" in res.revert_reason + assert "Input too long for arguments" in res.revert_reason -@pytest.mark.skipif( - condition="--client=gateway" in sys.argv, - reason="Separate FullNode tests from Gateway ones.", -) @pytest.mark.parametrize( "block_number, transaction_index", [ @@ -330,10 +272,6 @@ async def test_get_block(full_node_client_integration): assert tx.hash is not None -@pytest.mark.skipif( - condition="--client=gateway" in sys.argv, - reason="Method get_l1_message_hash not implemented for Gateway client.", -) @pytest.mark.asyncio async def test_get_l1_message_hash(full_node_client_integration): tx_hash = "0x0060bd50c38082211e6aedb21838fe7402a67216d559d9a4848e6c5e9670c90e" @@ -344,10 +282,6 @@ async def test_get_l1_message_hash(full_node_client_integration): ) -@pytest.mark.skipif( - condition="--client=gateway" in sys.argv, - reason="Method get_l1_message_hash not implemented for Gateway client.", -) @pytest.mark.asyncio async def test_get_l1_message_hash_raises_on_incorrect_transaction_type( full_node_client_integration, @@ -359,44 +293,6 @@ async def test_get_l1_message_hash_raises_on_incorrect_transaction_type( await full_node_client_integration.get_l1_message_hash(tx_hash) -@pytest.mark.asyncio -async def test_get_public_key(gateway_client_integration): - current_public_key = ( - "0x52934be54ce926b1e715f15dc2542849a97ecfdf829cd0b7384c64eeeb2264e" - ) - public_key = await gateway_client_integration.get_public_key() - - assert isinstance(public_key, str) - assert public_key == current_public_key - - -@pytest.mark.asyncio -async def test_get_signature(gateway_client_integration): - block_number = 100000 - signature = await gateway_client_integration.get_signature( - block_number=block_number - ) - block = await gateway_client_integration.get_block(block_number=block_number) - - assert isinstance(signature, SignatureOnStateDiff) - assert signature.block_number == block_number - assert len(signature.signature) == 2 - assert signature.signature_input.block_hash == block.block_hash - - -@pytest.mark.asyncio -async def test_get_state_update_with_block(gateway_client_integration): - res = await gateway_client_integration.get_state_update( - block_number=100000, include_block=True - ) - block = await gateway_client_integration.get_block(block_number=100000) - - assert isinstance(res, StateUpdateWithBlock) - - assert res.block == block - assert res.state_update is not None - - @pytest.mark.asyncio async def test_spec_version(full_node_client_testnet): spec_version = await full_node_client_testnet.spec_version() @@ -461,4 +357,3 @@ async def test_get_tx_receipt_new_fields(full_node_client_testnet): ) assert receipt.execution_resources is not None - assert len(receipt.execution_resources.keys()) in [8, 9] diff --git a/starknet_py/tests/e2e/tests_on_networks/fixtures.py b/starknet_py/tests/e2e/tests_on_networks/fixtures.py index acde9f3dd..d30b7d63d 100644 --- a/starknet_py/tests/e2e/tests_on_networks/fixtures.py +++ b/starknet_py/tests/e2e/tests_on_networks/fixtures.py @@ -1,121 +1,44 @@ -import sys -from typing import List - import pytest from starknet_py.net.account.account import Account -from starknet_py.net.client import Client from starknet_py.net.full_node_client import FullNodeClient -from starknet_py.net.gateway_client import GatewayClient from starknet_py.net.models import StarknetChainId from starknet_py.net.signer.stark_curve_signer import KeyPair from starknet_py.tests.e2e.fixtures.constants import ( INTEGRATION_ACCOUNT_ADDRESS, INTEGRATION_ACCOUNT_PRIVATE_KEY, - INTEGRATION_GATEWAY_URL, INTEGRATION_RPC_URL, + TESTNET_ACCOUNT_ADDRESS, + TESTNET_ACCOUNT_PRIVATE_KEY, TESTNET_RPC_URL, ) -@pytest.fixture(scope="package") -def gateway_client_integration() -> GatewayClient: - """ - A fixture returning a GatewayClient with a public integration network URL. - """ - return GatewayClient(net=INTEGRATION_GATEWAY_URL) - - @pytest.fixture(scope="package") def full_node_client_integration() -> FullNodeClient: - """ - A fixture returning a FullNodeClient with our integration network node URL. - """ - # because TESTNET and INTEGRATION constants are lambdas return FullNodeClient(node_url=INTEGRATION_RPC_URL()) -def net_to_integration_clients() -> List[str]: - if "--client=gateway" in sys.argv: - return ["gateway_client_integration"] - if "--client=full_node" in sys.argv: - return ["full_node_client_integration"] - return ["gateway_client_integration", "full_node_client_integration"] - - -@pytest.fixture( - scope="package", - params=net_to_integration_clients(), -) -def client_integration(request) -> Client: - """ - A fixture returning integration network clients one by one (GatewayClient, then FullNodeClient). - """ - return request.getfixturevalue(request.param) - - -@pytest.fixture(scope="package") -def gateway_account_integration(gateway_client_integration) -> Account: - """ - A fixture returning an Account with GatewayClient. - """ - return Account( - # because TESTNET and INTEGRATION constants are lambdas - address=INTEGRATION_ACCOUNT_ADDRESS(), - client=gateway_client_integration, - # because TESTNET and INTEGRATION constants are lambdas - key_pair=KeyPair.from_private_key(int(INTEGRATION_ACCOUNT_PRIVATE_KEY(), 0)), - chain=StarknetChainId.TESTNET, - ) - - @pytest.fixture(scope="package") def full_node_account_integration(full_node_client_integration) -> Account: - """ - A fixture returning an Account with FullNodeClient. - """ return Account( - # because TESTNET and INTEGRATION constants are lambdas address=INTEGRATION_ACCOUNT_ADDRESS(), client=full_node_client_integration, - # because TESTNET and INTEGRATION constants are lambdas key_pair=KeyPair.from_private_key(int(INTEGRATION_ACCOUNT_PRIVATE_KEY(), 0)), chain=StarknetChainId.TESTNET, ) -def net_to_integration_accounts() -> List[str]: - if "--client=gateway" in sys.argv: - return ["gateway_account_integration"] - if "--client=full_node" in sys.argv: - return ["full_node_account_integration"] - return ["gateway_account_integration", "full_node_account_integration"] - - -@pytest.fixture( - scope="package", - params=net_to_integration_accounts(), -) -def account_integration(request) -> Account: - """ - A fixture returning integration network accounts one by one (account with GatewayClient, then account with - FullNodeClient). - """ - return request.getfixturevalue(request.param) - - @pytest.fixture(scope="package") -def gateway_client_testnet() -> GatewayClient: - """ - A fixture returning a GatewayClient with a public integration network URL. - """ - return GatewayClient(net="testnet") +def full_node_client_testnet() -> FullNodeClient: + return FullNodeClient(node_url=TESTNET_RPC_URL()) @pytest.fixture(scope="package") -def full_node_client_testnet() -> FullNodeClient: - """ - A fixture returning a FullNodeClient with our integration network node URL. - """ - # because TESTNET and INTEGRATION constants are lambdas - return FullNodeClient(node_url=TESTNET_RPC_URL()) +def full_node_account_testnet(full_node_client_testnet) -> Account: + return Account( + address=TESTNET_ACCOUNT_ADDRESS(), + client=full_node_client_testnet, + key_pair=KeyPair.from_private_key(int(TESTNET_ACCOUNT_PRIVATE_KEY(), 0)), + chain=StarknetChainId.TESTNET, + ) diff --git a/starknet_py/tests/e2e/tests_on_networks/trace_api_test.py b/starknet_py/tests/e2e/tests_on_networks/trace_api_test.py index 8be042641..4006e5c29 100644 --- a/starknet_py/tests/e2e/tests_on_networks/trace_api_test.py +++ b/starknet_py/tests/e2e/tests_on_networks/trace_api_test.py @@ -1,6 +1,5 @@ import pytest -from starknet_py.net.account.account import Account from starknet_py.net.client_models import ( DeclareTransaction, DeclareTransactionTrace, @@ -14,14 +13,7 @@ Transaction, TransactionTrace, ) -from starknet_py.net.full_node_client import FullNodeClient -from starknet_py.net.models import StarknetChainId -from starknet_py.net.signer.stark_curve_signer import KeyPair -from starknet_py.tests.e2e.fixtures.constants import ( - CONTRACTS_COMPILED_V0_DIR, - TESTNET_ACCOUNT_ADDRESS, - TESTNET_ACCOUNT_PRIVATE_KEY, -) +from starknet_py.tests.e2e.fixtures.constants import CONTRACTS_COMPILED_V0_DIR from starknet_py.tests.e2e.fixtures.misc import read_contract # TODO (#1179): move those tests to full_node_test.py @@ -109,28 +101,16 @@ async def test_get_block_traces(full_node_client_testnet): @pytest.mark.asyncio -async def test_simulate_transactions_declare_on_network( - full_node_account, full_node_client_testnet -): - testnet_account_address = TESTNET_ACCOUNT_ADDRESS() - testnet_account_private_key = TESTNET_ACCOUNT_PRIVATE_KEY() - - full_node_account = Account( - address=testnet_account_address, - client=full_node_client_testnet, - key_pair=KeyPair.from_private_key(testnet_account_private_key), - chain=StarknetChainId.TESTNET, - ) +async def test_simulate_transactions_declare_on_network(full_node_account_testnet): compiled_contract = read_contract( "map_compiled.json", directory=CONTRACTS_COMPILED_V0_DIR ) - declare_tx = await full_node_account.sign_declare_transaction( + declare_tx = await full_node_account_testnet.sign_declare_transaction( compiled_contract, max_fee=int(1e16) ) - assert isinstance(full_node_account.client, FullNodeClient) - simulated_txs = await full_node_account.client.simulate_transactions( - transactions=[declare_tx], block_number="latest" + simulated_txs = await full_node_account_testnet.client.simulate_transactions( + transactions=[declare_tx] ) assert isinstance(simulated_txs[0].transaction_trace, DeclareTransactionTrace) diff --git a/starknet_py/tests/e2e/utils.py b/starknet_py/tests/e2e/utils.py index 4b348218e..8c7a0d774 100644 --- a/starknet_py/tests/e2e/utils.py +++ b/starknet_py/tests/e2e/utils.py @@ -1,16 +1,14 @@ import random -from typing import Optional, Tuple, cast +from typing import Tuple from starknet_py.constants import EC_ORDER from starknet_py.contract import Contract from starknet_py.hash.address import compute_address from starknet_py.net.account.account import Account from starknet_py.net.client import Client -from starknet_py.net.gateway_client import GatewayClient from starknet_py.net.http_client import HttpClient, HttpMethod from starknet_py.net.models import StarknetChainId from starknet_py.net.models.transaction import DeployAccount -from starknet_py.net.networks import Network from starknet_py.net.signer.stark_curve_signer import KeyPair from starknet_py.net.udc_deployer.deployer import _get_random_salt from starknet_py.tests.e2e.fixtures.constants import MAX_FEE @@ -53,28 +51,15 @@ async def get_deploy_account_details( async def get_deploy_account_transaction( - *, - address: int, - key_pair: KeyPair, - salt: int, - class_hash: int, - network: Optional[Network] = None, - client: Optional[Client] = None, + *, address: int, key_pair: KeyPair, salt: int, class_hash: int, client: Client ) -> DeployAccount: """ Get a signed DeployAccount transaction from provided details """ - if network is None and client is None: - raise ValueError("One of network or client must be provided.") account = Account( address=address, - client=client - or GatewayClient( - net=cast( - Network, network - ) # Cast needed because pyright doesn't recognize network as not None at this point - ), + client=client, key_pair=key_pair, chain=StarknetChainId.TESTNET, ) From 1859b5d2d2be9963c2fa178567cc2ba82a282823 Mon Sep 17 00:00:00 2001 From: xiaolou86 <20718693+xiaolou86@users.noreply.github.com> Date: Thu, 30 Nov 2023 18:10:20 +0800 Subject: [PATCH 2/7] Fix typos (#1229) --- docs/migration_guide.rst | 2 +- starknet_py/cairo/type_parser.py | 2 +- starknet_py/contract.py | 2 +- starknet_py/net/udc_deployer/deployer.py | 2 +- starknet_py/tests/e2e/deploy/deployer_test.py | 2 +- starknet_py/tests/e2e/mock/contracts_v1/hello.cairo | 2 +- starknet_py/tests/e2e/mock/contracts_v2/hello2.cairo | 6 +++--- ...or_args_translator.py => constructor_args_translator.py} | 0 8 files changed, 9 insertions(+), 9 deletions(-) rename starknet_py/utils/{contructor_args_translator.py => constructor_args_translator.py} (100%) diff --git a/docs/migration_guide.rst b/docs/migration_guide.rst index a56b1d867..789c3d26a 100644 --- a/docs/migration_guide.rst +++ b/docs/migration_guide.rst @@ -431,7 +431,7 @@ Also, dependencies are now optimized to include only necessary packages. 8. Removed deprecated ``typed_data`` parameter as dict in :meth:`BaseSigner.sign_message`. Use :ref:`TypedData` dataclass from ``starknet_py.utils.typed_data``. 9. ``starknet_py.utils.crypto`` module has been removed. -10. Changed name of ``starknet_py.transaction_excepions`` to ``starknet_py.transaction_errors`` to match other files. +10. Changed name of ``starknet_py.transaction_exceptions`` to ``starknet_py.transaction_errors`` to match other files. .. admonition:: Potentially breaking changes :class: attention diff --git a/starknet_py/cairo/type_parser.py b/starknet_py/cairo/type_parser.py index 2235607e6..62d3cdc14 100644 --- a/starknet_py/cairo/type_parser.py +++ b/starknet_py/cairo/type_parser.py @@ -62,7 +62,7 @@ def _transform_cairo_lang_type( self, cairo_type: cairo_lang_types.CairoType ) -> CairoType: """ - For now, we use parse function from cairo-lang pacakge. It will be replaced in the future, but we need to hide + For now, we use parse function from cairo-lang package. It will be replaced in the future, but we need to hide it from the users. This function takes types returned by cairo-lang package and maps them to our type classes. diff --git a/starknet_py/contract.py b/starknet_py/contract.py index 409adbdba..33bea8aec 100644 --- a/starknet_py/contract.py +++ b/starknet_py/contract.py @@ -46,7 +46,7 @@ from starknet_py.serialization.function_serialization_adapter import ( FunctionSerializationAdapter, ) -from starknet_py.utils.contructor_args_translator import ( +from starknet_py.utils.constructor_args_translator import ( _is_abi_v2, translate_constructor_args, ) diff --git a/starknet_py/net/udc_deployer/deployer.py b/starknet_py/net/udc_deployer/deployer.py index ccaba961d..a8ef95881 100644 --- a/starknet_py/net/udc_deployer/deployer.py +++ b/starknet_py/net/udc_deployer/deployer.py @@ -12,7 +12,7 @@ from starknet_py.net.client_models import Call, Hash from starknet_py.net.models import AddressRepresentation, parse_address from starknet_py.serialization import serializer_for_function -from starknet_py.utils.contructor_args_translator import translate_constructor_args +from starknet_py.utils.constructor_args_translator import translate_constructor_args class ContractDeployment(NamedTuple): diff --git a/starknet_py/tests/e2e/deploy/deployer_test.py b/starknet_py/tests/e2e/deploy/deployer_test.py index 2d96c8a95..52cac4c66 100644 --- a/starknet_py/tests/e2e/deploy/deployer_test.py +++ b/starknet_py/tests/e2e/deploy/deployer_test.py @@ -5,7 +5,7 @@ from starknet_py.net.full_node_client import FullNodeClient from starknet_py.net.udc_deployer.deployer import Deployer from starknet_py.tests.e2e.fixtures.constants import MAX_FEE -from starknet_py.utils.contructor_args_translator import translate_constructor_args +from starknet_py.utils.constructor_args_translator import translate_constructor_args @pytest.mark.asyncio diff --git a/starknet_py/tests/e2e/mock/contracts_v1/hello.cairo b/starknet_py/tests/e2e/mock/contracts_v1/hello.cairo index 390e9d342..39bd6cc5a 100644 --- a/starknet_py/tests/e2e/mock/contracts_v1/hello.cairo +++ b/starknet_py/tests/e2e/mock/contracts_v1/hello.cairo @@ -302,7 +302,7 @@ mod HelloStarknet { data } - // unamed Tuple + // unnamed Tuple #[view] fn echo_un_tuple(a:(felt252, u16)) -> (felt252, u16) { a diff --git a/starknet_py/tests/e2e/mock/contracts_v2/hello2.cairo b/starknet_py/tests/e2e/mock/contracts_v2/hello2.cairo index b6b40c13e..5e83f61f5 100644 --- a/starknet_py/tests/e2e/mock/contracts_v2/hello2.cairo +++ b/starknet_py/tests/e2e/mock/contracts_v2/hello2.cairo @@ -90,7 +90,7 @@ trait IHelloStarknet { fn echo_array(self: @TContractState, data: Array) -> Array; fn echo_array_u256(self: @TContractState, data: Array) -> Array; fn echo_array_bool(self: @TContractState, data: Array) -> Array; - // unamed Tuple + // unnamed Tuple fn echo_un_tuple(self: @TContractState, a: (felt252, u16)) -> (felt252, u16); // echo Struct fn echo_struct(self: @TContractState, tt: Foo) -> Foo; @@ -251,7 +251,7 @@ mod HelloStarknet { data } - // unamed Tuple + // unnamed Tuple fn echo_un_tuple(self: @ContractState, a: (felt252, u16)) -> (felt252, u16) { a } @@ -351,7 +351,7 @@ mod HelloStarknet { } MyEnum::Response(Order { p1: 1, p2: val1 }) } - // return Option + // return Option fn option_u8_output(self: @ContractState, val1: u8) -> Option { if val1 < 100 { return Option::None(()); diff --git a/starknet_py/utils/contructor_args_translator.py b/starknet_py/utils/constructor_args_translator.py similarity index 100% rename from starknet_py/utils/contructor_args_translator.py rename to starknet_py/utils/constructor_args_translator.py From 9fc34bee691d7b464a8e74589c326b3803706dc3 Mon Sep 17 00:00:00 2001 From: ddoktorski <45050160+ddoktorski@users.noreply.github.com> Date: Tue, 2 Jan 2024 15:14:32 +0100 Subject: [PATCH 3/7] Update `starknet-devnet` (#1239) * Add minimal changes to run starknet-devnet-rs locally * Fix or skip failing tests * Add devnet dependency to github workflow * Remove logic for `--cairo-compiler-manifest flag` * Add `DEVNET_PATH` * Install devnet from source * Add devnet rs to windows tests * Update devnet fixture * Add devnet setup to all required jobs * Fix failing tests due to empty params dict * Temp fix for faling declare v2 in docs tests * Make some fields required in client_models according to rpc spec * Remove `status` field from `TransactionReceipt` * Unskip tests * Update TODO comments * Update development guide * Update migration guide * Fix docs build * Fix failing tests on network * Skip test with invalid signature * Add devnet installation command to development guide * Pass empty array to payload when params is none * Skip interaction tests only for v1 dir contracts * Reduce sleep time for devnet start * Add skipif for test in docs * Add more info to development section * Use `get_transaction_status` in `wait_for_tx` * Add `TransactionRejectedError` to docs * Return `tx_receipt` if not reverted Co-authored-by: Maksim Zdobnikau <43750648+DelevoXDG@users.noreply.github.com> * Fix lint * Make field `type` required in `TransactionReceipt` --------- Co-authored-by: Maksim Zdobnikau <43750648+DelevoXDG@users.noreply.github.com> --- .github/workflows/checks.yml | 111 +++---- docs/api/transaction_errors.rst | 3 + docs/development.rst | 22 +- docs/migration_guide.rst | 3 + poetry.lock | 296 ++---------------- pyproject.toml | 1 - starknet_py/net/client.py | 58 ++-- starknet_py/net/client_models.py | 48 +-- starknet_py/net/full_node_client.py | 9 +- starknet_py/net/http_client.py | 2 +- starknet_py/net/schemas/rpc.py | 36 +-- starknet_py/tests/e2e/account/account_test.py | 54 +--- starknet_py/tests/e2e/client/client_test.py | 106 +++---- .../tests/e2e/client/fixtures/transactions.py | 19 +- .../tests/e2e/client/full_node_test.py | 82 ++--- .../contract_interaction/interaction_test.py | 30 +- .../v1_interaction_test.py | 16 + .../code_examples/test_full_node_client.py | 24 +- .../docs/guide/test_simple_deploy_cairo1.py | 11 +- starknet_py/tests/e2e/fixtures/accounts.py | 2 +- starknet_py/tests/e2e/fixtures/constants.py | 4 +- starknet_py/tests/e2e/fixtures/devnet.py | 53 +--- starknet_py/tests/e2e/fixtures/misc.py | 8 +- .../e2e/tests_on_networks/account_test.py | 62 ++++ .../e2e/tests_on_networks/client_test.py | 8 +- .../e2e/tests_on_networks/trace_api_test.py | 2 +- starknet_py/transaction_errors.py | 5 +- 27 files changed, 376 insertions(+), 699 deletions(-) create mode 100644 starknet_py/tests/e2e/tests_on_networks/account_test.py diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index b8842651b..96cd5b5b9 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -1,5 +1,10 @@ name: Checks +env: + STARKNET_VERSION: "0.12.3" + RPC_SPEC_VERSION: "0.5.1" + DEVNET_SHA: "78527de" + on: push: branches: @@ -115,48 +120,6 @@ jobs: name: contract-artifacts path: starknet_py/tests/e2e/mock/ - # ====================== CAIRO SETUP ====================== # - - - name: Install rust - run: | - curl https://sh.rustup.rs -sSf | sh -s -- -y - - - name: Clone Cairo1 compiler repository - uses: actions/checkout@v3 - with: - repository: starkware-libs/cairo - persist-credentials: false - ref: v2.0.0 - path: cairo - - - name: Cache rust dependencies - id: cache-rust - uses: actions/cache@v3 - with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry/index/ - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - target/ - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - - - name: Build compiler - if: steps.cache-rust.outputs.cache-hit != 'true' - working-directory: ./cairo - run: | - cargo build - - - name: Build starknet-compile - working-directory: ./cairo - run: | - cargo run --bin starknet-compile -- --version - cargo run --bin starknet-sierra-compile -- --version - - - name: Create manifest file - run: | - readlink -f cairo/Cargo.toml >> starknet_py/tests/e2e/manifest-path - # ---------------------------------------------------------- # # ........................RUN-TESTS......................... # # ---------------------------------------------------------- # @@ -171,6 +134,8 @@ jobs: python-version: [ "3.8", "3.9", "3.10", "3.11" ] steps: - uses: actions/checkout@v3 + - uses: actions-rust-lang/setup-rust-toolchain@v1 + - uses: Swatinem/rust-cache@v2 - name: Download contracts uses: actions/download-artifact@v3 @@ -206,6 +171,14 @@ jobs: run: | poetry install --without py39-dev + # ====================== SETUP DEVNET ====================== # + + - name: Install devnet + run: | + cargo install --locked \ + --git https://github.com/0xSpaceShard/starknet-devnet-rs.git \ + --rev ${{ env.DEVNET_SHA }} + # ====================== RUN TESTS ====================== # - name: Check circular imports @@ -310,6 +283,8 @@ jobs: python-version: [ "3.8", "3.9", "3.10", "3.11" ] steps: - uses: actions/checkout@v3 + - uses: actions-rust-lang/setup-rust-toolchain@v1 + - uses: Swatinem/rust-cache@v2 - name: Download contracts uses: actions/download-artifact@v3 @@ -322,22 +297,10 @@ jobs: python-version: "3.9" cache: 'pip' - # ====================== SETUP WSL ====================== # + # ====================== SETUP DEVNET ====================== # - - name: Setup WSL - uses: Vampire/setup-wsl@v2 - - - name: Setup WSL devnet - shell: wsl-bash {0} - run: | - sudo apt-get -y update - sudo apt install -y wget software-properties-common - sudo add-apt-repository ppa:deadsnakes/ppa - sudo apt install -y python3.9 - apt-get install -y python3-pip - sudo apt install -y libgmp3-dev - sudo apt-get install -y git - pip3 install git+https://github.com/0xSpaceShard/starknet-devnet.git@v0.6.2 + - name: Install devnet + run: cargo install --locked --git https://github.com/0xSpaceShard/starknet-devnet-rs.git --rev ${{ env.DEVNET_SHA }} # ====================== SETUP PYTHON ====================== # @@ -394,6 +357,8 @@ jobs: python-version: [ "3.8", "3.9", "3.10", "3.11" ] steps: - uses: actions/checkout@v3 + - uses: actions-rust-lang/setup-rust-toolchain@v1 + - uses: Swatinem/rust-cache@v2 - name: Download contracts uses: actions/download-artifact@v3 @@ -429,6 +394,14 @@ jobs: run: | poetry install --without py39-dev + # ====================== SETUP DEVNET ====================== # + + - name: Install devnet + run: | + cargo install --locked \ + --git https://github.com/0xSpaceShard/starknet-devnet-rs.git \ + --rev ${{ env.DEVNET_SHA }} + # ====================== RUN TESTS ====================== # - name: Run tests @@ -458,6 +431,8 @@ jobs: python-version: [ "3.8", "3.9", "3.10", "3.11" ] steps: - uses: actions/checkout@v3 + - uses: actions-rust-lang/setup-rust-toolchain@v1 + - uses: Swatinem/rust-cache@v2 - name: Download contracts uses: actions/download-artifact@v3 @@ -470,23 +445,6 @@ jobs: python-version: "3.9" cache: 'pip' - # ====================== SETUP WSL ====================== # - - - name: Setup WSL - uses: Vampire/setup-wsl@v2 - - - name: Setup WSL devnet - shell: wsl-bash {0} - run: | - sudo apt-get -y update - sudo apt install -y wget software-properties-common - sudo add-apt-repository ppa:deadsnakes/ppa - sudo apt install -y python3.9 - apt-get install -y python3-pip - sudo apt install -y libgmp3-dev - sudo apt-get install -y git - pip3 install git+https://github.com/0xSpaceShard/starknet-devnet.git@v0.6.2 - # ====================== SETUP PYTHON ====================== # - name: Install poetry @@ -510,6 +468,11 @@ jobs: run: | poetry install --without py39-dev + # ====================== SETUP DEVNET ====================== # + + - name: Install devnet + run: cargo install --locked --git https://github.com/0xSpaceShard/starknet-devnet-rs.git --rev ${{ env.DEVNET_SHA }} + # ====================== RUN TESTS ====================== # - name: Run tests diff --git a/docs/api/transaction_errors.rst b/docs/api/transaction_errors.rst index bb34ca5d0..7cc98fbbb 100644 --- a/docs/api/transaction_errors.rst +++ b/docs/api/transaction_errors.rst @@ -9,5 +9,8 @@ Transaction errors .. autoclass:: TransactionRejectedError :exclude-members: __init__, __new__ +.. autoclass:: TransactionRevertedError + :exclude-members: __init__, __new__ + .. autoclass:: TransactionNotReceivedError :exclude-members: __init__, __new__ diff --git a/docs/development.rst b/docs/development.rst index 7ffb10b51..56859c749 100644 --- a/docs/development.rst +++ b/docs/development.rst @@ -15,9 +15,23 @@ Make sure running ``poetry run python --version`` returns ``Python 3.9.x``. Setup ----- -In order to run Cairo1 devnet tests and compile contracts in Cairo1 via poetry command, -you need to create ``manifest-path`` file in ``starknet_py/tests/e2e/`` directory and pass the path in it to Cairo compiler. -An example file - ``manifest-path.template`` is in the same directory. Additional info can be found in `devnet docs `_. +In order to run tests on devnet, you need to install `starknet-devnet-rs `_. +The correct version of devnet to use corresponds to the Starknet and RPC specification that are currently supported by Starknet.py. +Information about the supported version for the latest release can be found in the :doc:`migration guide`. + +To avoid version discrepancies or other related issues, we recommend installing this dependency using the ``cargo install`` command, and specifying a certain commit along with the correct Starknet and RPC versions. + +Below is the command you can use to do this, designed for compatibility with the current version of Starknet.py: + +.. code-block:: bash + + STARKNET_VERSION="0.12.3" RPC_SPEC_VERSION="0.5.1" \ + cargo install \ + --locked \ + --git https://github.com/0xSpaceShard/starknet-devnet-rs.git \ + --rev 78527de + +If you choose to install `starknet-devnet-rs `_ using a different method, please make sure to add the executable ``starknet-devnet`` to your ``PATH`` environment variable. In order to be able to run tests on testnet and integration networks (``starknet_py/tests/e2e/tests_on_networks/``), you must set some environmental variables: @@ -38,8 +52,6 @@ You can find an example file ``test-variables.env.template`` in the same directo # Compile contracts poe compile_contracts - poe compile_contracts_v1 - poe compile_contracts_v2 # Make sure everything was installed properly poe test diff --git a/docs/migration_guide.rst b/docs/migration_guide.rst index 789c3d26a..093c64927 100644 --- a/docs/migration_guide.rst +++ b/docs/migration_guide.rst @@ -13,11 +13,14 @@ Migration guide 1. :class:`GatewayClient` all related classes and fields have been removed. 2. Client ``net`` property has been removed. 3. :class:`TransactionReceipt` field ``execution_resources`` has been changed from ``dict`` to :class:`ExecutionResources`. +4. :class:`TransactionReceipt` fields ``status`` and ``rejection_reason`` have been removed. +5. :class:`TransactionStatus`, :class:`TransactionExecutionStatus` and :class:`TransactionFinalityStatus` have been changed to have the same structure as in RPC specification. 0.19.0 Minor changes -------------------- 1. :class:`L1HandlerTransaction` field ``nonce`` is now required. +2. :class:`TransactionReceipt` fields ``finality_status``, ``execution_status`` and ``execution_resources`` are now required. ********************** 0.18.3 Migration guide diff --git a/poetry.lock b/poetry.lock index 9bdd27e88..10b95ce78 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,10 +1,9 @@ -# This file is automatically @generated by Poetry and should not be changed by hand. +# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. [[package]] name = "aiohttp" version = "3.8.5" description = "Async http client/server framework (asyncio)" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -113,7 +112,6 @@ speedups = ["Brotli", "aiodns", "cchardet"] name = "aiosignal" version = "1.3.1" description = "aiosignal: a list of registered asynchronous callbacks" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -128,7 +126,6 @@ frozenlist = ">=1.1.0" name = "alabaster" version = "0.7.13" description = "A configurable sidebar-enabled Sphinx theme" -category = "main" optional = true python-versions = ">=3.6" files = [ @@ -140,7 +137,6 @@ files = [ name = "apeye" version = "1.3.0" description = "Handy tools for working with URLs and APIs." -category = "main" optional = true python-versions = ">=3.6.1" files = [ @@ -162,7 +158,6 @@ limiter = ["cachecontrol[filecache] (>=0.12.6)", "lockfile (>=0.12.2)"] name = "apeye-core" version = "1.1.1" description = "Core (offline) functionality for the apeye library." -category = "main" optional = true python-versions = ">=3.6.1" files = [ @@ -178,7 +173,6 @@ idna = ">=2.5" name = "asgiref" version = "3.7.2" description = "ASGI specs, helper code, and adapters" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -196,7 +190,6 @@ tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"] name = "astroid" version = "2.15.6" description = "An abstract syntax tree for Python with inference support." -category = "dev" optional = false python-versions = ">=3.7.2" files = [ @@ -216,7 +209,6 @@ wrapt = [ name = "async-timeout" version = "4.0.2" description = "Timeout context manager for asyncio programs" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -228,7 +220,6 @@ files = [ name = "attrs" version = "22.2.0" description = "Classes Without Boilerplate" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -247,7 +238,6 @@ tests-no-zope = ["cloudpickle", "cloudpickle", "hypothesis", "hypothesis", "mypy name = "autodocsumm" version = "0.2.11" description = "Extended sphinx autodoc including automatic autosummaries" -category = "main" optional = true python-versions = ">=3.7" files = [ @@ -262,7 +252,6 @@ Sphinx = ">=2.2,<8.0" name = "babel" version = "2.11.0" description = "Internationalization utilities" -category = "main" optional = true python-versions = ">=3.6" files = [ @@ -277,7 +266,6 @@ pytz = ">=2015.7" name = "beautifulsoup4" version = "4.11.2" description = "Screen-scraping library" -category = "main" optional = true python-versions = ">=3.6.0" files = [ @@ -296,7 +284,6 @@ lxml = ["lxml"] name = "bitarray" version = "2.8.0" description = "efficient arrays of booleans -- C extension" -category = "dev" optional = false python-versions = "*" files = [ @@ -408,7 +395,6 @@ files = [ name = "black" version = "23.7.0" description = "The uncompromising code formatter." -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -455,7 +441,6 @@ uvloop = ["uvloop (>=0.15.2)"] name = "cachecontrol" version = "0.12.11" description = "httplib2 caching for requests" -category = "main" optional = true python-versions = ">=3.6" files = [ @@ -476,7 +461,6 @@ redis = ["redis (>=2.10.5)"] name = "cachetools" version = "5.3.1" description = "Extensible memoizing collections and decorators" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -488,7 +472,6 @@ files = [ name = "cairo-lang" version = "0.12.2" description = "Compiler and runner for the Cairo language" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -526,7 +509,6 @@ web3 = "*" name = "certifi" version = "2022.12.7" description = "Python package for providing Mozilla's CA Bundle." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -538,7 +520,6 @@ files = [ name = "charset-normalizer" version = "2.1.1" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -category = "main" optional = false python-versions = ">=3.6.0" files = [ @@ -553,7 +534,6 @@ unicode-backport = ["unicodedata2"] name = "click" version = "8.1.3" description = "Composable command line interface toolkit" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -564,23 +544,10 @@ files = [ [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} -[[package]] -name = "cloudpickle" -version = "2.1.0" -description = "Extended pickling support for Python objects" -category = "dev" -optional = false -python-versions = ">=3.6" -files = [ - {file = "cloudpickle-2.1.0-py3-none-any.whl", hash = "sha256:b5c434f75c34624eedad3a14f2be5ac3b5384774d5b0e3caf905c21479e6c4b1"}, - {file = "cloudpickle-2.1.0.tar.gz", hash = "sha256:bb233e876a58491d9590a676f93c7a5473a08f747d5ab9df7f9ce564b3e7938e"}, -] - [[package]] name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ @@ -592,7 +559,6 @@ files = [ name = "coverage" version = "7.3.0" description = "Code coverage measurement for Python" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -660,7 +626,6 @@ toml = ["tomli"] name = "crypto-cpp-py" version = "1.4.0" description = "This is a packaged crypto-cpp program" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -727,7 +692,6 @@ build = ["cmake (>=3.22.4)"] name = "cssutils" version = "2.6.0" description = "A CSS Cascading Style Sheets library for Python" -category = "main" optional = true python-versions = ">=3.7" files = [ @@ -743,7 +707,6 @@ testing = ["cssselect", "flake8 (<5)", "importlib-resources", "jaraco.test (>=5. name = "cytoolz" version = "0.12.2" description = "Cython implementation of Toolz: High performance functional utilities" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -852,7 +815,6 @@ cython = ["cython"] name = "dict2css" version = "0.3.0" description = "A μ-library for constructing cascading style sheets from Python dictionaries." -category = "main" optional = true python-versions = ">=3.6" files = [ @@ -868,7 +830,6 @@ domdf-python-tools = ">=2.2.0" name = "dill" version = "0.3.6" description = "serialize all of python" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -883,7 +844,6 @@ graph = ["objgraph (>=1.7.2)"] name = "docutils" version = "0.18.1" description = "Docutils -- Python Documentation Utilities" -category = "main" optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -895,7 +855,6 @@ files = [ name = "domdf-python-tools" version = "3.5.0" description = "Helpful functions for Python 🐍 🛠️" -category = "main" optional = true python-versions = ">=3.6" files = [ @@ -916,7 +875,6 @@ dates = ["pytz (>=2019.1)"] name = "ecdsa" version = "0.18.0" description = "ECDSA cryptographic signature library (pure python)" -category = "main" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" files = [ @@ -935,7 +893,6 @@ gmpy2 = ["gmpy2"] name = "enum-tools" version = "0.10.0" description = "Tools to expand Python's enum module." -category = "main" optional = true python-versions = ">=3.6" files = [ @@ -958,7 +915,6 @@ sphinx = ["sphinx (>=3.2.0)", "sphinx-jinja2-compat (>=0.1.1)", "sphinx-toolbox name = "eth-abi" version = "4.1.0" description = "eth_abi: Python utilities for working with Ethereum ABI definitions, especially encoding and decoding" -category = "dev" optional = false python-versions = ">=3.7.2, <4" files = [ @@ -982,7 +938,6 @@ tools = ["hypothesis (>=4.18.2,<5.0.0)"] name = "eth-account" version = "0.9.0" description = "eth-account: Sign Ethereum transactions and messages with local private keys" -category = "dev" optional = false python-versions = ">=3.7, <4" files = [ @@ -1010,7 +965,6 @@ test = ["coverage", "hypothesis (>=4.18.0,<5)", "pytest (>=7.0.0)", "pytest-xdis name = "eth-hash" version = "0.5.2" description = "eth-hash: The Ethereum hashing function, keccak256, sometimes (erroneously) called sha3" -category = "dev" optional = false python-versions = ">=3.7, <4" files = [ @@ -1033,7 +987,6 @@ test = ["pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)"] name = "eth-keyfile" version = "0.6.1" description = "A library for handling the encrypted keyfiles used to store ethereum private keys." -category = "dev" optional = false python-versions = "*" files = [ @@ -1056,7 +1009,6 @@ test = ["pytest (>=6.2.5,<7)"] name = "eth-keys" version = "0.4.0" description = "Common API for Ethereum key operations." -category = "dev" optional = false python-versions = "*" files = [ @@ -1079,7 +1031,6 @@ test = ["asn1tools (>=0.146.2,<0.147)", "eth-hash[pycryptodome]", "eth-hash[pysh name = "eth-rlp" version = "0.3.0" description = "eth-rlp: RLP definitions for common Ethereum objects in Python" -category = "dev" optional = false python-versions = ">=3.7, <4" files = [ @@ -1102,7 +1053,6 @@ test = ["eth-hash[pycryptodome]", "pytest (>=6.2.5,<7)", "pytest-xdist", "tox (= name = "eth-typing" version = "3.4.0" description = "eth-typing: Common type annotations for ethereum python packages" -category = "dev" optional = false python-versions = ">=3.7.2, <4" files = [ @@ -1120,7 +1070,6 @@ test = ["pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)"] name = "eth-utils" version = "2.2.0" description = "eth-utils: Common utility functions for python code that interacts with Ethereum" -category = "dev" optional = false python-versions = ">=3.7,<4" files = [ @@ -1144,7 +1093,6 @@ test = ["hypothesis (>=4.43.0)", "mypy (==0.971)", "pytest (>=7.0.0)", "pytest-x name = "exceptiongroup" version = "1.1.1" description = "Backport of PEP 654 (exception groups)" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1159,7 +1107,6 @@ test = ["pytest (>=6)"] name = "execnet" version = "1.9.0" description = "execnet: rapid multi-Python deployment" -category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -1174,7 +1121,6 @@ testing = ["pre-commit"] name = "fastecdsa" version = "2.3.0" description = "Fast elliptic curve digital signatures" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1190,50 +1136,10 @@ files = [ {file = "fastecdsa-2.3.0.tar.gz", hash = "sha256:6c59aba650862a59f601ff7f66cd6712f4798ae68907c953d58417a5887103de"}, ] -[[package]] -name = "flask" -version = "2.0.3" -description = "A simple framework for building complex web applications." -category = "dev" -optional = false -python-versions = ">=3.6" -files = [ - {file = "Flask-2.0.3-py3-none-any.whl", hash = "sha256:59da8a3170004800a2837844bfa84d49b022550616070f7cb1a659682b2e7c9f"}, - {file = "Flask-2.0.3.tar.gz", hash = "sha256:e1120c228ca2f553b470df4a5fa927ab66258467526069981b3eb0a91902687d"}, -] - -[package.dependencies] -asgiref = {version = ">=3.2", optional = true, markers = "extra == \"async\""} -click = ">=7.1.2" -itsdangerous = ">=2.0" -Jinja2 = ">=3.0" -Werkzeug = ">=2.0" - -[package.extras] -async = ["asgiref (>=3.2)"] -dotenv = ["python-dotenv"] - -[[package]] -name = "flask-cors" -version = "3.0.10" -description = "A Flask extension adding a decorator for CORS support" -category = "dev" -optional = false -python-versions = "*" -files = [ - {file = "Flask-Cors-3.0.10.tar.gz", hash = "sha256:b60839393f3b84a0f3746f6cdca56c1ad7426aa738b70d6c61375857823181de"}, - {file = "Flask_Cors-3.0.10-py2.py3-none-any.whl", hash = "sha256:74efc975af1194fc7891ff5cd85b0f7478be4f7f59fe158102e91abb72bb4438"}, -] - -[package.dependencies] -Flask = ">=0.9" -Six = "*" - [[package]] name = "frozendict" version = "2.3.8" description = "A simple immutable dictionary" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1280,7 +1186,6 @@ files = [ name = "frozenlist" version = "1.3.3" description = "A list-like structure which implements collections.abc.MutableSequence" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1364,7 +1269,6 @@ files = [ name = "furo" version = "2023.8.19" description = "A clean customisable Sphinx documentation theme." -category = "main" optional = true python-versions = ">=3.8" files = [ @@ -1382,7 +1286,6 @@ sphinx-basic-ng = "*" name = "gprof2dot" version = "2022.7.29" description = "Generate a dot graph from the output of several profilers." -category = "dev" optional = false python-versions = ">=2.7" files = [ @@ -1390,32 +1293,10 @@ files = [ {file = "gprof2dot-2022.7.29.tar.gz", hash = "sha256:45b4d298bd36608fccf9511c3fd88a773f7a1abc04d6cd39445b11ba43133ec5"}, ] -[[package]] -name = "gunicorn" -version = "20.1.0" -description = "WSGI HTTP Server for UNIX" -category = "dev" -optional = false -python-versions = ">=3.5" -files = [ - {file = "gunicorn-20.1.0-py3-none-any.whl", hash = "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e"}, - {file = "gunicorn-20.1.0.tar.gz", hash = "sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8"}, -] - -[package.dependencies] -setuptools = ">=3.0" - -[package.extras] -eventlet = ["eventlet (>=0.24.1)"] -gevent = ["gevent (>=1.4.0)"] -setproctitle = ["setproctitle"] -tornado = ["tornado (>=0.2)"] - [[package]] name = "hexbytes" version = "0.3.1" description = "hexbytes: Python `bytes` subclass that decodes hex, with a readable console output" -category = "dev" optional = false python-versions = ">=3.7, <4" files = [ @@ -1433,7 +1314,6 @@ test = ["eth-utils (>=1.0.1,<3)", "hypothesis (>=3.44.24,<=6.31.6)", "pytest (>= name = "html5lib" version = "1.1" description = "HTML parser based on the WHATWG HTML specification" -category = "main" optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -1455,7 +1335,6 @@ lxml = ["lxml"] name = "idna" version = "3.4" description = "Internationalized Domain Names in Applications (IDNA)" -category = "main" optional = false python-versions = ">=3.5" files = [ @@ -1467,7 +1346,6 @@ files = [ name = "imagesize" version = "1.4.1" description = "Getting image size from png/jpeg/jpeg2000/gif file" -category = "main" optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -1479,7 +1357,6 @@ files = [ name = "importlib-metadata" version = "5.1.0" description = "Read metadata from Python packages" -category = "main" optional = true python-versions = ">=3.7" files = [ @@ -1499,7 +1376,6 @@ testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packag name = "iniconfig" version = "1.1.1" description = "iniconfig: brain-dead simple config-ini parsing" -category = "dev" optional = false python-versions = "*" files = [ @@ -1511,7 +1387,6 @@ files = [ name = "isort" version = "5.12.0" description = "A Python utility / library to sort Python imports." -category = "dev" optional = false python-versions = ">=3.8.0" files = [ @@ -1525,24 +1400,11 @@ pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib" plugins = ["setuptools"] requirements-deprecated-finder = ["pip-api", "pipreqs"] -[[package]] -name = "itsdangerous" -version = "2.1.2" -description = "Safely pass data to untrusted environments and back." -category = "dev" -optional = false -python-versions = ">=3.7" -files = [ - {file = "itsdangerous-2.1.2-py3-none-any.whl", hash = "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44"}, - {file = "itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a"}, -] - [[package]] name = "Jinja2" version = "3.0.3" description = "A very fast and expressive template engine." -category = "main" -optional = false +optional = true python-versions = ">=3.6" files = [ {file = "Jinja2-3.0.3-py3-none-any.whl", hash = "sha256:077ce6014f7b40d03b47d1f1ca4b0fc8328a692bd284016f806ed0eaca390ad8"}, @@ -1559,7 +1421,6 @@ i18n = ["Babel (>=2.7)"] name = "jsonschema" version = "4.17.3" description = "An implementation of JSON Schema validation for Python" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1579,7 +1440,6 @@ format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339- name = "lark" version = "1.1.7" description = "a modern parsing library" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1597,7 +1457,6 @@ regex = ["regex"] name = "lazy-object-proxy" version = "1.8.0" description = "A fast and thorough lazy object proxy." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1626,7 +1485,6 @@ files = [ name = "lockfile" version = "0.12.2" description = "Platform-independent file locking module" -category = "main" optional = true python-versions = "*" files = [ @@ -1638,7 +1496,6 @@ files = [ name = "lru-dict" version = "1.2.0" description = "An Dict like LRU container." -category = "dev" optional = false python-versions = "*" files = [ @@ -1733,8 +1590,7 @@ test = ["pytest"] name = "MarkupSafe" version = "2.1.1" description = "Safely add untrusted strings to HTML/XML markup." -category = "main" -optional = false +optional = true python-versions = ">=3.7" files = [ {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"}, @@ -1783,7 +1639,6 @@ files = [ name = "marshmallow" version = "3.17.1" description = "A lightweight library for converting complex datatypes to and from native Python datatypes." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1804,7 +1659,6 @@ tests = ["pytest", "pytz", "simplejson"] name = "marshmallow-dataclass" version = "8.4.2" description = "Python library to convert dataclasses into marshmallow schemas." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1828,7 +1682,6 @@ union = ["typeguard"] name = "marshmallow-enum" version = "1.5.1" description = "Enum field for Marshmallow" -category = "dev" optional = false python-versions = "*" files = [ @@ -1843,7 +1696,6 @@ marshmallow = ">=2.0.0" name = "marshmallow-oneofschema" version = "3.0.1" description = "marshmallow multiplexing schema" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1863,7 +1715,6 @@ tests = ["mock", "pytest"] name = "mccabe" version = "0.7.0" description = "McCabe checker, plugin for flake8" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1875,7 +1726,6 @@ files = [ name = "mpmath" version = "1.2.1" description = "Python library for arbitrary-precision floating-point arithmetic" -category = "main" optional = false python-versions = "*" files = [ @@ -1891,7 +1741,6 @@ tests = ["pytest (>=4.6)"] name = "msgpack" version = "1.0.4" description = "MessagePack serializer" -category = "main" optional = true python-versions = "*" files = [ @@ -1953,7 +1802,6 @@ files = [ name = "multidict" version = "6.0.2" description = "multidict implementation" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2022,7 +1870,6 @@ files = [ name = "mypy-extensions" version = "0.4.3" description = "Experimental type system extensions for programs checked with the mypy typechecker." -category = "main" optional = false python-versions = "*" files = [ @@ -2034,7 +1881,6 @@ files = [ name = "natsort" version = "8.2.0" description = "Simple yet flexible natural sorting in Python." -category = "main" optional = true python-versions = ">=3.6" files = [ @@ -2050,7 +1896,6 @@ icu = ["PyICU (>=1.0.0)"] name = "nodeenv" version = "1.7.0" description = "Node.js virtual environment builder" -category = "dev" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" files = [ @@ -2065,7 +1910,6 @@ setuptools = "*" name = "numpy" version = "1.25.2" description = "Fundamental package for array computing in Python" -category = "dev" optional = false python-versions = ">=3.9" files = [ @@ -2100,7 +1944,6 @@ files = [ name = "packaging" version = "23.0" description = "Core utilities for Python packages" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2112,7 +1955,6 @@ files = [ name = "parsimonious" version = "0.9.0" description = "(Soon to be) the fastest pure-Python PEG parser I could muster" -category = "dev" optional = false python-versions = "*" files = [ @@ -2126,7 +1968,6 @@ regex = ">=2022.3.15" name = "pastel" version = "0.2.1" description = "Bring colors to your terminal." -category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -2138,7 +1979,6 @@ files = [ name = "pathspec" version = "0.10.2" description = "Utility library for gitignore style pattern matching of file paths." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -2150,7 +1990,6 @@ files = [ name = "pipdeptree" version = "2.12.0" description = "Command line utility to show dependency tree of packages." -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -2166,7 +2005,6 @@ test = ["covdefaults (>=2.3)", "diff-cover (>=7.7)", "pip (>=23.2)", "pytest (>= name = "platformdirs" version = "2.5.4" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2182,7 +2020,6 @@ test = ["appdirs (==1.4.4)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-mock name = "pluggy" version = "1.0.0" description = "plugin and hook calling mechanisms for python" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -2198,7 +2035,6 @@ testing = ["pytest", "pytest-benchmark"] name = "poethepoet" version = "0.22.0" description = "A task runner that works well with poetry." -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -2217,7 +2053,6 @@ poetry-plugin = ["poetry (>=1.0,<2.0)"] name = "poseidon-py" version = "0.1.3" description = "Python implementation of Poseidon hash" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -2276,7 +2111,6 @@ files = [ name = "prometheus-client" version = "0.17.1" description = "Python client for the Prometheus monitoring system." -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -2291,7 +2125,6 @@ twisted = ["twisted"] name = "protobuf" version = "4.23.4" description = "" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -2314,7 +2147,6 @@ files = [ name = "pycryptodome" version = "3.18.0" description = "Cryptographic library for Python" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -2356,7 +2188,6 @@ files = [ name = "Pygments" version = "2.13.0" description = "Pygments is a syntax highlighting package written in Python." -category = "main" optional = true python-versions = ">=3.6" files = [ @@ -2371,7 +2202,6 @@ plugins = ["importlib-metadata"] name = "pylint" version = "2.17.5" description = "python code static checker" -category = "dev" optional = false python-versions = ">=3.7.2" files = [ @@ -2401,7 +2231,6 @@ testutils = ["gitpython (>3)"] name = "pyright" version = "1.1.325" description = "Command line wrapper for pyright" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -2420,7 +2249,6 @@ dev = ["twine (>=3.4.1)"] name = "pyrsistent" version = "0.19.3" description = "Persistent/Functional/Immutable data structures" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -2457,7 +2285,6 @@ files = [ name = "pytest" version = "7.4.0" description = "pytest: simple powerful testing with Python" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -2480,7 +2307,6 @@ testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "no name = "pytest-asyncio" version = "0.21.1" description = "Pytest support for asyncio" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -2499,7 +2325,6 @@ testing = ["coverage (>=6.2)", "flaky (>=3.5.0)", "hypothesis (>=5.7.1)", "mypy name = "pytest-cov" version = "4.1.0" description = "Pytest plugin for measuring coverage." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -2518,7 +2343,6 @@ testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtuale name = "pytest-mock" version = "3.11.1" description = "Thin-wrapper around the mock package for easier use with pytest" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -2536,7 +2360,6 @@ dev = ["pre-commit", "pytest-asyncio", "tox"] name = "pytest-profiling" version = "1.7.0" description = "Profiling plugin for py.test" -category = "dev" optional = false python-versions = "*" files = [ @@ -2556,7 +2379,6 @@ tests = ["pytest-virtualenv"] name = "pytest-rerunfailures" version = "12.0" description = "pytest plugin to re-run tests to eliminate flaky failures" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -2572,7 +2394,6 @@ pytest = ">=6.2" name = "pytest-xdist" version = "3.3.1" description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -2593,7 +2414,6 @@ testing = ["filelock"] name = "python-dotenv" version = "1.0.0" description = "Read key-value pairs from a .env file and set them as environment variables" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -2608,7 +2428,6 @@ cli = ["click (>=5.0)"] name = "pytz" version = "2022.6" description = "World timezone definitions, modern and historical" -category = "main" optional = true python-versions = "*" files = [ @@ -2620,7 +2439,6 @@ files = [ name = "pywin32" version = "306" description = "Python for Window Extensions" -category = "main" optional = false python-versions = "*" files = [ @@ -2644,7 +2462,6 @@ files = [ name = "pyyaml" version = "6.0.1" description = "YAML parser and emitter for Python" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -2653,6 +2470,7 @@ files = [ {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, @@ -2660,8 +2478,15 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, + {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, + {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, + {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, @@ -2678,6 +2503,7 @@ files = [ {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, @@ -2685,6 +2511,7 @@ files = [ {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, @@ -2694,7 +2521,6 @@ files = [ name = "regex" version = "2023.6.3" description = "Alternative regular expression module, to replace re." -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -2792,7 +2618,6 @@ files = [ name = "requests" version = "2.28.1" description = "Python HTTP for Humans." -category = "main" optional = false python-versions = ">=3.7, <4" files = [ @@ -2814,7 +2639,6 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] name = "rlp" version = "3.0.0" description = "A package for Recursive Length Prefix encoding and decoding" -category = "dev" optional = false python-versions = "*" files = [ @@ -2836,7 +2660,6 @@ test = ["hypothesis (==5.19.0)", "pytest (>=6.2.5,<7)", "tox (>=2.9.1,<3)"] name = "ruamel.yaml" version = "0.17.21" description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order" -category = "main" optional = true python-versions = ">=3" files = [ @@ -2855,7 +2678,6 @@ jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"] name = "ruamel.yaml.clib" version = "0.2.7" description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml" -category = "main" optional = true python-versions = ">=3.5" files = [ @@ -2866,7 +2688,8 @@ files = [ {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-win32.whl", hash = "sha256:763d65baa3b952479c4e972669f679fe490eee058d5aa85da483ebae2009d231"}, {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-win_amd64.whl", hash = "sha256:d000f258cf42fec2b1bbf2863c61d7b8918d31ffee905da62dede869254d3b8a"}, {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:045e0626baf1c52e5527bd5db361bc83180faaba2ff586e763d3d5982a876a9e"}, - {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-macosx_12_6_arm64.whl", hash = "sha256:721bc4ba4525f53f6a611ec0967bdcee61b31df5a56801281027a3a6d1c2daf5"}, + {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:1a6391a7cabb7641c32517539ca42cf84b87b667bad38b78d4d42dd23e957c81"}, + {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:9c7617df90c1365638916b98cdd9be833d31d337dbcd722485597b43c4a215bf"}, {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:41d0f1fa4c6830176eef5b276af04c89320ea616655d01327d5ce65e50575c94"}, {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-win32.whl", hash = "sha256:f6d3d39611ac2e4f62c3128a9eed45f19a6608670c5a2f4f07f24e8de3441d38"}, {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-win_amd64.whl", hash = "sha256:da538167284de58a52109a9b89b8f6a53ff8437dd6dc26d33b57bf6699153122"}, @@ -2901,7 +2724,6 @@ files = [ name = "setuptools" version = "68.1.2" description = "Easily download, build, install, upgrade, and uninstall Python packages" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -2918,7 +2740,6 @@ testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs ( name = "six" version = "1.16.0" description = "Python 2 and 3 compatibility utilities" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" files = [ @@ -2930,7 +2751,6 @@ files = [ name = "snowballstemmer" version = "2.2.0" description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." -category = "main" optional = true python-versions = "*" files = [ @@ -2942,7 +2762,6 @@ files = [ name = "soupsieve" version = "2.3.2.post1" description = "A modern CSS selector implementation for Beautiful Soup." -category = "main" optional = true python-versions = ">=3.6" files = [ @@ -2954,7 +2773,6 @@ files = [ name = "sphinx" version = "7.1.2" description = "Python documentation generator" -category = "main" optional = true python-versions = ">=3.8" files = [ @@ -2990,7 +2808,6 @@ test = ["cython", "filelock", "html5lib", "pytest (>=4.6)"] name = "sphinx-autodoc-typehints" version = "1.19.5" description = "Type hints (PEP 484) support for the Sphinx autodoc extension" -category = "main" optional = true python-versions = ">=3.7" files = [ @@ -3010,7 +2827,6 @@ type-comment = ["typed-ast (>=1.5.4)"] name = "sphinx-basic-ng" version = "1.0.0b1" description = "A modern skeleton for Sphinx themes." -category = "main" optional = true python-versions = ">=3.7" files = [ @@ -3028,7 +2844,6 @@ docs = ["furo", "ipython", "myst-parser", "sphinx-copybutton", "sphinx-inline-ta name = "sphinx-jinja2-compat" version = "0.2.0" description = "Patches Jinja2 v3 to restore compatibility with earlier Sphinx versions." -category = "main" optional = true python-versions = ">=3.6" files = [ @@ -3044,7 +2859,6 @@ markupsafe = ">=1" name = "sphinx-prompt" version = "1.5.0" description = "Sphinx directive to add unselectable prompt" -category = "main" optional = true python-versions = "*" files = [ @@ -3059,7 +2873,6 @@ Sphinx = "*" name = "sphinx-tabs" version = "3.4.1" description = "Tabbed views for Sphinx" -category = "main" optional = true python-versions = "~=3.7" files = [ @@ -3080,7 +2893,6 @@ testing = ["bs4", "coverage", "pygments", "pytest (>=7.1,<8)", "pytest-cov", "py name = "sphinx-toolbox" version = "3.2.0" description = "Box of handy tools for Sphinx 🧰 📔" -category = "main" optional = true python-versions = ">=3.7" files = [ @@ -3115,7 +2927,6 @@ testing = ["coincidence (>=0.4.3)", "pygments (>=2.7.4)"] name = "sphinxcontrib-applehelp" version = "1.0.2" description = "sphinxcontrib-applehelp is a sphinx extension which outputs Apple help books" -category = "main" optional = true python-versions = ">=3.5" files = [ @@ -3131,7 +2942,6 @@ test = ["pytest"] name = "sphinxcontrib-devhelp" version = "1.0.2" description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document." -category = "main" optional = true python-versions = ">=3.5" files = [ @@ -3147,7 +2957,6 @@ test = ["pytest"] name = "sphinxcontrib-htmlhelp" version = "2.0.0" description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" -category = "main" optional = true python-versions = ">=3.6" files = [ @@ -3163,7 +2972,6 @@ test = ["html5lib", "pytest"] name = "sphinxcontrib-jsmath" version = "1.0.1" description = "A sphinx extension which renders display math in HTML via JavaScript" -category = "main" optional = true python-versions = ">=3.5" files = [ @@ -3178,7 +2986,6 @@ test = ["flake8", "mypy", "pytest"] name = "sphinxcontrib-qthelp" version = "1.0.3" description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document." -category = "main" optional = true python-versions = ">=3.5" files = [ @@ -3194,7 +3001,6 @@ test = ["pytest"] name = "sphinxcontrib-serializinghtml" version = "1.1.5" description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)." -category = "main" optional = true python-versions = ">=3.5" files = [ @@ -3206,39 +3012,10 @@ files = [ lint = ["docutils-stubs", "flake8", "mypy"] test = ["pytest"] -[[package]] -name = "starknet-devnet" -version = "0.6.2" -description = "A local testnet for Starknet" -category = "dev" -optional = false -python-versions = ">=3.9,<3.10" -files = [ - {file = "starknet_devnet-0.6.2-py3-none-any.whl", hash = "sha256:e3654c7373851512df63ea8e0d6dc92bdb9dd11be1ccd1f7246f4f3943ca046e"}, - {file = "starknet_devnet-0.6.2.tar.gz", hash = "sha256:a58b3b93fe9c5e43c538bbd0a76194c44d20fc9434193ec722d6b0daf088f518"}, -] - -[package.dependencies] -cairo-lang = "0.12.2" -cloudpickle = ">=2.1.0,<2.2.0" -crypto-cpp-py = ">=1.4.0,<1.5.0" -Flask = {version = ">=2.0.3,<2.1.0", extras = ["async"]} -flask-cors = ">=3.0.10,<3.1.0" -gunicorn = ">=20.1.0,<20.2.0" -jsonschema = ">=4.17.0,<4.18.0" -marshmallow = ">=3.17.0,<3.18.0" -marshmallow-dataclass = ">=8.4,<8.5" -poseidon-py = ">=0.1.3,<0.2.0" -pyyaml = ">=6.0.1,<6.1.0" -typing-extensions = ">=4.3.0,<4.4.0" -web3 = ">=6.0.0,<6.1.0" -Werkzeug = ">=2.0.3,<2.1.0" - [[package]] name = "sympy" version = "1.11.1" description = "Computer algebra system (CAS) in Python" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -3253,7 +3030,6 @@ mpmath = ">=0.19" name = "tabulate" version = "0.9.0" description = "Pretty-print tabular data" -category = "main" optional = true python-versions = ">=3.7" files = [ @@ -3268,7 +3044,6 @@ widechars = ["wcwidth"] name = "tomli" version = "1.2.3" description = "A lil' TOML parser" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -3280,7 +3055,6 @@ files = [ name = "tomlkit" version = "0.11.6" description = "Style preserving TOML library" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -3292,7 +3066,6 @@ files = [ name = "toolz" version = "0.12.0" description = "List processing tools and functional utilities" -category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -3304,7 +3077,6 @@ files = [ name = "typeguard" version = "2.13.3" description = "Run-time type checker for Python" -category = "dev" optional = false python-versions = ">=3.5.3" files = [ @@ -3320,7 +3092,6 @@ test = ["mypy", "pytest", "typing-extensions"] name = "typing-extensions" version = "4.3.0" description = "Backported and Experimental Type Hints for Python 3.7+" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -3332,7 +3103,6 @@ files = [ name = "typing-inspect" version = "0.8.0" description = "Runtime inspection utilities for typing module." -category = "main" optional = false python-versions = "*" files = [ @@ -3348,7 +3118,6 @@ typing-extensions = ">=3.7.4" name = "urllib3" version = "1.26.13" description = "HTTP library with thread-safe connection pooling, file post, and more." -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" files = [ @@ -3365,7 +3134,6 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] name = "web3" version = "6.0.0" description = "web3.py" -category = "dev" optional = false python-versions = ">=3.7.2" files = [ @@ -3400,7 +3168,6 @@ tester = ["eth-tester[py-evm] (==v0.8.0-b.3)", "py-geth (>=3.11.0)"] name = "webencodings" version = "0.5.1" description = "Character encoding aliases for legacy web content" -category = "main" optional = true python-versions = "*" files = [ @@ -3412,7 +3179,6 @@ files = [ name = "websockets" version = "11.0.3" description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -3488,26 +3254,10 @@ files = [ {file = "websockets-11.0.3.tar.gz", hash = "sha256:88fc51d9a26b10fc331be344f1781224a375b78488fc343620184e95a4b27016"}, ] -[[package]] -name = "werkzeug" -version = "2.0.3" -description = "The comprehensive WSGI web application library." -category = "dev" -optional = false -python-versions = ">=3.6" -files = [ - {file = "Werkzeug-2.0.3-py3-none-any.whl", hash = "sha256:1421ebfc7648a39a5c58c601b154165d05cf47a3cd0ccb70857cbdacf6c8f2b8"}, - {file = "Werkzeug-2.0.3.tar.gz", hash = "sha256:b863f8ff057c522164b6067c9e28b041161b4be5ba4d0daceeaa50a163822d3c"}, -] - -[package.extras] -watchdog = ["watchdog"] - [[package]] name = "wrapt" version = "1.14.1" description = "Module for decorators, wrappers and monkey patching." -category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" files = [ @@ -3530,6 +3280,16 @@ files = [ {file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8ad85f7f4e20964db4daadcab70b47ab05c7c1cf2a7c1e51087bfaa83831854c"}, {file = "wrapt-1.14.1-cp310-cp310-win32.whl", hash = "sha256:a9a52172be0b5aae932bef82a79ec0a0ce87288c7d132946d645eba03f0ad8a8"}, {file = "wrapt-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:6d323e1554b3d22cfc03cd3243b5bb815a51f5249fdcbb86fda4bf62bab9e164"}, + {file = "wrapt-1.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ecee4132c6cd2ce5308e21672015ddfed1ff975ad0ac8d27168ea82e71413f55"}, + {file = "wrapt-1.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2020f391008ef874c6d9e208b24f28e31bcb85ccff4f335f15a3251d222b92d9"}, + {file = "wrapt-1.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2feecf86e1f7a86517cab34ae6c2f081fd2d0dac860cb0c0ded96d799d20b335"}, + {file = "wrapt-1.14.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:240b1686f38ae665d1b15475966fe0472f78e71b1b4903c143a842659c8e4cb9"}, + {file = "wrapt-1.14.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9008dad07d71f68487c91e96579c8567c98ca4c3881b9b113bc7b33e9fd78b8"}, + {file = "wrapt-1.14.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6447e9f3ba72f8e2b985a1da758767698efa72723d5b59accefd716e9e8272bf"}, + {file = "wrapt-1.14.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:acae32e13a4153809db37405f5eba5bac5fbe2e2ba61ab227926a22901051c0a"}, + {file = "wrapt-1.14.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:49ef582b7a1152ae2766557f0550a9fcbf7bbd76f43fbdc94dd3bf07cc7168be"}, + {file = "wrapt-1.14.1-cp311-cp311-win32.whl", hash = "sha256:358fe87cc899c6bb0ddc185bf3dbfa4ba646f05b1b0b9b5a27c2cb92c2cea204"}, + {file = "wrapt-1.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:26046cd03936ae745a502abf44dac702a5e6880b2b01c29aea8ddf3353b68224"}, {file = "wrapt-1.14.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:43ca3bbbe97af00f49efb06e352eae40434ca9d915906f77def219b88e85d907"}, {file = "wrapt-1.14.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:6b1a564e6cb69922c7fe3a678b9f9a3c54e72b469875aa8018f18b4d1dd1adf3"}, {file = "wrapt-1.14.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:00b6d4ea20a906c0ca56d84f93065b398ab74b927a7a3dbd470f6fc503f95dc3"}, @@ -3581,7 +3341,6 @@ files = [ name = "yarl" version = "1.8.1" description = "Yet another URL library" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -3654,7 +3413,6 @@ multidict = ">=4.0" name = "zipp" version = "3.10.0" description = "Backport of pathlib-compatible object wrapper for zip files" -category = "main" optional = true python-versions = ">=3.7" files = [ @@ -3667,9 +3425,9 @@ docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] [extras] -docs = ["sphinx", "enum-tools", "furo"] +docs = ["enum-tools", "furo", "sphinx"] [metadata] lock-version = "2.0" python-versions = ">=3.8, <3.12" -content-hash = "b8dbf307acb1e5bc8759fb5d9eacc84a4f3c4f21e7bcf02f8b418db2d131b9cf" +content-hash = "bbe878f736e6d9a25af57c558b3d05f23633a88982b96a35a42969ab38b7ed8b" diff --git a/pyproject.toml b/pyproject.toml index d39048a4f..8f24eff25 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -51,7 +51,6 @@ python-dotenv = "^1.0.0" [tool.poetry.group.py39-dev.dependencies] cairo-lang = {version = "0.12.2", python = ">=3.9, <3.10"} -starknet-devnet = {version = "0.6.2", python = ">=3.9, <3.10"} [tool.poe.tasks] test = [ diff --git a/starknet_py/net/client.py b/starknet_py/net/client.py index 7ac6f48b5..9c0aa0da4 100644 --- a/starknet_py/net/client.py +++ b/starknet_py/net/client.py @@ -3,7 +3,7 @@ import asyncio import warnings from abc import ABC, abstractmethod -from typing import List, Optional, Tuple, Union +from typing import List, Optional, Union from starknet_py.net.client_errors import ClientError from starknet_py.net.client_models import ( @@ -21,9 +21,9 @@ Tag, Transaction, TransactionExecutionStatus, - TransactionFinalityStatus, TransactionReceipt, TransactionStatus, + TransactionStatusResponse, ) from starknet_py.net.models.transaction import ( AccountTransaction, @@ -124,6 +124,16 @@ async def get_transaction_receipt( :return: Transaction receipt object on Starknet """ + @abstractmethod + async def get_transaction_status(self, tx_hash: Hash) -> TransactionStatusResponse: + """ + Gets the transaction status (possibly reflecting that the transaction is still in the mempool, + or dropped from it). + + :param tx_hash: Hash of the executed transaction. + :return: Finality and execution status of a transaction. + """ + # https://community.starknet.io/t/efficient-utilization-of-sequencer-capacity-in-starknet-v0-12-1/95607 async def wait_for_tx( self, @@ -155,30 +165,27 @@ async def wait_for_tx( " block have status ACCEPTED_ON_L2." ) + transaction_received = False while True: + retries -= 1 try: - tx_receipt = await self.get_transaction_receipt(tx_hash=tx_hash) - - deprecated_status = _status_to_finality_execution(tx_receipt.status) - finality_status = tx_receipt.finality_status or deprecated_status[0] - execution_status = tx_receipt.execution_status or deprecated_status[1] + if not transaction_received: + tx_status = await self.get_transaction_status(tx_hash=tx_hash) - if execution_status == TransactionExecutionStatus.REJECTED: - raise TransactionRejectedError(message=tx_receipt.rejection_reason) + if tx_status.finality_status == TransactionStatus.REJECTED: + raise TransactionRejectedError() - if execution_status == TransactionExecutionStatus.REVERTED: - raise TransactionRevertedError(message=tx_receipt.revert_reason) + transaction_received = True + else: + tx_receipt = await self.get_transaction_receipt(tx_hash=tx_hash) - if execution_status == TransactionExecutionStatus.SUCCEEDED: + if ( + tx_receipt.execution_status + == TransactionExecutionStatus.REVERTED + ): + raise TransactionRevertedError(message=tx_receipt.revert_reason) return tx_receipt - if finality_status in ( - TransactionFinalityStatus.ACCEPTED_ON_L2, - TransactionFinalityStatus.ACCEPTED_ON_L1, - ): - return tx_receipt - - retries -= 1 if retries == 0: raise TransactionNotReceivedError() @@ -189,7 +196,7 @@ async def wait_for_tx( except ClientError as exc: if "Transaction hash not found" not in exc.message: raise exc - retries -= 1 + if retries == 0: raise TransactionNotReceivedError from exc @@ -303,14 +310,3 @@ async def get_contract_nonce( :param block_number: Block's number or literals `"pending"` or `"latest"` :return: The last nonce used for the given contract """ - - -def _status_to_finality_execution( - status: Optional[TransactionStatus], -) -> Tuple[Optional[TransactionFinalityStatus], Optional[TransactionExecutionStatus]]: - if status is None: - return None, None - finality_statuses = [finality.value for finality in TransactionFinalityStatus] - if status.value in finality_statuses: - return TransactionFinalityStatus(status.value), None - return None, TransactionExecutionStatus(status.value) diff --git a/starknet_py/net/client_models.py b/starknet_py/net/client_models.py index 9315623cc..f71171b8b 100644 --- a/starknet_py/net/client_models.py +++ b/starknet_py/net/client_models.py @@ -182,13 +182,10 @@ class TransactionStatus(Enum): Enum representing transaction statuses. """ - NOT_RECEIVED = "NOT_RECEIVED" RECEIVED = "RECEIVED" + REJECTED = "REJECTED" ACCEPTED_ON_L2 = "ACCEPTED_ON_L2" ACCEPTED_ON_L1 = "ACCEPTED_ON_L1" - REJECTED = "REJECTED" - REVERTED = "REVERTED" - SUCCEEDED = "SUCCEEDED" class TransactionExecutionStatus(Enum): @@ -196,9 +193,8 @@ class TransactionExecutionStatus(Enum): Enum representing transaction execution statuses. """ - REJECTED = "REJECTED" - REVERTED = "REVERTED" SUCCEEDED = "SUCCEEDED" + REVERTED = "REVERTED" class TransactionFinalityStatus(Enum): @@ -206,8 +202,6 @@ class TransactionFinalityStatus(Enum): Enum representing transaction finality statuses. """ - NOT_RECEIVED = "NOT_RECEIVED" - RECEIVED = "RECEIVED" ACCEPTED_ON_L2 = "ACCEPTED_ON_L2" ACCEPTED_ON_L1 = "ACCEPTED_ON_L1" @@ -231,7 +225,7 @@ class ExecutionResources: memory_holes: Optional[int] = None -# TODO (#1179): split into PendingTransactionReceipt and TransactionReceipt +# TODO (#1219): split into PendingTransactionReceipt and TransactionReceipt @dataclass class TransactionReceipt: """ @@ -241,28 +235,22 @@ class TransactionReceipt: # pylint: disable=too-many-instance-attributes transaction_hash: int + execution_status: TransactionExecutionStatus + finality_status: TransactionFinalityStatus + execution_resources: ExecutionResources + type: TransactionType + events: List[Event] = field(default_factory=list) messages_sent: List[L2toL1Message] = field(default_factory=list) - execution_status: Optional[TransactionExecutionStatus] = None - finality_status: Optional[TransactionFinalityStatus] = None - # TODO (#1179): remove status field once devnet is updated - status: Optional[ - TransactionStatus - ] = None # replaced by execution and finality status in RPC v0.4.0-rc1 - - type: Optional[TransactionType] = None contract_address: Optional[int] = None block_number: Optional[int] = None block_hash: Optional[int] = None actual_fee: int = 0 - # TODO (#1179): this field should be required - execution_resources: Optional[ExecutionResources] = None message_hash: Optional[int] = None # L1_HANDLER_TXN_RECEIPT-only - rejection_reason: Optional[str] = None revert_reason: Optional[str] = None @@ -316,10 +304,8 @@ class PendingStarknetBlock: parent_block_hash: int timestamp: int sequencer_address: int - # TODO (#1179): this field should be required - l1_gas_price: Optional[ResourcePrice] = None - # TODO (#1179): this field should be required - starknet_version: Optional[str] = None + l1_gas_price: ResourcePrice + starknet_version: str @dataclass @@ -332,10 +318,8 @@ class PendingStarknetBlockWithTxHashes: parent_block_hash: int timestamp: int sequencer_address: int - # TODO (#1179): this field should be required - l1_gas_price: Optional[ResourcePrice] = None - # TODO (#1179): this field should be required - starknet_version: Optional[str] = None + l1_gas_price: ResourcePrice + starknet_version: str @dataclass @@ -344,7 +328,7 @@ class StarknetBlockCommon: Dataclass representing a block header. """ - # TODO (#1179): change that into composition + # TODO (#1219): change that into composition # pylint: disable=too-many-instance-attributes block_hash: int @@ -353,10 +337,8 @@ class StarknetBlockCommon: root: int timestamp: int sequencer_address: int - # TODO (#1179): this field should be required - l1_gas_price: Optional[ResourcePrice] - # TODO (#1179): this field should be required - starknet_version: Optional[str] + l1_gas_price: ResourcePrice + starknet_version: str @dataclass diff --git a/starknet_py/net/full_node_client.py b/starknet_py/net/full_node_client.py index 8f34692d9..e09e4f052 100644 --- a/starknet_py/net/full_node_client.py +++ b/starknet_py/net/full_node_client.py @@ -661,16 +661,9 @@ async def spec_version(self) -> str: return res async def get_transaction_status(self, tx_hash: Hash) -> TransactionStatusResponse: - """ - Gets the transaction status (possibly reflecting that the transaction is still in the mempool, - or dropped from it). - - :param tx_hash: Hash of the executed transaction. - :return: Finality and execution status of a transaction. - """ res = await self._client.call( method_name="getTransactionStatus", - params={"transaction_hash": tx_hash}, + params={"transaction_hash": _to_rpc_felt(tx_hash)}, ) return cast( TransactionStatusResponse, diff --git a/starknet_py/net/http_client.py b/starknet_py/net/http_client.py index 15cef237a..4a4498a53 100644 --- a/starknet_py/net/http_client.py +++ b/starknet_py/net/http_client.py @@ -63,8 +63,8 @@ async def call(self, method_name: str, params: dict): payload = { "jsonrpc": "2.0", "method": f"starknet_{method_name}", - "params": params, "id": 0, + "params": params if params else [], } result = await self.request( diff --git a/starknet_py/net/schemas/rpc.py b/starknet_py/net/schemas/rpc.py index 2d37d1b56..878ab90c3 100644 --- a/starknet_py/net/schemas/rpc.py +++ b/starknet_py/net/schemas/rpc.py @@ -134,18 +134,13 @@ def make_dataclass(self, data, **kwargs) -> ExecutionResources: class TransactionReceiptSchema(Schema): transaction_hash = Felt(data_key="transaction_hash", required=True) - # replaced by execution and finality status in RPC v0.4.0-rc1 - status = StatusField(data_key="status", load_default=None) - execution_status = ExecutionStatusField( - data_key="execution_status", load_default=None - ) - finality_status = FinalityStatusField(data_key="finality_status", load_default=None) + execution_status = ExecutionStatusField(data_key="execution_status", required=True) + finality_status = FinalityStatusField(data_key="finality_status", required=True) 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) type = TransactionTypeField(data_key="type", load_default=None) contract_address = Felt(data_key="contract_address", load_default=None) - rejection_reason = fields.String(data_key="status_data", load_default=None) revert_reason = fields.String(data_key="revert_reason", load_default=None) events = fields.List( fields.Nested(EventSchema()), data_key="events", load_default=[] @@ -154,9 +149,8 @@ class TransactionReceiptSchema(Schema): fields.Nested(L2toL1MessageSchema()), data_key="messages_sent", load_default=[] ) message_hash = Felt(data_key="message_hash", load_default=None) - # TODO (#1179): this field should be required execution_resources = fields.Nested( - ExecutionResourcesSchema(), data_key="execution_resources", load_default=None + ExecutionResourcesSchema(), data_key="execution_resources", required=True ) @post_load @@ -281,12 +275,10 @@ class PendingStarknetBlockSchema(Schema): required=True, ) timestamp = fields.Integer(data_key="timestamp", required=True) - # TODO (#1179): this field should be required l1_gas_price = fields.Nested( - ResourcePriceSchema(), data_key="l1_gas_price", load_default=None + ResourcePriceSchema(), data_key="l1_gas_price", required=True ) - # TODO (#1179): this field should be required - starknet_version = fields.String(data_key="starknet_version", load_default=None) + starknet_version = fields.String(data_key="starknet_version", required=True) @post_load def make_dataclass(self, data, **kwargs): @@ -306,11 +298,9 @@ class StarknetBlockSchema(Schema): required=True, ) timestamp = fields.Integer(data_key="timestamp", required=True) - # TODO (#1179): this field should be required - starknet_version = fields.String(data_key="starknet_version", load_default=None) - # TODO (#1179): this field should be required + starknet_version = fields.String(data_key="starknet_version", required=True) l1_gas_price = fields.Nested( - ResourcePriceSchema(), data_key="l1_gas_price", load_default=None + ResourcePriceSchema(), data_key="l1_gas_price", required=True ) @post_load @@ -327,11 +317,9 @@ class StarknetBlockWithTxHashesSchema(Schema): root = NonPrefixedHex(data_key="new_root", required=True) transactions = fields.List(Felt(), data_key="transactions", required=True) timestamp = fields.Integer(data_key="timestamp", required=True) - # TODO (#1179): this field should be required - starknet_version = fields.String(data_key="starknet_version", load_default=None) - # TODO (#1179): this field should be required + starknet_version = fields.String(data_key="starknet_version", required=True) l1_gas_price = fields.Nested( - ResourcePriceSchema(), data_key="l1_gas_price", load_default=None + ResourcePriceSchema(), data_key="l1_gas_price", required=True ) @post_load @@ -366,11 +354,9 @@ class PendingStarknetBlockWithTxHashesSchema(Schema): sequencer_address = Felt(data_key="sequencer_address", required=True) transactions = fields.List(Felt(), data_key="transactions", required=True) timestamp = fields.Integer(data_key="timestamp", required=True) - # TODO (#1179): this field should be required - starknet_version = fields.String(data_key="starknet_version", load_default=None) - # TODO (#1179): this field should be required + starknet_version = fields.String(data_key="starknet_version", required=True) l1_gas_price = fields.Nested( - ResourcePriceSchema(), data_key="l1_gas_price", load_default=None + ResourcePriceSchema(), data_key="l1_gas_price", required=True ) @post_load diff --git a/starknet_py/tests/e2e/account/account_test.py b/starknet_py/tests/e2e/account/account_test.py index a535bd5db..5d55c4e85 100644 --- a/starknet_py/tests/e2e/account/account_test.py +++ b/starknet_py/tests/e2e/account/account_test.py @@ -15,7 +15,8 @@ DeployAccountTransactionResponse, EstimatedFee, SierraContractClass, - TransactionStatus, + TransactionExecutionStatus, + TransactionFinalityStatus, ) from starknet_py.net.full_node_client import FullNodeClient from starknet_py.net.models import StarknetChainId @@ -297,7 +298,6 @@ async def test_sign_deploy_account_transaction_auto_estimate( assert signed_tx.constructor_calldata == calldata -@pytest.mark.skip("No 0.12.1 devnet") @pytest.mark.asyncio async def test_deploy_account(client, deploy_account_details_factory, map_contract): address, key_pair, salt, class_hash = await deploy_account_details_factory.get() @@ -318,7 +318,6 @@ async def test_deploy_account(client, deploy_account_details_factory, map_contra assert isinstance(account, BaseAccount) assert account.address == address - # Test making a tx res = await account.execute( calls=Call( to_addr=map_contract.address, @@ -329,10 +328,7 @@ async def test_deploy_account(client, deploy_account_details_factory, map_contra ) tx_receipt = await account.client.wait_for_tx(res.transaction_hash) - assert tx_receipt.finality_status in ( - TransactionStatus.ACCEPTED_ON_L1, - TransactionStatus.ACCEPTED_ON_L2, - ) + assert tx_receipt.execution_status == TransactionExecutionStatus.SUCCEEDED @pytest.mark.asyncio @@ -447,45 +443,8 @@ async def test_deploy_account_uses_custom_calldata( assert tx.constructor_calldata == calldata -@pytest.mark.asyncio -async def test_sign_invoke_tx_for_fee_estimation(account, map_contract): - call = map_contract.functions["put"].prepare(key=40, value=50) - transaction = await account.sign_invoke_transaction(calls=call, max_fee=MAX_FEE) - - estimate_fee_transaction = await account.sign_for_fee_estimate(transaction) - - estimation = await account.client.estimate_fee(estimate_fee_transaction) - assert estimation.overall_fee > 0 - - # Verify that the transaction signed for fee estimation cannot be sent - with pytest.raises(ClientError): - await account.client.send_transaction(estimate_fee_transaction) - - # Verify that original transaction can be sent - result = await account.client.send_transaction(transaction) - await account.client.wait_for_tx(result.transaction_hash) - - -@pytest.mark.asyncio -async def test_sign_declare_tx_for_fee_estimation(account, map_compiled_contract): - transaction = await account.sign_declare_transaction( - compiled_contract=map_compiled_contract, max_fee=MAX_FEE - ) - - estimate_fee_transaction = await account.sign_for_fee_estimate(transaction) - - estimation = await account.client.estimate_fee(estimate_fee_transaction) - assert estimation.overall_fee > 0 - - # Verify that the transaction signed for fee estimation cannot be sent - with pytest.raises(ClientError): - await account.client.declare(estimate_fee_transaction) - - # Verify that original transaction can be sent - result = await account.client.declare(transaction) - await account.client.wait_for_tx(result.transaction_hash) - - +# TODO (#1219): investigate why this test fails +@pytest.mark.skip @pytest.mark.asyncio async def test_sign_deploy_account_tx_for_fee_estimation( client, deploy_account_details_factory @@ -615,8 +574,7 @@ async def test_argent_cairo1_account_execute( tx_hash=execute.transaction_hash ) - # TODO (#1179): devnet 0.3.0 still return STATUS instead of FINALITY_STATUS - assert receipt.status == TransactionStatus.ACCEPTED_ON_L2 + assert receipt.finality_status == TransactionFinalityStatus.ACCEPTED_ON_L2 # verify that the previous call was executed get_balance_call = Call( diff --git a/starknet_py/tests/e2e/client/client_test.py b/starknet_py/tests/e2e/client/client_test.py index c7326d698..646ab8c91 100644 --- a/starknet_py/tests/e2e/client/client_test.py +++ b/starknet_py/tests/e2e/client/client_test.py @@ -1,8 +1,7 @@ # pylint: disable=too-many-arguments -import asyncio import dataclasses from typing import Tuple -from unittest.mock import AsyncMock, patch +from unittest.mock import AsyncMock, Mock, patch import pytest from aiohttp import ClientSession @@ -16,14 +15,17 @@ DeclaredContractHash, DeclareTransaction, DeployAccountTransaction, + ExecutionResources, InvokeTransaction, L1HandlerTransaction, PendingBlockStateUpdate, - ReplacedClass, SierraContractClass, SierraEntryPointsByType, + TransactionExecutionStatus, + TransactionFinalityStatus, TransactionReceipt, TransactionStatus, + TransactionStatusResponse, TransactionType, ) from starknet_py.net.full_node_client import FullNodeClient @@ -35,6 +37,7 @@ from starknet_py.transaction_errors import ( TransactionNotReceivedError, TransactionRejectedError, + TransactionRevertedError, ) @@ -226,7 +229,7 @@ async def test_add_transaction(map_contract, client, account): await client.wait_for_tx(result.transaction_hash) transaction_receipt = await client.get_transaction_receipt(result.transaction_hash) - assert transaction_receipt.status != TransactionStatus.NOT_RECEIVED + assert transaction_receipt.execution_status == TransactionExecutionStatus.SUCCEEDED assert transaction_receipt.type == TransactionType.INVOKE @@ -249,80 +252,79 @@ async def test_get_class_by_hash(client, class_hash): @pytest.mark.asyncio -async def test_wait_for_tx_accepted(client, get_tx_receipt_path): +async def test_wait_for_tx_accepted(client, get_tx_receipt_path, get_tx_status_path): with patch( get_tx_receipt_path, AsyncMock(), - ) as mocked_receipt: + ) as mocked_receipt, patch(get_tx_status_path, AsyncMock()) as mocked_status: mocked_receipt.return_value = TransactionReceipt( transaction_hash=0x1, - status=TransactionStatus.ACCEPTED_ON_L2, block_number=1, type=TransactionType.INVOKE, + execution_status=TransactionExecutionStatus.SUCCEEDED, + finality_status=TransactionFinalityStatus.ACCEPTED_ON_L2, + execution_resources=Mock(spec=ExecutionResources), ) + + mocked_status.return_value = TransactionStatusResponse( + finality_status=TransactionStatus.RECEIVED + ) + tx_receipt = await client.wait_for_tx(tx_hash=0x1) - assert tx_receipt.status == TransactionStatus.ACCEPTED_ON_L2 + assert tx_receipt.finality_status == TransactionFinalityStatus.ACCEPTED_ON_L2 @pytest.mark.asyncio -@pytest.mark.parametrize( - "status, exception, exc_message", - ( - ( - TransactionStatus.REJECTED, - TransactionRejectedError, - "Unknown Starknet error", - ), - ), -) -async def test_wait_for_tx_rejected( - status, exception, exc_message, client, get_tx_receipt_path -): +async def test_wait_for_tx_reverted(client, get_tx_receipt_path, get_tx_status_path): + exc_message = "Unknown Starknet error" + with patch( get_tx_receipt_path, AsyncMock(), - ) as mocked_receipt: + ) as mocked_receipt, patch(get_tx_status_path, AsyncMock()) as mocked_status: mocked_receipt.return_value = TransactionReceipt( transaction_hash=0x1, - status=status, block_number=1, - rejection_reason=exc_message, type=TransactionType.INVOKE, + execution_status=TransactionExecutionStatus.REVERTED, + finality_status=Mock(spec=TransactionFinalityStatus), + execution_resources=Mock(spec=ExecutionResources), + revert_reason=exc_message, + ) + + mocked_status.return_value = TransactionStatusResponse( + finality_status=TransactionStatus.RECEIVED ) - with pytest.raises(exception) as err: + with pytest.raises(TransactionRevertedError) as err: await client.wait_for_tx(tx_hash=0x1) assert exc_message in err.value.message @pytest.mark.asyncio -async def test_wait_for_tx_cancelled(client, get_tx_receipt_path): - with patch( - get_tx_receipt_path, - AsyncMock(), - ) as mocked_receipt: - mocked_receipt.return_value = TransactionReceipt( - transaction_hash=0x1, - status=TransactionStatus.NOT_RECEIVED, - block_number=1, - type=TransactionType.INVOKE, +async def test_wait_for_tx_rejected(client, get_tx_status_path): + with patch(get_tx_status_path, AsyncMock()) as mocked_status: + mocked_status.return_value = TransactionStatusResponse( + finality_status=TransactionStatus.REJECTED ) - task = asyncio.create_task(client.wait_for_tx(tx_hash=0x1)) - await asyncio.sleep(1) - task.cancel() - with pytest.raises(TransactionNotReceivedError): - await task + with pytest.raises(TransactionRejectedError): + await client.wait_for_tx(tx_hash=0x1) @pytest.mark.asyncio -async def test_wait_for_tx_unknown_error(client, get_tx_receipt_path): +async def test_wait_for_tx_unknown_error( + client, get_tx_receipt_path, get_tx_status_path +): with patch( get_tx_receipt_path, AsyncMock(), - ) as mocked_receipt: + ) as mocked_receipt, patch(get_tx_status_path, AsyncMock()) as mocked_status: mocked_receipt.side_effect = ClientError(message="Unknown error") + mocked_status.return_value = TransactionStatusResponse( + finality_status=TransactionStatus.RECEIVED + ) with pytest.raises(ClientError, match="Unknown error"): await client.wait_for_tx(tx_hash="0x2137") @@ -339,7 +341,7 @@ async def test_declare_contract(account, map_compiled_contract): await client.wait_for_tx(result.transaction_hash) transaction_receipt = await client.get_transaction_receipt(result.transaction_hash) - assert transaction_receipt.status != TransactionStatus.NOT_RECEIVED + assert transaction_receipt.execution_status == TransactionExecutionStatus.SUCCEEDED assert transaction_receipt.transaction_hash assert 0 < transaction_receipt.actual_fee <= MAX_FEE assert transaction_receipt.type == TransactionType.DECLARE @@ -420,6 +422,8 @@ async def test_get_l1_handler_transaction(client): assert transaction.nonce == 0x34C20 +# TODO (#1219): investigate why test fails in batch but passes when single run +@pytest.mark.skip @pytest.mark.run_on_devnet @pytest.mark.asyncio async def test_state_update_declared_contract_hashes( @@ -533,30 +537,22 @@ async def test_get_block_with_declare_v2( ) +# TODO (#1219): add assert for replaced_class once it is fixed in devnet @pytest.mark.asyncio async def test_get_new_state_update( client, cairo1_hello_starknet_class_hash: int, declare_v2_hello_starknet: DeclareV2, block_with_declare_v2_number: int, - replaced_class: Tuple[int, int, int], ): - state_update = await client.get_state_update( + state_update_first = await client.get_state_update( block_number=block_with_declare_v2_number ) - assert state_update.state_diff.replaced_classes == [] + assert state_update_first.state_diff.replaced_classes == [] assert ( DeclaredContractHash( class_hash=cairo1_hello_starknet_class_hash, compiled_class_hash=declare_v2_hello_starknet.compiled_class_hash, ) - in state_update.state_diff.declared_classes - ) - - (block_number, contract_address, class_hash) = replaced_class - state_update = await client.get_state_update(block_number=block_number) - - assert ( - ReplacedClass(contract_address=contract_address, class_hash=class_hash) - in state_update.state_diff.replaced_classes # pyright: ignore + in state_update_first.state_diff.declared_classes ) diff --git a/starknet_py/tests/e2e/client/fixtures/transactions.py b/starknet_py/tests/e2e/client/fixtures/transactions.py index c0b501770..c859a5833 100644 --- a/starknet_py/tests/e2e/client/fixtures/transactions.py +++ b/starknet_py/tests/e2e/client/fixtures/transactions.py @@ -99,17 +99,20 @@ async def replaced_class(account: Account, map_class_hash: int) -> Tuple[int, in "replace_class_compiled.json", directory=CONTRACTS_COMPILED_V0_DIR ) - declare_result = await Contract.declare(account, compiled_contract, max_fee=MAX_FEE) - await declare_result.wait_for_acceptance() + declare_result = await ( + await Contract.declare(account, compiled_contract, max_fee=MAX_FEE) + ).wait_for_acceptance() - deploy_result = await declare_result.deploy(max_fee=MAX_FEE) - await deploy_result.wait_for_acceptance() + deploy_result = await ( + await declare_result.deploy(max_fee=MAX_FEE) + ).wait_for_acceptance() contract = deploy_result.deployed_contract - resp = await contract.functions["replace_implementation"].invoke( - new_class=map_class_hash, max_fee=MAX_FEE - ) - await resp.wait_for_acceptance() + resp = await ( + await contract.functions["replace_implementation"].invoke( + new_class=map_class_hash, max_fee=MAX_FEE + ) + ).wait_for_acceptance() return cast(int, resp.block_number), contract.address, map_class_hash diff --git a/starknet_py/tests/e2e/client/full_node_test.py b/starknet_py/tests/e2e/client/full_node_test.py index 309ca7a31..fd36e8365 100644 --- a/starknet_py/tests/e2e/client/full_node_test.py +++ b/starknet_py/tests/e2e/client/full_node_test.py @@ -319,7 +319,7 @@ async def test_get_events_nonexistent_starting_block( client, simple_storage_with_event_contract: Contract, ): - with pytest.raises(ClientError, match="Block not found"): + with pytest.raises(ClientError, match="No block found"): await client.get_events( from_block_number=10000, to_block_hash="latest", @@ -332,6 +332,9 @@ async def test_get_events_nonexistent_starting_block( @pytest.mark.asyncio async def test_get_block_number(client): + # pylint: disable=protected-access + await create_empty_block(client._client) + block_number = await client.get_block_number() # pylint: disable=protected-access @@ -343,6 +346,9 @@ async def test_get_block_number(client): @pytest.mark.asyncio async def test_get_block_hash_and_number(client): + # pylint: disable=protected-access + await create_empty_block(client._client) + block_hash_and_number = await client.get_block_hash_and_number() assert isinstance(block_hash_and_number, BlockHashAndNumber) @@ -394,39 +400,31 @@ async def test_get_syncing_status(client): # ---------------------------- Trace API tests ---------------------------- -# TODO (#1179): remove @pytest.mark.skip -@pytest.mark.skip(reason="Old devnet without RPC 0.5.0") @pytest.mark.asyncio -async def test_simulate_transactions_skip_validate( - full_node_account, deployed_balance_contract -): +async def test_simulate_transactions_skip_validate(account, deployed_balance_contract): assert isinstance(deployed_balance_contract, Contract) call = Call( to_addr=deployed_balance_contract.address, selector=get_selector_from_name("increase_balance"), calldata=[0x10], ) - invoke_tx = await full_node_account.sign_invoke_transaction( - calls=call, auto_estimate=True - ) + invoke_tx = await account.sign_invoke_transaction(calls=call, auto_estimate=True) invoke_tx = dataclasses.replace(invoke_tx, signature=[]) - simulated_txs = await full_node_account.client.simulate_transactions( + simulated_txs = await account.client.simulate_transactions( transactions=[invoke_tx], skip_validate=True, block_number="latest" ) assert simulated_txs[0].transaction_trace.validate_invocation is None - with pytest.raises(ClientError, match=r".*INVALID_SIGNATURE_LENGTH.*"): - _ = await full_node_account.client.simulate_transactions( + with pytest.raises(ClientError, match="Contract error"): + await account.client.simulate_transactions( transactions=[invoke_tx], block_number="latest" ) -# TODO (#1179): remove @pytest.mark.skip -@pytest.mark.skip(reason="Old devnet without RPC 0.5.0") @pytest.mark.asyncio async def test_simulate_transactions_skip_fee_charge( - full_node_account, deployed_balance_contract + account, deployed_balance_contract ): assert isinstance(deployed_balance_contract, Contract) call = Call( @@ -434,34 +432,24 @@ async def test_simulate_transactions_skip_fee_charge( selector=get_selector_from_name("increase_balance"), calldata=[0x10], ) - invoke_tx = await full_node_account.sign_invoke_transaction( - calls=call, auto_estimate=True - ) + invoke_tx = await account.sign_invoke_transaction(calls=call, auto_estimate=True) - # TODO (#1179): change this test - # because of python devnet, the SKIP_FEE_CHARGE flag isn't accepted - with pytest.raises(ClientError, match=r".*SKIP_FEE_CHARGE.*"): - _ = await full_node_account.client.simulate_transactions( - transactions=[invoke_tx], skip_fee_charge=True, block_number="latest" - ) + simulated_txs = await account.client.simulate_transactions( + transactions=[invoke_tx], skip_fee_charge=True, block_number="latest" + ) + assert simulated_txs is not None -# TODO (#1179): remove @pytest.mark.skip -@pytest.mark.skip(reason="Old devnet without RPC 0.5.0") @pytest.mark.asyncio -async def test_simulate_transactions_invoke( - full_node_account, deployed_balance_contract -): +async def test_simulate_transactions_invoke(account, deployed_balance_contract): assert isinstance(deployed_balance_contract, Contract) call = Call( to_addr=deployed_balance_contract.address, selector=get_selector_from_name("increase_balance"), calldata=[0x10], ) - invoke_tx = await full_node_account.sign_invoke_transaction( - calls=call, auto_estimate=True - ) - simulated_txs = await full_node_account.client.simulate_transactions( + invoke_tx = await account.sign_invoke_transaction(calls=call, auto_estimate=True) + simulated_txs = await account.client.simulate_transactions( transactions=[invoke_tx], block_number="latest" ) @@ -469,10 +457,10 @@ async def test_simulate_transactions_invoke( assert isinstance(simulated_txs[0].transaction_trace, InvokeTransactionTrace) assert simulated_txs[0].transaction_trace.execute_invocation is not None - invoke_tx = await full_node_account.sign_invoke_transaction( + invoke_tx = await account.sign_invoke_transaction( calls=[call, call], auto_estimate=True ) - simulated_txs = await full_node_account.client.simulate_transactions( + simulated_txs = await account.client.simulate_transactions( transactions=[invoke_tx], block_number="latest" ) @@ -481,18 +469,16 @@ async def test_simulate_transactions_invoke( assert simulated_txs[0].transaction_trace.execute_invocation is not None -# TODO (#1179): remove @pytest.mark.skip -@pytest.mark.skip(reason="Old devnet without RPC 0.5.0") @pytest.mark.asyncio -async def test_simulate_transactions_declare(full_node_account): +async def test_simulate_transactions_declare(account): compiled_contract = read_contract( "map_compiled.json", directory=CONTRACTS_COMPILED_V0_DIR ) - declare_tx = await full_node_account.sign_declare_transaction( + declare_tx = await account.sign_declare_transaction( compiled_contract, max_fee=int(1e16) ) - simulated_txs = await full_node_account.client.simulate_transactions( + simulated_txs = await account.client.simulate_transactions( transactions=[declare_tx], block_number="latest" ) @@ -501,21 +487,15 @@ async def test_simulate_transactions_declare(full_node_account): assert simulated_txs[0].transaction_trace.validate_invocation is not None -# TODO (#1179): remove @pytest.mark.skip -@pytest.mark.skip(reason="Old devnet without RPC 0.5.0") @pytest.mark.asyncio -async def test_simulate_transactions_two_txs( - full_node_account, deployed_balance_contract -): +async def test_simulate_transactions_two_txs(account, deployed_balance_contract): assert isinstance(deployed_balance_contract, Contract) call = Call( to_addr=deployed_balance_contract.address, selector=get_selector_from_name("increase_balance"), calldata=[0x10], ) - invoke_tx = await full_node_account.sign_invoke_transaction( - calls=call, auto_estimate=True - ) + invoke_tx = await account.sign_invoke_transaction(calls=call, auto_estimate=True) compiled_v2_contract = read_contract( "test_contract_declare_compiled.json", directory=CONTRACTS_COMPILED_V1_DIR @@ -526,7 +506,7 @@ async def test_simulate_transactions_two_txs( casm_class = create_casm_class(compiled_v2_contract_casm) casm_class_hash = compute_casm_class_hash(casm_class) - declare_v2_tx = await full_node_account.sign_declare_v2_transaction( + declare_v2_tx = await account.sign_declare_v2_transaction( compiled_contract=compiled_v2_contract, compiled_class_hash=casm_class_hash, # because raw calls do not increment nonce, it needs to be done manually @@ -534,7 +514,7 @@ async def test_simulate_transactions_two_txs( max_fee=int(1e16), ) - simulated_txs = await full_node_account.client.simulate_transactions( + simulated_txs = await account.client.simulate_transactions( transactions=[invoke_tx, declare_v2_tx], block_number="latest" ) @@ -548,8 +528,6 @@ async def test_simulate_transactions_two_txs( assert simulated_txs[1].transaction_trace.validate_invocation is not None -# TODO (#1179): remove @pytest.mark.skip -@pytest.mark.skip(reason="Old devnet without RPC 0.5.0") @pytest.mark.asyncio async def test_simulate_transactions_deploy_account( client, deploy_account_details_factory diff --git a/starknet_py/tests/e2e/contract_interaction/interaction_test.py b/starknet_py/tests/e2e/contract_interaction/interaction_test.py index 012618699..76e69d43f 100644 --- a/starknet_py/tests/e2e/contract_interaction/interaction_test.py +++ b/starknet_py/tests/e2e/contract_interaction/interaction_test.py @@ -13,25 +13,24 @@ async def test_max_fee_is_set_in_sent_invoke(map_contract): key = 2 value = 3 - max_fee_for_invoke = 248400000000 - prepared_call = map_contract.functions["put"].prepare( - key, value, max_fee=max_fee_for_invoke - ) - assert prepared_call.max_fee == max_fee_for_invoke + prepared_call = map_contract.functions["put"].prepare(key, value, max_fee=MAX_FEE) + assert prepared_call.max_fee == MAX_FEE + invocation = await prepared_call.invoke() - assert invocation.invoke_transaction.max_fee == max_fee_for_invoke + assert invocation.invoke_transaction.max_fee == MAX_FEE invocation = await map_contract.functions["put"].invoke( - key, value, max_fee=max_fee_for_invoke + 100 + key, value, max_fee=MAX_FEE + 100 ) - assert invocation.invoke_transaction.max_fee == max_fee_for_invoke + 100 + assert invocation.invoke_transaction.max_fee == MAX_FEE + 100 prepared_call = map_contract.functions["put"].prepare( - key, value, max_fee=max_fee_for_invoke + 200 + key, value, max_fee=MAX_FEE + 200 ) - assert prepared_call.max_fee == max_fee_for_invoke + 200 - invocation = await prepared_call.invoke(max_fee=max_fee_for_invoke + 300) - assert invocation.invoke_transaction.max_fee == max_fee_for_invoke + 300 + assert prepared_call.max_fee == MAX_FEE + 200 + + invocation = await prepared_call.invoke(max_fee=MAX_FEE + 300) + assert invocation.invoke_transaction.max_fee == MAX_FEE + 300 @pytest.mark.asyncio @@ -87,13 +86,12 @@ async def test_latest_max_fee_takes_precedence(map_contract): key = 2 value = 3 - max_fee = 248400000000 prepared_function = map_contract.functions["put"].prepare( - key, value, max_fee=max_fee + key, value, max_fee=MAX_FEE ) - invocation = await prepared_function.invoke(max_fee=max_fee + 30) + invocation = await prepared_function.invoke(max_fee=MAX_FEE + 30) - assert invocation.invoke_transaction.max_fee == max_fee + 30 + assert invocation.invoke_transaction.max_fee == MAX_FEE + 30 @pytest.mark.asyncio diff --git a/starknet_py/tests/e2e/contract_interaction/v1_interaction_test.py b/starknet_py/tests/e2e/contract_interaction/v1_interaction_test.py index 355c23868..23281e10a 100644 --- a/starknet_py/tests/e2e/contract_interaction/v1_interaction_test.py +++ b/starknet_py/tests/e2e/contract_interaction/v1_interaction_test.py @@ -1,3 +1,5 @@ +import sys + import pytest from starknet_py.cairo.felt import decode_shortstring, encode_shortstring @@ -5,7 +7,13 @@ from starknet_py.tests.e2e.fixtures.constants import MAX_FEE from starknet_py.tests.e2e.fixtures.contracts import deploy_v1_contract +# TODO (#1219): investigate why some of these tests fails for contracts_compiled_v1 + +@pytest.mark.skipif( + "--contract_dir=v2" not in sys.argv, + reason="Some cairo 1 contracts compiled with v1 compiler fail with new devnet-rs", +) @pytest.mark.asyncio async def test_general_v1_interaction(account, cairo1_erc20_class_hash: int): calldata = { @@ -48,6 +56,10 @@ async def test_general_v1_interaction(account, cairo1_erc20_class_hash: int): assert after_transfer_balance == calldata["initial_supply"] - transfer_amount +@pytest.mark.skipif( + "--contract_dir=v2" not in sys.argv, + reason="Some Cairo 1 contracts compiled with v1 compiler fail with new devnet-rs", +) @pytest.mark.asyncio async def test_serializing_struct(account, cairo1_token_bridge_class_hash: int): bridge = await deploy_v1_contract( @@ -136,6 +148,10 @@ async def test_serializing_enum(account, cairo1_test_enum_class_hash: int): assert received_enum.value == value +@pytest.mark.skipif( + "--contract_dir=v2" not in sys.argv, + reason="Some cairo 1 contracts compiled with v1 compiler fail with new devnet-rs", +) @pytest.mark.asyncio async def test_from_address_on_v1_contract(account, cairo1_erc20_class_hash: int): calldata = { diff --git a/starknet_py/tests/e2e/docs/code_examples/test_full_node_client.py b/starknet_py/tests/e2e/docs/code_examples/test_full_node_client.py index 9db3ac3cb..c8029ce0d 100644 --- a/starknet_py/tests/e2e/docs/code_examples/test_full_node_client.py +++ b/starknet_py/tests/e2e/docs/code_examples/test_full_node_client.py @@ -15,12 +15,16 @@ def test_init(): @pytest.mark.asyncio -async def test_get_block(client): +async def test_get_block(client, block_with_declare_hash): # docs-start: get_block block = await client.get_block(block_number="latest") block = await client.get_block(block_number=0) # or - block = await client.get_block(block_hash="0x0") + block_hash = "0x0" + # docs-end: get_block + block_hash = block_with_declare_hash + # docs-start: get_block + block = await client.get_block(block_hash=block_hash) # docs-end: get_block @@ -185,7 +189,7 @@ async def test_get_events(client, contract_address): # docs-end: get_events -# TODO (#1179): fix that +# TODO (#1219): fix that after update to RPC to v0.6.0 @pytest.mark.xfail( reason="Passing devnet client without implemented methods - test simply for a code example." ) @@ -199,7 +203,7 @@ async def test_trace_block_transactions(client): # docs-end: trace_block_transactions -# TODO (#1179): fix that +# TODO (#1219): fix that after update to RPC to v0.6.0 @pytest.mark.xfail( reason="Passing devnet client without implemented methods - test simply for a code example." ) @@ -216,11 +220,9 @@ async def test_trace_transaction(client): # docs-end: trace_transaction -# TODO (#1179): remove @pytest.mark.skip -@pytest.mark.skip(reason="Old devnet without RPC 0.5.0") @pytest.mark.asyncio async def test_simulate_transactions( - full_node_account, deployed_balance_contract, deploy_account_transaction + account, deployed_balance_contract, deploy_account_transaction ): assert isinstance(deployed_balance_contract, Contract) contract_address = deployed_balance_contract.address @@ -231,7 +233,7 @@ async def test_simulate_transactions( selector=get_selector_from_name("method_name"), calldata=[0xCA11DA7A], ) - first_transaction = await full_node_account.sign_invoke_transaction( + first_transaction = await account.sign_invoke_transaction( calls=call, max_fee=int(1e16) ) # docs-end: simulate_transactions @@ -241,17 +243,17 @@ async def test_simulate_transactions( selector=get_selector_from_name("increase_balance"), calldata=[0x10], ) - first_transaction = await full_node_account.sign_invoke_transaction( + first_transaction = await account.sign_invoke_transaction( calls=call, auto_estimate=True ) # docs-start: simulate_transactions # one transaction - simulated_txs = await full_node_account.client.simulate_transactions( + simulated_txs = await account.client.simulate_transactions( transactions=[first_transaction], block_number="latest" ) # or multiple - simulated_txs = await full_node_account.client.simulate_transactions( + simulated_txs = await account.client.simulate_transactions( transactions=[first_transaction, second_transaction], block_number="latest" ) # docs-end: simulate_transactions diff --git a/starknet_py/tests/e2e/docs/guide/test_simple_deploy_cairo1.py b/starknet_py/tests/e2e/docs/guide/test_simple_deploy_cairo1.py index 581dd6f18..26ba5d973 100644 --- a/starknet_py/tests/e2e/docs/guide/test_simple_deploy_cairo1.py +++ b/starknet_py/tests/e2e/docs/guide/test_simple_deploy_cairo1.py @@ -1,10 +1,17 @@ import json +import sys import pytest +from starknet_py.tests.e2e.fixtures.constants import CONTRACTS_COMPILED_V2_DIR from starknet_py.tests.e2e.fixtures.misc import read_contract +# TODO (#1219): investigate why this test fails for v1 contract +@pytest.mark.skipif( + "--contract_dir=v2" not in sys.argv, + reason="Some cairo 1 contracts compiled with v1 compiler fail with new devnet-rs - test simply for a code example.", +) @pytest.mark.asyncio async def test_simple_deploy_cairo1(account, cairo1_erc20_class_hash): # pylint: disable=import-outside-toplevel @@ -15,7 +22,9 @@ async def test_simple_deploy_cairo1(account, cairo1_erc20_class_hash): # docs: end - compiled_contract = read_contract("erc20_compiled.json") + compiled_contract = read_contract( + "erc20_compiled.json", directory=CONTRACTS_COMPILED_V2_DIR + ) class_hash = cairo1_erc20_class_hash # docs: start diff --git a/starknet_py/tests/e2e/fixtures/accounts.py b/starknet_py/tests/e2e/fixtures/accounts.py index 1728ab3f6..feca50063 100644 --- a/starknet_py/tests/e2e/fixtures/accounts.py +++ b/starknet_py/tests/e2e/fixtures/accounts.py @@ -38,7 +38,7 @@ async def devnet_account_details( """ Deploys an Account and adds fee tokens to its balance (only on devnet). """ - # TODO (#1179): consider using predeployed account with initial balance + private_key = _get_random_private_key_unsafe() key_pair = KeyPair.from_private_key(private_key) salt = 1 diff --git a/starknet_py/tests/e2e/fixtures/constants.py b/starknet_py/tests/e2e/fixtures/constants.py index b2d544172..79d2d23f1 100644 --- a/starknet_py/tests/e2e/fixtures/constants.py +++ b/starknet_py/tests/e2e/fixtures/constants.py @@ -52,9 +52,9 @@ def _get_env_lambda(env_name): # ----------------------------------------------------------------------------- DEVNET_PRE_DEPLOYED_ACCOUNT_ADDRESS = ( - "0x7d2f37b75a5e779f7da01c22acee1b66c39e8ba470ee5448f05e1462afcedb4" + "0x260a8311b4f1092db620b923e8d7d20e76dedcc615fb4b6fdf28315b81de201" ) -DEVNET_PRE_DEPLOYED_ACCOUNT_PRIVATE_KEY = "0xcd613e30d8f16adf91b7584a2265b1f5" +DEVNET_PRE_DEPLOYED_ACCOUNT_PRIVATE_KEY = "0xc10662b7b247c7cecf7e8a30726cff12" MAX_FEE = int(1e16) diff --git a/starknet_py/tests/e2e/fixtures/devnet.py b/starknet_py/tests/e2e/fixtures/devnet.py index 63bf9b597..7a4068a65 100644 --- a/starknet_py/tests/e2e/fixtures/devnet.py +++ b/starknet_py/tests/e2e/fixtures/devnet.py @@ -1,10 +1,7 @@ -import os import socket import subprocess import time -import warnings from contextlib import closing -from pathlib import Path from typing import Generator, List import pytest @@ -17,69 +14,25 @@ def get_available_port() -> int: return sock.getsockname()[1] -def get_compiler_manifest() -> List[str]: - """ - Load manifest-path file and return it as --cairo-compiler-manifest flag to starknet-devnet. - To configure manifest locally, install Cairo 1 compiler https://github.com/starkware-libs/cairo - and create manifest-path containing a path to top-level Cargo.toml file in cairo 1 compiler directory - file from manifest-path.template. - """ - try: - manifest_file_path = Path(os.path.dirname(__file__)) / "../manifest-path" - manifest = manifest_file_path.read_text("utf-8").splitlines()[0] - - return ["--cairo-compiler-manifest", manifest] - except (IndexError, FileNotFoundError): - warnings.warn( - "File 'manifest-path' was not found in directory 'starknet_py/tests/e2e'. More info " - "here: https://starknetpy.readthedocs.io/en/latest/development.html#setup" - ) - return [] - - def start_devnet(): devnet_port = get_available_port() - - if os.name == "nt": - start_devnet_command = start_devnet_command_windows(devnet_port) - else: - start_devnet_command = start_devnet_command_unix(devnet_port) + start_devnet_command = get_start_devnet_command(devnet_port) # pylint: disable=consider-using-with proc = subprocess.Popen(start_devnet_command) - time.sleep(10) + time.sleep(5) return devnet_port, proc -def start_devnet_command_unix(devnet_port: int) -> List[str]: +def get_start_devnet_command(devnet_port: int) -> List[str]: return [ - "poetry", - "run", "starknet-devnet", - "--host", - "localhost", "--port", str(devnet_port), "--accounts", # deploys specified number of accounts str(1), "--seed", # generates same accounts each time str(1), - *get_compiler_manifest(), - ] - - -def start_devnet_command_windows(devnet_port: int) -> List[str]: - return [ - "wsl", - "python3", - "-m", - "starknet_devnet.server", - "--port", - f"{devnet_port}", - "--accounts", - str(1), - "--seed", - str(1), ] diff --git a/starknet_py/tests/e2e/fixtures/misc.py b/starknet_py/tests/e2e/fixtures/misc.py index 8d63c83ae..aa53017ed 100644 --- a/starknet_py/tests/e2e/fixtures/misc.py +++ b/starknet_py/tests/e2e/fixtures/misc.py @@ -7,6 +7,7 @@ import pytest +from starknet_py.net.full_node_client import FullNodeClient from starknet_py.net.models.typed_data import TypedData from starknet_py.tests.e2e.fixtures.constants import ( CONTRACTS_COMPILED_V0_DIR, @@ -85,7 +86,12 @@ def typed_data(request) -> TypedData: @pytest.fixture(name="get_tx_receipt_path", scope="package") def get_tx_receipt_full_node_client(): - return "starknet_py.net.full_node_client.FullNodeClient.get_transaction_receipt" + return f"{FullNodeClient.__module__}.FullNodeClient.get_transaction_receipt" + + +@pytest.fixture(name="get_tx_status_path", scope="package") +def get_tx_status_full_node_client(): + return f"{FullNodeClient.__module__}.FullNodeClient.get_transaction_status" def read_contract(file_name: str, *, directory: Optional[Path] = None) -> str: diff --git a/starknet_py/tests/e2e/tests_on_networks/account_test.py b/starknet_py/tests/e2e/tests_on_networks/account_test.py new file mode 100644 index 000000000..7760a597a --- /dev/null +++ b/starknet_py/tests/e2e/tests_on_networks/account_test.py @@ -0,0 +1,62 @@ +import pytest + +from starknet_py.contract import Contract +from starknet_py.net.account.account import Account +from starknet_py.net.client_errors import ClientError +from starknet_py.net.client_models import EstimatedFee +from starknet_py.net.models import StarknetChainId +from starknet_py.tests.e2e.fixtures.constants import ( + MAP_CONTRACT_ADDRESS_INTEGRATION, + MAX_FEE, +) + +# TODO (#1219): run these tests on devnet if possible + + +@pytest.mark.asyncio +async def test_sign_invoke_tx_for_fee_estimation(full_node_account_integration): + account = full_node_account_integration + + map_contract = await Contract.from_address( + address=MAP_CONTRACT_ADDRESS_INTEGRATION, provider=account + ) + + call = map_contract.functions["put"].prepare(key=40, value=50) + transaction = await account.sign_invoke_transaction(calls=call, max_fee=MAX_FEE) + + estimate_fee_transaction = await account.sign_for_fee_estimate(transaction) + + estimation = await account.client.estimate_fee(estimate_fee_transaction) + assert estimation.overall_fee > 0 + + # Verify that the transaction signed for fee estimation cannot be sent + with pytest.raises(ClientError): + await account.client.send_transaction(estimate_fee_transaction) + + # Verify that original transaction can be sent + result = await account.client.send_transaction(transaction) + await account.client.wait_for_tx(result.transaction_hash) + + +@pytest.mark.asyncio +async def test_sign_declare_tx_for_fee_estimation( + full_node_account_integration, map_compiled_contract +): + account = full_node_account_integration + + transaction = await account.sign_declare_transaction( + compiled_contract=map_compiled_contract, max_fee=MAX_FEE + ) + + estimate_fee_transaction = await account.sign_for_fee_estimate(transaction) + + estimation = await account.client.estimate_fee(estimate_fee_transaction) + assert estimation.overall_fee > 0 + + # Verify that the transaction signed for fee estimation cannot be sent + with pytest.raises(ClientError): + await account.client.declare(estimate_fee_transaction) + + # Verify that original transaction can be sent + result = await account.client.declare(transaction) + await account.client.wait_for_tx(result.transaction_hash) diff --git a/starknet_py/tests/e2e/tests_on_networks/client_test.py b/starknet_py/tests/e2e/tests_on_networks/client_test.py index de2ad23f0..6daca5c8b 100644 --- a/starknet_py/tests/e2e/tests_on_networks/client_test.py +++ b/starknet_py/tests/e2e/tests_on_networks/client_test.py @@ -36,6 +36,8 @@ async def test_get_transaction_receipt(full_node_client_integration, transaction assert isinstance(receipt, TransactionReceipt) assert receipt.execution_status is not None assert receipt.finality_status is not None + assert receipt.execution_resources is not None + assert receipt.type is not None @pytest.mark.asyncio @@ -79,7 +81,7 @@ async def test_transaction_not_received_max_fee_too_small(full_node_account_test selector=get_selector_from_name("empty"), calldata=[], ) - sign_invoke = await account.sign_invoke_transaction(calls=call, max_fee=1) + sign_invoke = await account.sign_invoke_transaction(calls=call, max_fee=int(1e10)) with pytest.raises(ClientError, match=r".*Max fee.*"): await account.client.send_transaction(sign_invoke) @@ -115,6 +117,8 @@ async def test_transaction_not_received_invalid_nonce(full_node_account_testnet) await account.client.send_transaction(sign_invoke) +# TODO (#1219): fix this error once ClientError returns data field +@pytest.mark.skip @pytest.mark.asyncio async def test_transaction_not_received_invalid_signature(full_node_account_testnet): account = full_node_account_testnet @@ -132,7 +136,7 @@ async def test_transaction_not_received_invalid_signature(full_node_account_test # ------------------------------------ FULL_NODE_CLIENT TESTS ------------------------------------ -# TODO (#1142): move tests below to full_node_test.py once devnet releases rust version supporting RPC v0.4.0 +# TODO (#1219): move tests below to full_node_test.py @pytest.mark.asyncio diff --git a/starknet_py/tests/e2e/tests_on_networks/trace_api_test.py b/starknet_py/tests/e2e/tests_on_networks/trace_api_test.py index 4006e5c29..c0641b0fc 100644 --- a/starknet_py/tests/e2e/tests_on_networks/trace_api_test.py +++ b/starknet_py/tests/e2e/tests_on_networks/trace_api_test.py @@ -16,7 +16,7 @@ from starknet_py.tests.e2e.fixtures.constants import CONTRACTS_COMPILED_V0_DIR from starknet_py.tests.e2e.fixtures.misc import read_contract -# TODO (#1179): move those tests to full_node_test.py +# TODO (#1219): move those tests to full_node_test.py @pytest.mark.asyncio diff --git a/starknet_py/transaction_errors.py b/starknet_py/transaction_errors.py index 4c4ce6f97..b9b343277 100644 --- a/starknet_py/transaction_errors.py +++ b/starknet_py/transaction_errors.py @@ -37,10 +37,7 @@ class TransactionRejectedError(TransactionFailedError): """ def __str__(self): - return ( - "Transaction was rejected with following Starknet error: " - f"{self.message}." - ) + return "Transaction was rejected on Starknet." class TransactionNotReceivedError(TransactionFailedError): From 74249a603caee79664d175c32332e5240536ad94 Mon Sep 17 00:00:00 2001 From: ddoktorski <45050160+ddoktorski@users.noreply.github.com> Date: Wed, 10 Jan 2024 18:31:39 +0100 Subject: [PATCH 4/7] Support RPC v0.6.0 (#1243) * Modify transaction models and add hash computation logic * Create broadcasted v3 transactions * Add account sign and execute v3 * Fix models * Prepare devnet for tests with STRK * Run devnet with flag to keep states * Fix helper for deploy account * Add declare and deploy account tests * Add more tests * Fix client models * Update devnet env variables in github workflow * Fix typo in `TransactionV3` class * Add `auto_estimate` parameter to v3 sign methods * Add missing `account_deployment_data` * Rename `fee_contract` to `eth_fee_contract` * Align v3 common fields to match with the snip Co-authored-by: Maksim Zdobnikau <43750648+DelevoXDG@users.noreply.github.com> * Rename `get_common_tx_fields` Co-authored-by: Maksim Zdobnikau <43750648+DelevoXDG@users.noreply.github.com> * Fix lint and failing tests * Add docstrings for compute hash functions * Change `class_hash` and `contract_class` validation in compute_declare_v2_hash * Add `auto_estimate` to `execute_v3` and fix typos * Fix punctuation and typos in `_prepare_invoke_v3` docstring Co-authored-by: Maksim Zdobnikau <43750648+DelevoXDG@users.noreply.github.com> * Fix docs build * Disallow passing currently unused v3 fields to `init` * Add V1 postfix for deprecated transaction types * Add `DeprecatedTransaction` and `DeprecatedTransactionSchema` * Add intermediary `_DeprecatedAccountTransaction` class * Rename sign functions to have transaction version in names * Fix `simulate_transactions` description * Replace `resource_bounds` with `l1_resource_bounds` in sign methods * RPC v0.6.0 documentation (#1250) --------- Co-authored-by: Maksim Zdobnikau <43750648+DelevoXDG@users.noreply.github.com> --- .github/workflows/checks.yml | 6 +- docs/api/models.rst | 15 +- docs/guide/deploying_contracts.rst | 4 +- docs/migration_guide.rst | 57 +++- starknet_py/contract.py | 10 +- starknet_py/hash/transaction.py | 166 +++++++++++- starknet_py/hash/transaction_test.py | 138 ++++++++++ starknet_py/net/account/account.py | 256 +++++++++++++++--- starknet_py/net/account/base_account.py | 121 ++++++++- starknet_py/net/client.py | 12 +- starknet_py/net/client_models.py | 154 +++++++++-- starknet_py/net/client_test.py | 25 +- starknet_py/net/client_utils.py | 44 +++ starknet_py/net/full_node_client.py | 150 ++++++---- starknet_py/net/models/__init__.py | 9 +- starknet_py/net/models/transaction.py | 150 +++++++++- starknet_py/net/models/transaction_test.py | 17 +- starknet_py/net/schemas/common.py | 40 +++ starknet_py/net/schemas/rpc.py | 161 ++++++++++- starknet_py/net/schemas/utils.py | 11 + starknet_py/net/signer/stark_curve_signer.py | 87 +----- .../net/signer/test_stark_curve_signer.py | 8 +- starknet_py/tests/e2e/account/account_test.py | 221 +++++++++++++-- starknet_py/tests/e2e/client/client_test.py | 62 ++++- .../tests/e2e/client/fixtures/transactions.py | 15 +- .../tests/e2e/client/full_node_test.py | 14 +- starknet_py/tests/e2e/declare/declare_test.py | 21 +- starknet_py/tests/e2e/deploy/deployer_test.py | 10 +- .../e2e/deploy_account/deploy_account_test.py | 30 +- .../test_deploy_prefunded_account.py | 4 +- .../code_examples/test_full_node_client.py | 4 +- .../test_account_sign_without_execute.py | 14 +- .../e2e/docs/guide/test_cairo1_contract.py | 4 +- .../test_contract_account_compatibility.py | 6 +- .../tests/e2e/docs/guide/test_custom_nonce.py | 4 +- .../docs/guide/test_declaring_contracts.py | 4 +- .../docs/guide/test_deploying_in_multicall.py | 2 +- .../e2e/docs/guide/test_deploying_with_udc.py | 2 +- .../docs/guide/test_sign_for_fee_estimate.py | 4 +- .../test_account_comparison.py | 4 +- starknet_py/tests/e2e/fixtures/accounts.py | 33 ++- starknet_py/tests/e2e/fixtures/constants.py | 11 + starknet_py/tests/e2e/fixtures/contracts.py | 64 ++++- starknet_py/tests/e2e/fixtures/devnet.py | 2 + starknet_py/tests/e2e/fixtures/proxy.py | 2 +- .../e2e/tests_on_networks/account_test.py | 4 +- .../e2e/tests_on_networks/client_test.py | 59 +++- .../e2e/tests_on_networks/trace_api_test.py | 2 +- starknet_py/tests/e2e/utils.py | 24 +- 49 files changed, 1867 insertions(+), 400 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 96cd5b5b9..95d6d2054 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -1,9 +1,9 @@ name: Checks env: - STARKNET_VERSION: "0.12.3" - RPC_SPEC_VERSION: "0.5.1" - DEVNET_SHA: "78527de" + STARKNET_VERSION: "0.13.0" + RPC_SPEC_VERSION: "0.6.0" + DEVNET_SHA: "1bd447d" on: push: diff --git a/docs/api/models.rst b/docs/api/models.rst index 3450c8dc8..ed0f596ea 100644 --- a/docs/api/models.rst +++ b/docs/api/models.rst @@ -13,16 +13,25 @@ Module containing base models and functions to operate on them. :exclude-members: __init__, __new__ :members: -.. autoclass:: DeployAccount +.. autoclass:: DeployAccountV1 :exclude-members: __init__, __new__ -.. autoclass:: Declare +.. autoclass:: DeployAccountV3 + :exclude-members: __init__, __new__ + +.. autoclass:: DeclareV1 :exclude-members: __init__, __new__ .. autoclass:: DeclareV2 :exclude-members: __init__, __new__ -.. autoclass:: Invoke +.. autoclass:: DeclareV3 + :exclude-members: __init__, __new__ + +.. autoclass:: InvokeV1 + :exclude-members: __init__, __new__ + +.. autoclass:: InvokeV3 :exclude-members: __init__, __new__ .. autoenum:: StarknetChainId diff --git a/docs/guide/deploying_contracts.rst b/docs/guide/deploying_contracts.rst index 21336e48c..f5cd5b423 100644 --- a/docs/guide/deploying_contracts.rst +++ b/docs/guide/deploying_contracts.rst @@ -77,8 +77,8 @@ Declaring Cairo1 contracts | Starknet 0.11 introduced the ability to declare contracts written in Cairo1! -To declare a new contract, Declare v2 transaction has to be sent. -You can see the structure of the new Declare transaction `here `_. +To declare a new contract, Declare v2 or Declare v3 transaction has to be sent. +You can see the structure of these transactions `here `_. The main differences in the structure of the transaction from its previous version are: - ``contract_class`` field is a ``SierraContractClass`` diff --git a/docs/migration_guide.rst b/docs/migration_guide.rst index 093c64927..26f2d7c4c 100644 --- a/docs/migration_guide.rst +++ b/docs/migration_guide.rst @@ -5,6 +5,43 @@ Migration guide 0.19.0 Migration guide ********************** +Version 0.19.0 of **starknet.py** comes with support for RPC 0.6.0! + +.. currentmodule:: starknet_py.net.client_models + +New classes added to mirror the recent changes in the RPC v0.6.0 specification include: +:class:`ResourceBoundsMapping`, :class:`ResourceBounds`, :class:`PriceUnit`, :class:`FeePayment`, :class:`DAMode`. + +Changes in the :class:`~starknet_py.net.account.account.Account`: + +.. currentmodule:: starknet_py.net.account.account + +- :meth:`~Account.execute_v3` has been added. +- :meth:`~Account.sign_declare_v3_transaction`, :meth:`~Account.sign_deploy_account_v3_transaction` and :meth:`~Account.sign_invoke_v3_transaction` have been added. +- :meth:`~Account.sign_declare_transaction`, :meth:`~Account.sign_deploy_account_transaction` and :meth:`~Account.sign_invoke_transaction` have been renamed to :meth:`~Account.sign_declare_v1_transaction`, :meth:`~Account.sign_deploy_account_v1_transaction` and :meth:`~Account.sign_invoke_v1_transaction` respectively. + +All new functions with ``v3`` in their name operate similarly to their ``v1`` and ``v2`` counterparts. +Unlike their ``v1`` counterparts, ``v3`` transaction fees are paid in Fri (10^-18 STRK). Therefore, ``max_fee`` parameter, which is typically set in Wei, is not applicable for ``v3`` functions. Instead, ``l1_resource_bounds`` parameter is utilized to limit the Fri amount used. + +Changes in the :class:`~starknet_py.net.full_node_client.FullNodeClient`: + +.. currentmodule:: starknet_py.net.full_node_client + +- :meth:`~FullNodeClient.estimate_fee` has a new parameter ``skip_validate``. +- :meth:`~FullNodeClient.declare` accepts ``transaction`` argument of the type :class:`~starknet_py.net.models.transaction.DeclareV3` +- :meth:`~FullNodeClient.send_transaction` accepts ``transaction`` argument of the type :class:`~starknet_py.net.models.transaction.InvokeV3` +- :meth:`~FullNodeClient.deploy_account` accepts ``transaction`` argument of the type :class:`~starknet_py.net.models.transaction.DeployAccountV3` + +.. warning:: + :class:`~starknet_py.contract.Contract` class does not support V3 transactions in the pre-release. + + +0.19.0 Targeted versions +------------------------ + +- Starknet - `0.13.0 `_ +- RPC - `0.6.0 `_ + 0.19.0 Breaking changes ----------------------- @@ -12,15 +49,27 @@ Migration guide 1. :class:`GatewayClient` all related classes and fields have been removed. 2. Client ``net`` property has been removed. -3. :class:`TransactionReceipt` field ``execution_resources`` has been changed from ``dict`` to :class:`ExecutionResources`. -4. :class:`TransactionReceipt` fields ``status`` and ``rejection_reason`` have been removed. -5. :class:`TransactionStatus`, :class:`TransactionExecutionStatus` and :class:`TransactionFinalityStatus` have been changed to have the same structure as in RPC specification. +3. :class:`Declare`, :class:`DeployAccount` and :class:`Invoke` have been renamed to :class:`~starknet_py.net.models.transaction.DeclareV1`, :class:`~starknet_py.net.models.transaction.DeployAccountV1` and :class:`~starknet_py.net.models.transaction.InvokeV1` respectively. +4. :class:`TransactionReceipt` field ``execution_resources`` has been changed from ``dict`` to :class:`ExecutionResources`. +5. :class:`TransactionReceipt` fields ``status`` and ``rejection_reason`` have been removed. +6. :class:`TransactionStatus`, :class:`TransactionExecutionStatus` and :class:`TransactionFinalityStatus` have been changed to have the same structure as in RPC specification. +7. :class:`EstimatedFee` has a new required field ``unit``. +8. :class:`EstimatedFee` field ``gas_usage`` has been renamed to ``gas_consumed``. +9. :class:`FunctionInvocation` has a new required field ``execution_resources``. +10. :class:`ResourcePrice` field ``price_in_strk`` has been renamed to ``price_in_fri`` and has now become required. +11. :class:`ResourceLimits` class has been renamed to :class:`ResourceBounds`. 0.19.0 Minor changes -------------------- 1. :class:`L1HandlerTransaction` field ``nonce`` is now required. -2. :class:`TransactionReceipt` fields ``finality_status``, ``execution_status`` and ``execution_resources`` are now required. +2. :class:`TransactionReceipt` fields ``actual_fee``, ``finality_status``, ``execution_status``, ``execution_resources`` and ``type`` are now required. + +0.19.0 Development-related changes +---------------------------------- + +Test execution has been transitioned to the new `starknet-devnet-rs `_. +To adapt to this change, it should be installed locally and added to the ``PATH``. Further information regarding this change can be found in the `Development `_ section. ********************** 0.18.3 Migration guide diff --git a/starknet_py/contract.py b/starknet_py/contract.py index 33bea8aec..eb2c730ff 100644 --- a/starknet_py/contract.py +++ b/starknet_py/contract.py @@ -34,7 +34,7 @@ from starknet_py.net.account.base_account import BaseAccount from starknet_py.net.client import Client from starknet_py.net.client_models import Call, EstimatedFee, Hash, Tag -from starknet_py.net.models import AddressRepresentation, Invoke, parse_address +from starknet_py.net.models import AddressRepresentation, InvokeV1, parse_address from starknet_py.net.udc_deployer.deployer import Deployer from starknet_py.proxy.contract_abi_resolver import ( ContractAbiResolver, @@ -153,7 +153,7 @@ class InvokeResult(SentTransaction): contract: ContractData = None # pyright: ignore """Additional information about the Contract that made the transaction.""" - invoke_transaction: Invoke = None # pyright: ignore + invoke_transaction: InvokeV1 = None # pyright: ignore """A InvokeTransaction instance used.""" def __post_init__(self): @@ -357,7 +357,7 @@ async def invoke( if max_fee is not None: self.max_fee = max_fee - transaction = await self._account.sign_invoke_transaction( + transaction = await self._account.sign_invoke_v1_transaction( calls=self, nonce=nonce, max_fee=self.max_fee, @@ -391,7 +391,7 @@ async def estimate_fee( :param nonce: Nonce of the transaction. :return: Estimated amount of Wei executing specified transaction will cost. """ - tx = await self._account.sign_invoke_transaction( + tx = await self._account.sign_invoke_v1_transaction( calls=self, nonce=nonce, max_fee=0 ) @@ -668,7 +668,7 @@ async def declare( ) else: cairo_version = 0 - declare_tx = await account.sign_declare_transaction( + declare_tx = await account.sign_declare_v1_transaction( compiled_contract=compiled_contract, nonce=nonce, max_fee=max_fee, diff --git a/starknet_py/hash/transaction.py b/starknet_py/hash/transaction.py index d83306819..92ad2602e 100644 --- a/starknet_py/hash/transaction.py +++ b/starknet_py/hash/transaction.py @@ -1,12 +1,24 @@ +from dataclasses import dataclass from enum import IntEnum -from typing import Optional, Sequence +from typing import List, Optional, Sequence +from poseidon_py.poseidon_hash import poseidon_hash_many + +from starknet_py.cairo.felt import encode_shortstring from starknet_py.common import int_from_bytes from starknet_py.constants import DEFAULT_ENTRY_POINT_SELECTOR from starknet_py.hash.class_hash import compute_class_hash from starknet_py.hash.sierra_class_hash import compute_sierra_class_hash from starknet_py.hash.utils import compute_hash_on_elements -from starknet_py.net.client_models import ContractClass, SierraContractClass +from starknet_py.net.client_models import ( + ContractClass, + DAMode, + ResourceBoundsMapping, + SierraContractClass, +) + +L1_GAS_ENCODED = encode_shortstring("L1_GAS") +l2_GAS_ENCODED = encode_shortstring("L2_GAS") class TransactionHashPrefix(IntEnum): @@ -21,6 +33,54 @@ class TransactionHashPrefix(IntEnum): L1_HANDLER = int_from_bytes(b"l1_handler") +@dataclass +class CommonTransactionV3Fields: + # pylint: disable=too-many-instance-attributes + + tx_prefix: TransactionHashPrefix + version: int + address: int + tip: int + resource_bounds: ResourceBoundsMapping + paymaster_data: List[int] + chain_id: int + nonce: int + nonce_data_availability_mode: DAMode + fee_data_availability_mode: DAMode + + def compute_common_tx_fields(self): + return [ + self.tx_prefix, + self.version, + self.address, + poseidon_hash_many([self.tip, *self.compute_resource_bounds_for_fee()]), + poseidon_hash_many(self.paymaster_data), + self.chain_id, + self.nonce, + self.get_data_availability_modes(), + ] + + def compute_resource_bounds_for_fee(self) -> List[int]: + l1_gas_bounds = ( + (L1_GAS_ENCODED << (128 + 64)) + + (self.resource_bounds.l1_gas.max_amount << 128) + + self.resource_bounds.l1_gas.max_price_per_unit + ) + + l2_gas_bounds = ( + (l2_GAS_ENCODED << (128 + 64)) + + (self.resource_bounds.l2_gas.max_amount << 128) + + self.resource_bounds.l2_gas.max_price_per_unit + ) + + return [l1_gas_bounds, l2_gas_bounds] + + def get_data_availability_modes(self) -> int: + return ( + self.nonce_data_availability_mode.value << 32 + ) + self.fee_data_availability_mode.value + + # pylint: disable=too-many-arguments def compute_transaction_hash( tx_hash_prefix: TransactionHashPrefix, @@ -88,7 +148,7 @@ def compute_invoke_transaction_hash( nonce: int, ) -> int: """ - Computes hash of the Invoke transaction. + Computes hash of an Invoke transaction. :param version: The transaction's version. :param sender_address: Sender address. @@ -110,6 +170,30 @@ def compute_invoke_transaction_hash( ) +def compute_invoke_v3_transaction_hash( + *, + account_deployment_data: List[int], + calldata: List[int], + common_fields: CommonTransactionV3Fields, +) -> int: + """ + Computes hash of an Invoke transaction version 3. + + :param account_deployment_data: This will contain the class_hash, salt, and the calldata needed for the constructor. + Currently, this value is always empty. + :param calldata: Calldata of the function. + :param common_fields: Common fields for V3 transactions. + :return: Hash of the transaction. + """ + return poseidon_hash_many( + [ + *common_fields.compute_common_tx_fields(), + poseidon_hash_many(account_deployment_data), + poseidon_hash_many(calldata), + ] + ) + + def compute_deploy_account_transaction_hash( version: int, contract_address: int, @@ -121,7 +205,7 @@ def compute_deploy_account_transaction_hash( chain_id: int, ) -> int: """ - Computes hash of the DeployAccount transaction. + Computes hash of a DeployAccount transaction. :param version: The transaction's version. :param contract_address: Contract address. @@ -145,6 +229,32 @@ def compute_deploy_account_transaction_hash( ) +def compute_deploy_account_v3_transaction_hash( + *, + class_hash: int, + constructor_calldata: List[int], + contract_address_salt: int, + common_fields: CommonTransactionV3Fields, +) -> int: + """ + Computes hash of a DeployAccount transaction version 3. + + :param class_hash: The class hash of the contract. + :param constructor_calldata: Constructor calldata of the contract. + :param contract_address_salt: A random salt that determines the account address. + :param common_fields: Common fields for V3 transactions. + :return: Hash of the transaction. + """ + return poseidon_hash_many( + [ + *common_fields.compute_common_tx_fields(), + poseidon_hash_many(constructor_calldata), + class_hash, + contract_address_salt, + ] + ) + + def compute_declare_transaction_hash( contract_class: ContractClass, chain_id: int, @@ -154,7 +264,7 @@ def compute_declare_transaction_hash( nonce: int, ) -> int: """ - Computes hash of the Declare transaction. + Computes hash of a Declare transaction. :param contract_class: ContractClass of the contract. :param chain_id: The network's chain ID. @@ -190,11 +300,11 @@ def compute_declare_v2_transaction_hash( nonce: int, ) -> int: """ - Computes class hash of declare transaction version 2. + Computes class hash of a Declare transaction version 2. :param contract_class: SierraContractClass of the contract. :param class_hash: Class hash of the contract. - :param compiled_class_hash: compiled class hash of the program. + :param compiled_class_hash: Compiled class hash of the program. :param chain_id: The network's chain ID. :param sender_address: Address which sends the transaction. :param max_fee: The transaction's maximum fee. @@ -202,13 +312,9 @@ def compute_declare_v2_transaction_hash( :param nonce: Nonce of the transaction. :return: Hash of the transaction. """ - if contract_class is None and class_hash is None: - raise ValueError("Either contract_class or class_hash is required.") - if contract_class is not None and class_hash is not None: - raise ValueError("Both contract_class and class_hash passed.") - if class_hash is None: - assert contract_class is not None + if contract_class is None: + raise ValueError("Either contract_class or class_hash is required.") class_hash = compute_sierra_class_hash(contract_class) return compute_transaction_hash( @@ -221,3 +327,37 @@ def compute_declare_v2_transaction_hash( chain_id=chain_id, additional_data=[nonce, compiled_class_hash], ) + + +def compute_declare_v3_transaction_hash( + *, + contract_class: Optional[SierraContractClass] = None, + class_hash: Optional[int] = None, + account_deployment_data: List[int], + compiled_class_hash: int, + common_fields: CommonTransactionV3Fields, +) -> int: + """ + Computes class hash of a Declare transaction version 3. + + :param contract_class: SierraContractClass of the contract. + :param class_hash: Class hash of the contract. + :param account_deployment_data: This will contain the class_hash and the calldata needed for the constructor. + Currently, this value is always empty. + :param compiled_class_hash: Compiled class hash of the program. + :param common_fields: Common fields for V3 transactions. + :return: Hash of the transaction. + """ + if class_hash is None: + if contract_class is None: + raise ValueError("Either contract_class or class_hash is required.") + class_hash = compute_sierra_class_hash(contract_class) + + return poseidon_hash_many( + [ + *common_fields.compute_common_tx_fields(), + poseidon_hash_many(account_deployment_data), + class_hash, + compiled_class_hash, + ] + ) diff --git a/starknet_py/hash/transaction_test.py b/starknet_py/hash/transaction_test.py index bb43f8a93..2d8a64edb 100644 --- a/starknet_py/hash/transaction_test.py +++ b/starknet_py/hash/transaction_test.py @@ -2,18 +2,31 @@ from starknet_py.common import create_compiled_contract from starknet_py.hash.transaction import ( + CommonTransactionV3Fields, TransactionHashPrefix, compute_declare_transaction_hash, compute_declare_v2_transaction_hash, + compute_declare_v3_transaction_hash, compute_deploy_account_transaction_hash, + compute_deploy_account_v3_transaction_hash, compute_invoke_transaction_hash, + compute_invoke_v3_transaction_hash, compute_transaction_hash, ) +from starknet_py.net.client_models import DAMode, ResourceBounds, ResourceBoundsMapping from starknet_py.net.models import StarknetChainId from starknet_py.tests.e2e.fixtures.constants import CONTRACTS_COMPILED_V0_DIR from starknet_py.tests.e2e.fixtures.misc import read_contract +@pytest.fixture(name="default_resource_bounds") +def get_resource_bounds(): + return ResourceBoundsMapping( + l1_gas=ResourceBounds(max_amount=0x186A0, max_price_per_unit=0x5AF3107A4000), + l2_gas=ResourceBounds(max_amount=0, max_price_per_unit=0), + ) + + @pytest.mark.parametrize( "data, expected_hash", ( @@ -120,3 +133,128 @@ def test_compute_declare_v2_transaction_hash(data, expected_hash): ) def test_compute_invoke_transaction_hash(data, expected_hash): assert compute_invoke_transaction_hash(**data) == expected_hash + + +@pytest.mark.parametrize( + "common_data, declare_data, expected_hash", + ( + ( + { + "address": 0x52125C1E043126C637D1436D9551EF6C4F6E3E36945676BBD716A56E3A41B7A, + "chain_id": StarknetChainId.TESTNET, + "nonce": 0x675, + "tip": 0x0, + "paymaster_data": [], + "nonce_data_availability_mode": DAMode.L1, + "fee_data_availability_mode": DAMode.L1, + "tx_prefix": TransactionHashPrefix.DECLARE, + "version": 0x3, + }, + { + "class_hash": 0x2338634F11772EA342365ABD5BE9D9DC8A6F44F159AD782FDEBD3DB5D969738, + "compiled_class_hash": 0x17B5169C770D0E49100AB0FC672A49CA90CC572F21F79A640B5227B19D3A447, + "account_deployment_data": [], + }, + 0x7B31376D1C4F467242616530901E1B441149F1106EF765F202A50A6F917762B, + ), + ), +) +def test_compute_declare_v3_transaction_hash( + common_data, declare_data, expected_hash, default_resource_bounds +): + assert ( + compute_declare_v3_transaction_hash( + **declare_data, + common_fields=CommonTransactionV3Fields( + **common_data, resource_bounds=default_resource_bounds + ), + ) + == expected_hash + ) + + +@pytest.mark.parametrize( + "common_data, invoke_data, expected_hash", + ( + ( + { + "address": 0x35ACD6DD6C5045D18CA6D0192AF46B335A5402C02D41F46E4E77EA2C951D9A3, + "chain_id": StarknetChainId.TESTNET, + "nonce": 0x5, + "tip": 0x0, + "paymaster_data": [], + "nonce_data_availability_mode": DAMode.L1, + "fee_data_availability_mode": DAMode.L1, + "tx_prefix": TransactionHashPrefix.INVOKE, + "version": 0x3, + }, + { + "calldata": [ + 0x2, + 0x6359ED638DF79B82F2F9DBF92ABBCB41B57F9DD91EAD86B1C85D2DEE192C, + 0xB17D8A2731BA7CA1816631E6BE14F0FC1B8390422D649FA27F0FBB0C91EEA8, + 0x0, + 0x3FE8E4571772BBE0065E271686BD655EFD1365A5D6858981E582F82F2C10313, + 0x2FD9126EE011F3A837CEA02E32AE4EE73342D827E216998E5616BAB88D8B7EA, + 0x1, + 0x2FD9126EE011F3A837CEA02E32AE4EE73342D827E216998E5616BAB88D8B7EA, + ], + "account_deployment_data": [], + }, + 0x15F2CF38832542602E2D1C8BF0634893E6B43ACB6879E8A8F892F5A9B03C907, + ), + ), +) +def test_compute_invoke_v3_transaction_hash( + common_data, invoke_data, expected_hash, default_resource_bounds +): + assert ( + compute_invoke_v3_transaction_hash( + **invoke_data, + common_fields=CommonTransactionV3Fields( + **common_data, resource_bounds=default_resource_bounds + ), + ) + == expected_hash + ) + + +@pytest.mark.parametrize( + "common_data, deploy_account_data, expected_hash", + ( + ( + { + "address": 0x2FAB82E4AEF1D8664874E1F194951856D48463C3E6BF9A8C68E234A629A6F50, + "chain_id": StarknetChainId.TESTNET, + "nonce": 0x0, + "tip": 0x0, + "paymaster_data": [], + "nonce_data_availability_mode": DAMode.L1, + "fee_data_availability_mode": DAMode.L1, + "tx_prefix": TransactionHashPrefix.DEPLOY_ACCOUNT, + "version": 0x3, + }, + { + "constructor_calldata": [ + 0x5CD65F3D7DAEA6C63939D659B8473EA0C5CD81576035A4D34E52FB06840196C + ], + "contract_address_salt": 0x0, + "class_hash": 0x2338634F11772EA342365ABD5BE9D9DC8A6F44F159AD782FDEBD3DB5D969738, + }, + 0x29FD7881F14380842414CDFDD8D6C0B1F2174F8916EDCFEB1EDE1EB26AC3EF0, + ), + ), +) +def test_compute_deploy_account_v3_transaction_hash( + common_data, deploy_account_data, expected_hash, default_resource_bounds +): + assert ( + compute_deploy_account_v3_transaction_hash( + **deploy_account_data, + common_fields=CommonTransactionV3Fields( + **common_data, + resource_bounds=default_resource_bounds, + ), + ) + == expected_hash + ) diff --git a/starknet_py/net/account/account.py b/starknet_py/net/account/account.py index e1319cb43..08a9f16e5 100644 --- a/starknet_py/net/account/account.py +++ b/starknet_py/net/account/account.py @@ -17,6 +17,8 @@ Calls, EstimatedFee, Hash, + ResourceBounds, + ResourceBoundsMapping, SentTransactionResponse, SierraContractClass, Tag, @@ -25,10 +27,13 @@ from starknet_py.net.models import AddressRepresentation, StarknetChainId, parse_address from starknet_py.net.models.transaction import ( AccountTransaction, - Declare, + DeclareV1, DeclareV2, - DeployAccount, - Invoke, + DeclareV3, + DeployAccountV1, + DeployAccountV3, + InvokeV1, + InvokeV3, TypeAccountTransaction, ) from starknet_py.net.models.typed_data import TypedData @@ -56,6 +61,11 @@ class Account(BaseAccount): ESTIMATED_FEE_MULTIPLIER: float = 1.5 """Amount by which each estimated fee is multiplied when using `auto_estimate`.""" + ESTIMATED_AMOUNT_MULTIPLIER: float = 1.1 + ESTIMATED_UNIT_PRICE_MULTIPLIER: float = 1.5 + """Values by which each estimated `max_amount` and `max_price_per_unit` are multiplied when using + `auto_estimate`. Used only for V3 transactions""" + def __init__( self, *, @@ -145,6 +155,37 @@ async def _get_max_fee( return max_fee + async def _get_resource_bounds( + self, + transaction: AccountTransaction, + l1_resource_bounds: Optional[ResourceBounds] = None, + auto_estimate: bool = False, + ) -> ResourceBoundsMapping: + if auto_estimate and l1_resource_bounds is not None: + raise ValueError( + "Arguments auto_estimate and l1_resource_bounds are mutually exclusive." + ) + + if auto_estimate: + estimated_fee = await self._estimate_fee(transaction) + l1_resource_bounds = ResourceBounds( + max_amount=int( + estimated_fee.gas_consumed * Account.ESTIMATED_AMOUNT_MULTIPLIER + ), + max_price_per_unit=int( + estimated_fee.gas_price * Account.ESTIMATED_UNIT_PRICE_MULTIPLIER + ), + ) + + if l1_resource_bounds is None: + raise ValueError( + "One of arguments: l1_resource_bounds or auto_estimate must be specified when invoking a transaction." + ) + + return ResourceBoundsMapping( + l1_gas=l1_resource_bounds, l2_gas=ResourceBounds.init_with_zeros() + ) + async def _prepare_invoke( self, calls: Calls, @@ -152,7 +193,7 @@ async def _prepare_invoke( nonce: Optional[int] = None, max_fee: Optional[int] = None, auto_estimate: bool = False, - ) -> Invoke: + ) -> InvokeV1: """ Takes calls and creates Invoke from them. @@ -164,18 +205,9 @@ async def _prepare_invoke( if nonce is None: nonce = await self.get_nonce() - if await self.cairo_version == 1: - parsed_calls = _parse_calls_v2(ensure_iterable(calls)) - wrapped_calldata = _execute_payload_serializer_v2.serialize( - {"calls": parsed_calls} - ) - else: - call_descriptions, calldata = _merge_calls(ensure_iterable(calls)) - wrapped_calldata = _execute_payload_serializer.serialize( - {"call_array": call_descriptions, "calldata": calldata} - ) + wrapped_calldata = _parse_calls(await self.cairo_version, calls) - transaction = Invoke( + transaction = InvokeV1( calldata=wrapped_calldata, signature=[], max_fee=0, @@ -188,6 +220,41 @@ async def _prepare_invoke( return _add_max_fee_to_transaction(transaction, max_fee) + async def _prepare_invoke_v3( + self, + calls: Calls, + *, + l1_resource_bounds: Optional[ResourceBounds] = None, + nonce: Optional[int] = None, + auto_estimate: bool = False, + ) -> InvokeV3: + """ + Takes calls and creates InvokeV3 from them. + + :param calls: Single call or a list of calls. + :param l1_resource_bounds: Max amount and max price per unit of L1 gas used in this transaction. + :param auto_estimate: Use automatic fee estimation; not recommended as it may lead to high costs. + :return: InvokeV3 created from the calls (without the signature). + """ + if nonce is None: + nonce = await self.get_nonce() + + wrapped_calldata = _parse_calls(await self.cairo_version, calls) + + transaction = InvokeV3( + calldata=wrapped_calldata, + resource_bounds=ResourceBoundsMapping.init_with_zeros(), + signature=[], + nonce=nonce, + sender_address=self.address, + version=3, + ) + + resource_bounds = await self._get_resource_bounds( + transaction, l1_resource_bounds, auto_estimate + ) + return _add_resource_bounds_to_transaction(transaction, resource_bounds) + async def _estimate_fee( self, tx: AccountTransaction, @@ -260,14 +327,14 @@ async def sign_for_fee_estimate( signature = self.signer.sign_transaction(transaction) return _add_signature_to_transaction(tx=transaction, signature=signature) - async def sign_invoke_transaction( + async def sign_invoke_v1_transaction( self, calls: Calls, *, nonce: Optional[int] = None, max_fee: Optional[int] = None, auto_estimate: bool = False, - ) -> Invoke: + ) -> InvokeV1: execute_tx = await self._prepare_invoke( calls, nonce=nonce, @@ -277,20 +344,37 @@ async def sign_invoke_transaction( signature = self.signer.sign_transaction(execute_tx) return _add_signature_to_transaction(execute_tx, signature) - async def sign_declare_transaction( + async def sign_invoke_v3_transaction( + self, + calls: Calls, + *, + nonce: Optional[int] = None, + l1_resource_bounds: Optional[ResourceBounds] = None, + auto_estimate: bool = False, + ) -> InvokeV3: + invoke_tx = await self._prepare_invoke_v3( + calls, + l1_resource_bounds=l1_resource_bounds, + nonce=nonce, + auto_estimate=auto_estimate, + ) + signature = self.signer.sign_transaction(invoke_tx) + return _add_signature_to_transaction(invoke_tx, signature) + + async def sign_declare_v1_transaction( self, compiled_contract: str, *, nonce: Optional[int] = None, max_fee: Optional[int] = None, auto_estimate: bool = False, - ) -> Declare: + ) -> DeclareV1: if _is_sierra_contract(json.loads(compiled_contract)): raise ValueError( "Signing sierra contracts requires using `sign_declare_v2_transaction` method." ) - declare_tx = await self._make_declare_transaction( + declare_tx = await self._make_declare_v1_transaction( compiled_contract, nonce=nonce ) @@ -320,15 +404,37 @@ async def sign_declare_v2_transaction( signature = self.signer.sign_transaction(declare_tx) return _add_signature_to_transaction(declare_tx, signature) - async def _make_declare_transaction( + async def sign_declare_v3_transaction( + self, + compiled_contract: str, + compiled_class_hash: int, + *, + nonce: Optional[int] = None, + l1_resource_bounds: Optional[ResourceBounds] = None, + auto_estimate: bool = False, + ) -> DeclareV3: + declare_tx = await self._make_declare_v3_transaction( + compiled_contract, + compiled_class_hash, + nonce=nonce, + ) + resource_bounds = await self._get_resource_bounds( + declare_tx, l1_resource_bounds, auto_estimate + ) + declare_tx = _add_resource_bounds_to_transaction(declare_tx, resource_bounds) + + signature = self.signer.sign_transaction(declare_tx) + return _add_signature_to_transaction(declare_tx, signature) + + async def _make_declare_v1_transaction( self, compiled_contract: str, *, nonce: Optional[int] = None - ) -> Declare: + ) -> DeclareV1: contract_class = create_compiled_contract(compiled_contract=compiled_contract) if nonce is None: nonce = await self.get_nonce() - declare_tx = Declare( + declare_tx = DeclareV1( contract_class=contract_class, sender_address=self.address, max_fee=0, @@ -363,7 +469,32 @@ async def _make_declare_v2_transaction( ) return declare_tx - async def sign_deploy_account_transaction( + async def _make_declare_v3_transaction( + self, + compiled_contract: str, + compiled_class_hash: int, + *, + nonce: Optional[int] = None, + ) -> DeclareV3: + contract_class = create_sierra_compiled_contract( + compiled_contract=compiled_contract + ) + + if nonce is None: + nonce = await self.get_nonce() + + declare_tx = DeclareV3( + contract_class=contract_class, + compiled_class_hash=compiled_class_hash, + sender_address=self.address, + signature=[], + nonce=nonce, + version=3, + resource_bounds=ResourceBoundsMapping.init_with_zeros(), + ) + return declare_tx + + async def sign_deploy_account_v1_transaction( self, class_hash: int, contract_address_salt: int, @@ -372,13 +503,11 @@ async def sign_deploy_account_transaction( nonce: int = 0, max_fee: Optional[int] = None, auto_estimate: bool = False, - ) -> DeployAccount: - constructor_calldata = constructor_calldata or [] - - deploy_account_tx = DeployAccount( + ) -> DeployAccountV1: + deploy_account_tx = DeployAccountV1( class_hash=class_hash, contract_address_salt=contract_address_salt, - constructor_calldata=constructor_calldata, + constructor_calldata=(constructor_calldata or []), version=1, max_fee=0, signature=[], @@ -392,6 +521,35 @@ async def sign_deploy_account_transaction( signature = self.signer.sign_transaction(deploy_account_tx) return _add_signature_to_transaction(deploy_account_tx, signature) + async def sign_deploy_account_v3_transaction( + self, + class_hash: int, + contract_address_salt: int, + *, + constructor_calldata: Optional[List[int]] = None, + nonce: int = 0, + l1_resource_bounds: Optional[ResourceBounds] = None, + auto_estimate: bool = False, + ) -> DeployAccountV3: + deploy_account_tx = DeployAccountV3( + class_hash=class_hash, + contract_address_salt=contract_address_salt, + constructor_calldata=(constructor_calldata or []), + version=3, + resource_bounds=ResourceBoundsMapping.init_with_zeros(), + signature=[], + nonce=nonce, + ) + resource_bounds = await self._get_resource_bounds( + deploy_account_tx, l1_resource_bounds, auto_estimate + ) + deploy_account_tx = _add_resource_bounds_to_transaction( + deploy_account_tx, resource_bounds + ) + + signature = self.signer.sign_transaction(deploy_account_tx) + return _add_signature_to_transaction(deploy_account_tx, signature) + async def execute( self, calls: Calls, @@ -400,7 +558,7 @@ async def execute( max_fee: Optional[int] = None, auto_estimate: bool = False, ) -> SentTransactionResponse: - execute_transaction = await self.sign_invoke_transaction( + execute_transaction = await self.sign_invoke_v1_transaction( calls, nonce=nonce, max_fee=max_fee, @@ -408,6 +566,22 @@ async def execute( ) return await self._client.send_transaction(execute_transaction) + async def execute_v3( + self, + calls: Calls, + *, + l1_resource_bounds: Optional[ResourceBounds] = None, + nonce: Optional[int] = None, + auto_estimate: bool = False, + ) -> SentTransactionResponse: + execute_transaction = await self.sign_invoke_v3_transaction( + calls, + l1_resource_bounds=l1_resource_bounds, + nonce=nonce, + auto_estimate=auto_estimate, + ) + return await self._client.send_transaction(execute_transaction) + def sign_message(self, typed_data: TypedData) -> List[int]: typed_data_dataclass = TypedDataDataclass.from_dict(typed_data) return self.signer.sign_message(typed_data_dataclass, self.address) @@ -480,7 +654,7 @@ async def deploy_account( chain=chain, ) - deploy_account_tx = await account.sign_deploy_account_transaction( + deploy_account_tx = await account.sign_deploy_account_v1_transaction( class_hash=class_hash, contract_address_salt=salt, constructor_calldata=calldata, @@ -535,6 +709,26 @@ def _add_max_fee_to_transaction( return dataclasses.replace(tx, max_fee=max_fee) +def _add_resource_bounds_to_transaction( + tx: TypeAccountTransaction, resource_bounds: ResourceBoundsMapping +) -> TypeAccountTransaction: + return dataclasses.replace(tx, resource_bounds=resource_bounds) + + +def _parse_calls(cairo_version: int, calls: Calls) -> List[int]: + if cairo_version == 1: + parsed_calls = _parse_calls_v2(ensure_iterable(calls)) + wrapped_calldata = _execute_payload_serializer_v2.serialize( + {"calls": parsed_calls} + ) + else: + call_descriptions, calldata = _merge_calls(ensure_iterable(calls)) + wrapped_calldata = _execute_payload_serializer.serialize( + {"call_array": call_descriptions, "calldata": calldata} + ) + return wrapped_calldata + + def _parse_call(call: Call, entire_calldata: List) -> Tuple[Dict, List]: _data = { "to": call.to_addr, diff --git a/starknet_py/net/account/base_account.py b/starknet_py/net/account/base_account.py index 57bf02f2f..9f87b9516 100644 --- a/starknet_py/net/account/base_account.py +++ b/starknet_py/net/account/base_account.py @@ -2,13 +2,22 @@ from typing import List, Optional, Union from starknet_py.net.client import Client -from starknet_py.net.client_models import Calls, Hash, SentTransactionResponse, Tag +from starknet_py.net.client_models import ( + Calls, + Hash, + ResourceBounds, + SentTransactionResponse, + Tag, +) from starknet_py.net.models import AddressRepresentation, StarknetChainId from starknet_py.net.models.transaction import ( - Declare, + DeclareV1, DeclareV2, - DeployAccount, - Invoke, + DeclareV3, + DeployAccountV1, + DeployAccountV3, + InvokeV1, + InvokeV3, TypeAccountTransaction, ) from starknet_py.net.models.typed_data import TypedData @@ -102,14 +111,14 @@ async def sign_for_fee_estimate( """ @abstractmethod - async def sign_invoke_transaction( + async def sign_invoke_v1_transaction( self, calls: Calls, *, nonce: Optional[int] = None, max_fee: Optional[int] = None, auto_estimate: bool = False, - ) -> Invoke: + ) -> InvokeV1: """ Takes calls and creates signed Invoke. @@ -121,16 +130,35 @@ async def sign_invoke_transaction( """ @abstractmethod - async def sign_declare_transaction( + async def sign_invoke_v3_transaction( + self, + calls: Calls, + *, + nonce: Optional[int] = None, + l1_resource_bounds: Optional[ResourceBounds] = None, + auto_estimate: bool = False, + ) -> InvokeV3: + """ + Takes calls and creates signed Invoke. + + :param calls: Single call or list of calls. + :param nonce: Nonce of the transaction. + :param l1_resource_bounds: Max amount and max price per unit of L1 gas used in this transaction. + :param auto_estimate: Use automatic fee estimation, not recommend as it may lead to high costs. + :return: Invoke created from the calls. + """ + + @abstractmethod + async def sign_declare_v1_transaction( self, compiled_contract: str, *, nonce: Optional[int] = None, max_fee: Optional[int] = None, auto_estimate: bool = False, - ) -> Declare: + ) -> DeclareV1: """ - Create and sign declare transaction. + Create and sign declare transaction version 1. :param compiled_contract: string containing a compiled Starknet contract. Supports old contracts. :param nonce: Nonce of the transaction. @@ -150,7 +178,7 @@ async def sign_declare_v2_transaction( auto_estimate: bool = False, ) -> DeclareV2: """ - Create and sign declare transaction using sierra contract. + Create and sign declare transaction version 2 using sierra contract. :param compiled_contract: string containing a compiled Starknet contract. Supports new contracts (compiled to sierra). @@ -163,7 +191,30 @@ async def sign_declare_v2_transaction( """ @abstractmethod - async def sign_deploy_account_transaction( + async def sign_declare_v3_transaction( + self, + compiled_contract: str, + compiled_class_hash: int, + *, + nonce: Optional[int] = None, + l1_resource_bounds: Optional[ResourceBounds] = None, + auto_estimate: bool = False, + ) -> DeclareV3: + """ + Create and sign declare transaction version 3 using sierra contract. + + :param compiled_contract: string containing a compiled Starknet contract. + Supports new contracts (compiled to sierra). + :param compiled_class_hash: a class hash of the sierra compiled contract used in the declare transaction. + Computed from casm compiled contract. + :param nonce: Nonce of the transaction. + :param l1_resource_bounds: Max amount and max price per unit of L1 gas used in this transaction. + :param auto_estimate: Use automatic fee estimation, not recommend as it may lead to high costs. + :return: Signed DeclareV3 transaction. + """ + + @abstractmethod + async def sign_deploy_account_v1_transaction( self, class_hash: int, contract_address_salt: int, @@ -172,9 +223,9 @@ async def sign_deploy_account_transaction( nonce: Optional[int] = None, max_fee: Optional[int] = None, auto_estimate: bool = False, - ) -> DeployAccount: + ) -> DeployAccountV1: """ - Create and sign deploy account transaction. + Create and sign deploy account transaction version 1. :param class_hash: Class hash of the contract class to be deployed. :param contract_address_salt: A salt used to calculate deployed contract address. @@ -187,6 +238,31 @@ async def sign_deploy_account_transaction( :return: Signed DeployAccount transaction. """ + @abstractmethod + async def sign_deploy_account_v3_transaction( + self, + class_hash: int, + contract_address_salt: int, + *, + constructor_calldata: Optional[List[int]] = None, + nonce: int = 0, + l1_resource_bounds: Optional[ResourceBounds] = None, + auto_estimate: bool = False, + ) -> DeployAccountV3: + """ + Create and sign deploy account transaction version 3. + + :param class_hash: Class hash of the contract class to be deployed. + :param contract_address_salt: A salt used to calculate deployed contract address. + :param constructor_calldata: Calldata to be ed to contract constructor + and used to calculate deployed contract address. + :param nonce: Nonce of the transaction. + :param l1_resource_bounds: Max amount and max price per unit of L1 gas used in this transaction. + Enough tokens must be prefunded before sending the transaction for it to succeed. + :param auto_estimate: Use automatic fee estimation, not recommend as it may lead to high costs. + :return: Signed DeployAccountV3 transaction. + """ + @abstractmethod async def execute( self, @@ -206,6 +282,25 @@ async def execute( :return: SentTransactionResponse. """ + @abstractmethod + async def execute_v3( + self, + calls: Calls, + *, + l1_resource_bounds: Optional[ResourceBounds] = None, + nonce: Optional[int] = None, + auto_estimate: bool = False, + ) -> SentTransactionResponse: + """ + Takes calls and executes transaction. + + :param calls: Single call or list of calls. + :param l1_resource_bounds: Max amount and max price per unit of L1 gas used in this transaction. + :param nonce: Nonce of the transaction. + :param auto_estimate: Use automatic fee estimation, not recommend as it may lead to high costs. + :return: SentTransactionResponse. + """ + @abstractmethod def sign_message(self, typed_data: TypedData) -> List[int]: """ diff --git a/starknet_py/net/client.py b/starknet_py/net/client.py index 9c0aa0da4..a1504de44 100644 --- a/starknet_py/net/client.py +++ b/starknet_py/net/client.py @@ -28,7 +28,6 @@ from starknet_py.net.models.transaction import ( AccountTransaction, Declare, - DeclareV2, DeployAccount, Invoke, ) @@ -206,13 +205,18 @@ async def wait_for_tx( async def estimate_fee( self, tx: Union[AccountTransaction, List[AccountTransaction]], + skip_validate: bool = False, block_hash: Optional[Union[Hash, Tag]] = None, block_number: Optional[Union[int, Tag]] = None, ) -> Union[EstimatedFee, List[EstimatedFee]]: """ - Estimate how much Wei it will cost to run provided transaction. + Estimates the resources required by a given sequence of transactions when applied on a given state. + If one of the transactions reverts or fails due to any reason (e.g. validation failure or an internal error), + a TRANSACTION_EXECUTION_ERROR is returned. + For v0-2 transactions the estimate is given in Wei, and for v3 transactions it is given in Fri. :param tx: Transaction to estimate + :param skip_validate: Flag checking whether the validation part of the transaction should be executed. :param block_hash: Block's hash or literals `"pending"` or `"latest"`. :param block_number: Block's number or literals `"pending"` or `"latest"`. :return: Estimated amount of Wei executing specified transaction will cost. @@ -258,9 +262,7 @@ async def deploy_account( """ @abstractmethod - async def declare( - self, transaction: Union[Declare, DeclareV2] - ) -> DeclareTransactionResponse: + async def declare(self, transaction: Declare) -> DeclareTransactionResponse: """ Send a declare transaction diff --git a/starknet_py/net/client_models.py b/starknet_py/net/client_models.py index f71171b8b..956cee735 100644 --- a/starknet_py/net/client_models.py +++ b/starknet_py/net/client_models.py @@ -69,18 +69,67 @@ class ResourcePrice: """ price_in_wei: int - price_in_strk: Optional[int] + price_in_fri: int @dataclass -class ResourceLimits: +class ResourceBounds: """ - Dataclass representing resource limits. + Dataclass representing max amount and price of the resource that can be used in the transaction. """ max_amount: int max_price_per_unit: int + @staticmethod + def init_with_zeros(): + return ResourceBounds(max_amount=0, max_price_per_unit=0) + + +@dataclass +class ResourceBoundsMapping: + """ + Dataclass representing resource limits that can be used in the transaction. + """ + + l1_gas: ResourceBounds + l2_gas: ResourceBounds + + @staticmethod + def init_with_zeros(): + return ResourceBoundsMapping( + l1_gas=ResourceBounds.init_with_zeros(), + l2_gas=ResourceBounds.init_with_zeros(), + ) + + +class PriceUnit(Enum): + """ + Enum representing price unit types. + """ + + WEI = "WEI" + FRI = "FRI" + + +@dataclass +class FeePayment: + """ + Dataclass representing fee payment info as it appears in receipts. + """ + + amount: int + unit: PriceUnit + + +class DAMode(Enum): + """ + Specifies a storage domain in Starknet. Each domain has different guarantees regarding availability. + """ + + L1 = 0 + L2 = 1 + class TransactionType(Enum): """ @@ -104,8 +153,6 @@ class Transaction(ABC): # together with the rest of the data, it remains here (but is still Optional just in case as spec says) hash: Optional[int] signature: List[int] - # Optional for DECLARE_V3 and DEPLOY_ACCOUNT_V3, where there is no `max_fee` field, but `l1_gas` - max_fee: Optional[int] version: int def __post_init__(self): @@ -114,7 +161,37 @@ def __post_init__(self): @dataclass -class InvokeTransaction(Transaction): +class DeprecatedTransaction(Transaction): + """ + Dataclass representing common attributes of transactions v1 and v2. + """ + + max_fee: int + + def __post_init__(self): + if self.__class__ == DeprecatedTransaction: + raise TypeError("Cannot instantiate abstract DeprecatedTransaction class.") + + +@dataclass +class TransactionV3(Transaction): + """ + Dataclass representing common attributes of all transactions v3. + """ + + resource_bounds: ResourceBoundsMapping + paymaster_data: List[int] + tip: int + nonce_data_availability_mode: DAMode + fee_data_availability_mode: DAMode + + def __post_init__(self): + if self.__class__ == TransactionV3: + raise TypeError("Cannot instantiate abstract TransactionV3 class.") + + +@dataclass +class InvokeTransaction(DeprecatedTransaction): """ Dataclass representing invoke transaction. """ @@ -127,7 +204,19 @@ class InvokeTransaction(Transaction): @dataclass -class DeclareTransaction(Transaction): +class InvokeTransactionV3(TransactionV3): + """ + Dataclass representing invoke transaction v3. + """ + + sender_address: int + calldata: List[int] + account_deployment_data: List[int] + nonce: int + + +@dataclass +class DeclareTransaction(DeprecatedTransaction): """ Dataclass representing declare transaction. """ @@ -136,7 +225,19 @@ class DeclareTransaction(Transaction): sender_address: int compiled_class_hash: Optional[int] = None # only in DeclareV2, hence Optional nonce: Optional[int] = None - l1_gas: Optional[ResourceLimits] = None # DECLARE_V3-only field, hence Optional + + +@dataclass +class DeclareTransactionV3(TransactionV3): + """ + Dataclass representing declare transaction v3. + """ + + class_hash: int + compiled_class_hash: int + nonce: int + sender_address: int + account_deployment_data: List[int] @dataclass @@ -148,13 +249,10 @@ class DeployTransaction(Transaction): contract_address_salt: int constructor_calldata: List[int] class_hash: int - l1_gas: Optional[ - ResourceLimits - ] = None # DEPLOY_ACCOUNT_V3-only field, hence Optional @dataclass -class DeployAccountTransaction(Transaction): +class DeployAccountTransaction(DeprecatedTransaction): """ Dataclass representing deploy account transaction. """ @@ -165,6 +263,18 @@ class DeployAccountTransaction(Transaction): nonce: int +@dataclass +class DeployAccountTransactionV3(TransactionV3): + """ + Dataclass representing deploy account transaction v3. + """ + + contract_address_salt: int + class_hash: int + constructor_calldata: List[int] + nonce: int + + @dataclass class L1HandlerTransaction(Transaction): """ @@ -215,13 +325,13 @@ class ExecutionResources: # pylint: disable=too-many-instance-attributes steps: int - range_check_builtin_applications: int - pedersen_builtin_applications: int - poseidon_builtin_applications: int - ec_op_builtin_applications: int - ecdsa_builtin_applications: int - bitwise_builtin_applications: int - keccak_builtin_applications: int + range_check_builtin_applications: Optional[int] = None + pedersen_builtin_applications: Optional[int] = None + poseidon_builtin_applications: Optional[int] = None + ec_op_builtin_applications: Optional[int] = None + ecdsa_builtin_applications: Optional[int] = None + bitwise_builtin_applications: Optional[int] = None + keccak_builtin_applications: Optional[int] = None memory_holes: Optional[int] = None @@ -238,6 +348,7 @@ class TransactionReceipt: execution_status: TransactionExecutionStatus finality_status: TransactionFinalityStatus execution_resources: ExecutionResources + actual_fee: FeePayment type: TransactionType events: List[Event] = field(default_factory=list) @@ -247,7 +358,6 @@ class TransactionReceipt: block_number: Optional[int] = None block_hash: Optional[int] = None - actual_fee: int = 0 message_hash: Optional[int] = None # L1_HANDLER_TXN_RECEIPT-only @@ -405,7 +515,8 @@ class EstimatedFee: overall_fee: int gas_price: int - gas_usage: int + gas_consumed: int + unit: PriceUnit @dataclass @@ -689,6 +800,7 @@ class FunctionInvocation: calls: List["FunctionInvocation"] events: List[OrderedEvent] messages: List[OrderedMessage] + execution_resources: ExecutionResources @dataclass diff --git a/starknet_py/net/client_test.py b/starknet_py/net/client_test.py index 778feeb1a..a29ff712f 100644 --- a/starknet_py/net/client_test.py +++ b/starknet_py/net/client_test.py @@ -1,7 +1,12 @@ import pytest from starknet_py.constants import ADDR_BOUND -from starknet_py.net.client_models import Transaction +from starknet_py.net.client_models import ( + DAMode, + ResourceBoundsMapping, + Transaction, + TransactionV3, +) from starknet_py.net.full_node_client import _to_storage_key from starknet_py.net.http_client import RpcHttpClient, ServerError @@ -18,7 +23,23 @@ def test_cannot_instantiate_abstract_transaction_class(): with pytest.raises( TypeError, match="Cannot instantiate abstract Transaction class." ): - _ = Transaction(hash=0, signature=[0, 0], max_fee=0, version=0) + _ = Transaction(hash=0, signature=[0, 0], version=0) + + +def test_cannot_instantiate_abstract_transaction_v3_class(): + with pytest.raises( + TypeError, match="Cannot instantiate abstract TransactionV3 class." + ): + _ = TransactionV3( + hash=0, + signature=[0, 0], + version=0, + paymaster_data=[], + tip=0, + nonce_data_availability_mode=DAMode.L1, + fee_data_availability_mode=DAMode.L1, + resource_bounds=ResourceBoundsMapping.init_with_zeros(), + ) def test_handle_rpc_error_server_error(): diff --git a/starknet_py/net/client_utils.py b/starknet_py/net/client_utils.py index 320e827df..97b3f45a2 100644 --- a/starknet_py/net/client_utils.py +++ b/starknet_py/net/client_utils.py @@ -1,3 +1,4 @@ +import re from typing import Union from typing_extensions import get_args @@ -33,3 +34,46 @@ def encode_l1_message(tx: L1HandlerTransaction) -> bytes: + encode_uint(len(tx.calldata)) + encode_uint_list(tx.calldata) ) + + +def _to_storage_key(key: int) -> str: + """ + Convert a value to RPC storage key matching a ``^0x0[0-7]{1}[a-fA-F0-9]{0,62}$`` pattern. + + :param key: The key to convert. + :return: RPC storage key representation of the key. + """ + + hashed_key = hex(key)[2:] + + if hashed_key[0] not in ("0", "1", "2", "3", "4", "5", "6", "7"): + hashed_key = "0" + hashed_key + + hashed_key = "0x0" + hashed_key + + if not re.match(r"^0x0[0-7]{1}[a-fA-F0-9]{0,62}$", hashed_key): + raise ValueError(f"Value {key} cannot be represented as RPC storage key.") + + return hashed_key + + +def _to_rpc_felt(value: Hash) -> str: + """ + Convert the value to RPC felt matching a ``^0x(0|[a-fA-F1-9]{1}[a-fA-F0-9]{0,62})$`` pattern. + + :param value: The value to convert. + :return: RPC felt representation of the value. + """ + if isinstance(value, str): + value = int(value, 16) + + rpc_felt = hex(value) + assert re.match(r"^0x(0|[a-fA-F1-9]{1}[a-fA-F0-9]{0,62})$", rpc_felt) + return rpc_felt + + +def _is_valid_eth_address(address: str) -> bool: + """ + A function checking if an address matches Ethereum address regex. Note that it doesn't validate any checksums etc. + """ + return bool(re.fullmatch("^0x[a-fA-F0-9]{40}$", address)) diff --git a/starknet_py/net/full_node_client.py b/starknet_py/net/full_node_client.py index e09e4f052..6c453d6b6 100644 --- a/starknet_py/net/full_node_client.py +++ b/starknet_py/net/full_node_client.py @@ -1,4 +1,3 @@ -import re from typing import Dict, List, Optional, Tuple, Union, cast import aiohttp @@ -37,17 +36,26 @@ TransactionTrace, TransactionType, ) -from starknet_py.net.client_utils import encode_l1_message +from starknet_py.net.client_utils import ( + _is_valid_eth_address, + _to_rpc_felt, + _to_storage_key, + encode_l1_message, +) from starknet_py.net.http_client import RpcHttpClient from starknet_py.net.models.transaction import ( AccountTransaction, Declare, - DeclareSchema, + DeclareV1Schema, DeclareV2, DeclareV2Schema, + DeclareV3, DeployAccount, + DeployAccountV3, Invoke, + InvokeV3, ) +from starknet_py.net.schemas.gateway import SierraCompiledContractSchema from starknet_py.net.schemas.rpc import ( BlockHashAndNumberSchema, BlockStateUpdateSchema, @@ -60,6 +68,7 @@ PendingBlockStateUpdateSchema, PendingStarknetBlockSchema, PendingStarknetBlockWithTxHashesSchema, + ResourceBoundsMappingSchema, SentTransactionSchema, SierraContractClassSchema, SimulatedTransactionSchema, @@ -71,6 +80,7 @@ TransactionTraceSchema, TypesOfTransactionsSchema, ) +from starknet_py.net.schemas.utils import _extract_tx_version from starknet_py.transaction_errors import TransactionNotReceivedError from starknet_py.utils.sync import add_sync_methods @@ -336,6 +346,7 @@ async def get_transaction_receipt(self, tx_hash: Hash) -> TransactionReceipt: async def estimate_fee( self, tx: Union[AccountTransaction, List[AccountTransaction]], + skip_validate: bool = False, block_hash: Optional[Union[Hash, Tag]] = None, block_number: Optional[Union[int, Tag]] = None, ) -> Union[EstimatedFee, List[EstimatedFee]]: @@ -350,6 +361,9 @@ async def estimate_fee( method_name="estimateFee", params={ "request": [_create_broadcasted_txn(transaction=t) for t in tx], + "simulation_flags": [SimulationFlag.SKIP_VALIDATE] + if skip_validate + else [], **block_identifier, }, ) @@ -485,9 +499,7 @@ async def deploy_account( DeployAccountTransactionResponseSchema().load(res, unknown=EXCLUDE), ) - async def declare( - self, transaction: Union[Declare, DeclareV2] - ) -> DeclareTransactionResponse: + async def declare(self, transaction: Declare) -> DeclareTransactionResponse: params = _create_broadcasted_txn(transaction=transaction) res = await self._client.call( @@ -703,7 +715,13 @@ async def simulate_transactions( # pylint: disable=too-many-arguments """ Simulates a given sequence of transactions on the requested state, and generates the execution traces. - If one of the transactions is reverted, raises CONTRACT_ERROR. + Note the following: + + - A transaction may revert. If this occurs, no error is thrown. Instead, revert details are visible + in the returned trace object. + - If a transaction reverts, this will be reflected by the revert_error property in the trace. + - Other types of failures (e.g. unexpected error or failure in the validation phase) will result + in TRANSACTION_EXECUTION_ERROR. :param transactions: Transactions to be traced. :param skip_validate: Flag checking whether the validation part of the transaction should be executed. @@ -811,12 +829,16 @@ def _create_broadcasted_txn(transaction: AccountTransaction) -> dict: def _create_broadcasted_declare_properties( - transaction: Union[Declare, DeclareV2] + transaction: Union[Declare, DeclareV2, DeclareV3] ) -> dict: if isinstance(transaction, DeclareV2): return _create_broadcasted_declare_v2_properties(transaction) + if isinstance(transaction, DeclareV3): + return _create_broadcasted_declare_v3_properties(transaction) - contract_class = cast(Dict, DeclareSchema().dump(obj=transaction))["contract_class"] + contract_class = cast(Dict, DeclareV1Schema().dump(obj=transaction))[ + "contract_class" + ] declare_properties = { "contract_class": { "entry_points_by_type": contract_class["entry_points_by_type"], @@ -849,15 +871,54 @@ def _create_broadcasted_declare_v2_properties(transaction: DeclareV2) -> dict: return declare_v2_properties -def _create_broadcasted_invoke_properties(transaction: Invoke) -> dict: +def _create_broadcasted_declare_v3_properties(transaction: DeclareV3) -> dict: + contract_class = cast( + Dict, SierraCompiledContractSchema().dump(obj=transaction.contract_class) + ) + + declare_v3_properties = { + "contract_class": { + "entry_points_by_type": contract_class["entry_points_by_type"], + "sierra_program": contract_class["sierra_program"], + "contract_class_version": contract_class["contract_class_version"], + }, + "sender_address": _to_rpc_felt(transaction.sender_address), + "compiled_class_hash": _to_rpc_felt(transaction.compiled_class_hash), + "account_deployment_data": [ + _to_rpc_felt(data) for data in transaction.account_deployment_data + ], + } + + if contract_class["abi"] is not None: + declare_v3_properties["contract_class"]["abi"] = contract_class["abi"] + + return { + **_create_broadcasted_txn_v3_common_properties(transaction), + **declare_v3_properties, + } + + +def _create_broadcasted_invoke_properties(transaction: Union[Invoke, InvokeV3]) -> dict: invoke_properties = { "sender_address": _to_rpc_felt(transaction.sender_address), "calldata": [_to_rpc_felt(data) for data in transaction.calldata], } + + if isinstance(transaction, InvokeV3): + return { + **_create_broadcasted_txn_v3_common_properties(transaction), + **invoke_properties, + "account_deployment_data": [ + _to_rpc_felt(data) for data in transaction.account_deployment_data + ], + } + return invoke_properties -def _create_broadcasted_deploy_account_properties(transaction: DeployAccount) -> dict: +def _create_broadcasted_deploy_account_properties( + transaction: Union[DeployAccount, DeployAccountV3] +) -> dict: deploy_account_txn_properties = { "contract_address_salt": _to_rpc_felt(transaction.contract_address_salt), "constructor_calldata": [ @@ -865,58 +926,45 @@ def _create_broadcasted_deploy_account_properties(transaction: DeployAccount) -> ], "class_hash": _to_rpc_felt(transaction.class_hash), } + + if isinstance(transaction, DeployAccountV3): + return { + **_create_broadcasted_txn_v3_common_properties(transaction), + **deploy_account_txn_properties, + } + return deploy_account_txn_properties def _create_broadcasted_txn_common_properties(transaction: AccountTransaction) -> dict: broadcasted_txn_common_properties = { "type": transaction.type.name, - "max_fee": _to_rpc_felt(transaction.max_fee), "version": _to_rpc_felt(transaction.version), "signature": [_to_rpc_felt(sig) for sig in transaction.signature], "nonce": _to_rpc_felt(transaction.nonce), } - return broadcasted_txn_common_properties - - -def _to_storage_key(key: int) -> str: - """ - Convert a value to RPC storage key matching a ``^0x0[0-7]{1}[a-fA-F0-9]{0,62}$`` pattern. - - :param key: The key to convert. - :return: RPC storage key representation of the key. - """ - hashed_key = hex(key)[2:] - - if hashed_key[0] not in ("0", "1", "2", "3", "4", "5", "6", "7"): - hashed_key = "0" + hashed_key - - hashed_key = "0x0" + hashed_key - - if not re.match(r"^0x0[0-7]{1}[a-fA-F0-9]{0,62}$", hashed_key): - raise ValueError(f"Value {key} cannot be represented as RPC storage key.") - - return hashed_key - - -def _to_rpc_felt(value: Hash) -> str: - """ - Convert the value to RPC felt matching a ``^0x(0|[a-fA-F1-9]{1}[a-fA-F0-9]{0,62})$`` pattern. + if _extract_tx_version(transaction.version) < 3 and hasattr(transaction, "max_fee"): + broadcasted_txn_common_properties["max_fee"] = _to_rpc_felt( + transaction.max_fee # pyright: ignore + ) - :param value: The value to convert. - :return: RPC felt representation of the value. - """ - if isinstance(value, str): - value = int(value, 16) + return broadcasted_txn_common_properties - rpc_felt = hex(value) - assert re.match(r"^0x(0|[a-fA-F1-9]{1}[a-fA-F0-9]{0,62})$", rpc_felt) - return rpc_felt +def _create_broadcasted_txn_v3_common_properties( + transaction: Union[DeclareV3, InvokeV3, DeployAccountV3] +) -> dict: + resource_bonds = cast( + Dict, ResourceBoundsMappingSchema().dump(obj=transaction.resource_bounds) + ) + + broadcasted_txn_v3_common_properties = { + "resource_bounds": resource_bonds, + "tip": _to_rpc_felt(transaction.tip), + "paymaster_data": [_to_rpc_felt(data) for data in transaction.paymaster_data], + "nonce_data_availability_mode": transaction.nonce_data_availability_mode.name, + "fee_data_availability_mode": transaction.fee_data_availability_mode.name, + } -def _is_valid_eth_address(address: str) -> bool: - """ - A function checking if an address matches Ethereum address regex. Note that it doesn't validate any checksums etc. - """ - return bool(re.fullmatch("^0x[a-fA-F0-9]{40}$", address)) + return broadcasted_txn_v3_common_properties diff --git a/starknet_py/net/models/__init__.py b/starknet_py/net/models/__init__.py index 736b9b9a8..42b1591f8 100644 --- a/starknet_py/net/models/__init__.py +++ b/starknet_py/net/models/__init__.py @@ -2,9 +2,12 @@ from .chains import StarknetChainId, chain_from_network from .transaction import ( AccountTransaction, - Declare, + DeclareV1, DeclareV2, - DeployAccount, - Invoke, + DeclareV3, + DeployAccountV1, + DeployAccountV3, + InvokeV1, + InvokeV3, Transaction, ) diff --git a/starknet_py/net/models/transaction.py b/starknet_py/net/models/transaction.py index f9634044a..3fcb4ac15 100644 --- a/starknet_py/net/models/transaction.py +++ b/starknet_py/net/models/transaction.py @@ -5,11 +5,12 @@ """ import base64 +import dataclasses import gzip import json from abc import ABC, abstractmethod from dataclasses import dataclass, field -from typing import Any, Dict, List, TypeVar +from typing import Any, Dict, List, TypeVar, Union import marshmallow import marshmallow_dataclass @@ -17,13 +18,20 @@ from starknet_py.hash.address import compute_address from starknet_py.hash.transaction import ( + CommonTransactionV3Fields, + TransactionHashPrefix, compute_declare_transaction_hash, compute_declare_v2_transaction_hash, + compute_declare_v3_transaction_hash, compute_deploy_account_transaction_hash, + compute_deploy_account_v3_transaction_hash, compute_invoke_transaction_hash, + compute_invoke_v3_transaction_hash, ) from starknet_py.net.client_models import ( ContractClass, + DAMode, + ResourceBoundsMapping, SierraContractClass, TransactionType, ) @@ -34,6 +42,10 @@ SierraContractClassSchema, ) +# TODO (#1219): +# consider unifying these classes with client_models +# remove marshmallow logic if not needed + @dataclass(frozen=True) class Transaction(ABC): @@ -65,7 +77,6 @@ class AccountTransaction(Transaction, ABC): account. """ - max_fee: int = field(metadata={"marshmallow_field": Felt()}) signature: List[int] = field( metadata={"marshmallow_field": fields.List(fields.String())} ) @@ -77,7 +88,67 @@ class AccountTransaction(Transaction, ABC): @dataclass(frozen=True) -class DeclareV2(AccountTransaction): +class _DeprecatedAccountTransaction(AccountTransaction, ABC): + max_fee: int = field(metadata={"marshmallow_field": Felt()}) + + +@dataclass(frozen=True) +class _AccountTransactionV3(AccountTransaction, ABC): + resource_bounds: ResourceBoundsMapping + tip: int = field(init=False, default=0) + nonce_data_availability_mode: DAMode = field(init=False, default=DAMode.L1) + fee_data_availability_mode: DAMode = field(init=False, default=DAMode.L1) + paymaster_data: List[int] = field(init=False, default_factory=list) + + def get_common_fields( + self, tx_prefix: TransactionHashPrefix, address: int, chain_id: StarknetChainId + ) -> CommonTransactionV3Fields: + common_fields = [f.name for f in dataclasses.fields(_AccountTransactionV3)] + + # this is a helper function in a process to compute transaction hash + # therefore signature is not included at this point + common_fields.remove("signature") + + common_fields_with_values = { + field_name: getattr(self, field_name) for field_name in common_fields + } + + return CommonTransactionV3Fields( + tx_prefix=tx_prefix, + address=address, + chain_id=chain_id, + **common_fields_with_values, + ) + + +@dataclass(frozen=True) +class DeclareV3(_AccountTransactionV3): + """ + Represents a transaction in the Starknet network that is a version 3 declaration of a Starknet contract + class. Supports only sierra compiled contracts. + """ + + sender_address: int + compiled_class_hash: int + contract_class: SierraContractClass + account_deployment_data: List[int] = field(default_factory=list) + type: TransactionType = TransactionType.DECLARE + + def calculate_hash(self, chain_id: StarknetChainId) -> int: + return compute_declare_v3_transaction_hash( + account_deployment_data=self.account_deployment_data, + contract_class=self.contract_class, + compiled_class_hash=self.compiled_class_hash, + common_fields=self.get_common_fields( + tx_prefix=TransactionHashPrefix.DECLARE, + address=self.sender_address, + chain_id=chain_id, + ), + ) + + +@dataclass(frozen=True) +class DeclareV2(_DeprecatedAccountTransaction): """ Represents a transaction in the Starknet network that is a version 2 declaration of a Starknet contract class. Supports only sierra compiled contracts. @@ -106,7 +177,7 @@ def calculate_hash(self, chain_id: StarknetChainId) -> int: @dataclass(frozen=True) -class Declare(AccountTransaction): +class DeclareV1(_DeprecatedAccountTransaction): """ Represents a transaction in the Starknet network that is a declaration of a Starknet contract class. @@ -150,7 +221,38 @@ def calculate_hash(self, chain_id: StarknetChainId) -> int: @dataclass(frozen=True) -class DeployAccount(AccountTransaction): +class DeployAccountV3(_AccountTransactionV3): + """ + Represents a transaction in the Starknet network that is a version 3 deployment of a Starknet account + contract. + """ + + class_hash: int + contract_address_salt: int + constructor_calldata: List[int] + type: TransactionType = TransactionType.DEPLOY_ACCOUNT + + def calculate_hash(self, chain_id: StarknetChainId) -> int: + contract_address = compute_address( + salt=self.contract_address_salt, + class_hash=self.class_hash, + constructor_calldata=self.constructor_calldata, + deployer_address=0, + ) + return compute_deploy_account_v3_transaction_hash( + class_hash=self.class_hash, + constructor_calldata=self.constructor_calldata, + contract_address_salt=self.contract_address_salt, + common_fields=self.get_common_fields( + tx_prefix=TransactionHashPrefix.DEPLOY_ACCOUNT, + address=contract_address, + chain_id=chain_id, + ), + ) + + +@dataclass(frozen=True) +class DeployAccountV1(_DeprecatedAccountTransaction): """ Represents a transaction in the Starknet network that is a deployment of a Starknet account contract. @@ -161,7 +263,6 @@ class DeployAccount(AccountTransaction): constructor_calldata: List[int] = field( metadata={"marshmallow_field": fields.List(fields.String())} ) - type: TransactionType = field( metadata={"marshmallow_field": TransactionTypeField()}, default=TransactionType.DEPLOY_ACCOUNT, @@ -190,7 +291,31 @@ def calculate_hash(self, chain_id: StarknetChainId) -> int: @dataclass(frozen=True) -class Invoke(AccountTransaction): +class InvokeV3(_AccountTransactionV3): + """ + Represents a transaction in the Starknet network that is a version 3 invocation of a Cairo contract + function. + """ + + calldata: List[int] + sender_address: int + account_deployment_data: List[int] = field(default_factory=list) + type: TransactionType = TransactionType.INVOKE + + def calculate_hash(self, chain_id: StarknetChainId) -> int: + return compute_invoke_v3_transaction_hash( + account_deployment_data=self.account_deployment_data, + calldata=self.calldata, + common_fields=self.get_common_fields( + tx_prefix=TransactionHashPrefix.INVOKE, + address=self.sender_address, + chain_id=chain_id, + ), + ) + + +@dataclass(frozen=True) +class InvokeV1(_DeprecatedAccountTransaction): """ Represents a transaction in the Starknet network that is an invocation of a Cairo contract function. @@ -200,7 +325,6 @@ class Invoke(AccountTransaction): calldata: List[int] = field( metadata={"marshmallow_field": fields.List(fields.String())} ) - type: TransactionType = field( metadata={"marshmallow_field": TransactionTypeField()}, default=TransactionType.INVOKE, @@ -220,10 +344,14 @@ def calculate_hash(self, chain_id: StarknetChainId) -> int: ) -InvokeSchema = marshmallow_dataclass.class_schema(Invoke) -DeclareSchema = marshmallow_dataclass.class_schema(Declare) +Declare = Union[DeclareV1, DeclareV2, DeclareV3] +DeployAccount = Union[DeployAccountV1, DeployAccountV3] +Invoke = Union[InvokeV1, InvokeV3] + +InvokeV1Schema = marshmallow_dataclass.class_schema(InvokeV1) +DeclareV1Schema = marshmallow_dataclass.class_schema(DeclareV1) DeclareV2Schema = marshmallow_dataclass.class_schema(DeclareV2) -DeployAccountSchema = marshmallow_dataclass.class_schema(DeployAccount) +DeployAccountV1Schema = marshmallow_dataclass.class_schema(DeployAccountV1) def compress_program(data: dict, program_name: str = "program") -> dict: diff --git a/starknet_py/net/models/transaction_test.py b/starknet_py/net/models/transaction_test.py index e6de7dad0..a4db369ed 100644 --- a/starknet_py/net/models/transaction_test.py +++ b/starknet_py/net/models/transaction_test.py @@ -6,15 +6,16 @@ from starknet_py.net.client_models import TransactionType from starknet_py.net.models.transaction import ( Declare, - DeclareSchema, - Invoke, - InvokeSchema, + DeclareV1, + DeclareV1Schema, + InvokeV1, + InvokeV1Schema, ) def test_declare_compress_program(balance_contract): contract_class = create_contract_class(balance_contract) - declare_transaction = Declare( + declare_transaction = DeclareV1( contract_class=contract_class, sender_address=0x1234, max_fee=0x1111, @@ -23,7 +24,7 @@ def test_declare_compress_program(balance_contract): version=1, ) - schema = DeclareSchema() + schema = DeclareV1Schema() serialized = typing.cast(dict, schema.dump(declare_transaction)) # Pattern used in match taken from @@ -47,10 +48,10 @@ def test_serialize_deserialize_invoke(): "version": "0x1", "type": "INVOKE_FUNCTION", } - invoke = InvokeSchema().load(data) - serialized_invoke = InvokeSchema().dump(invoke) + invoke = InvokeV1Schema().load(data) + serialized_invoke = InvokeV1Schema().dump(invoke) - assert isinstance(invoke, Invoke) + assert isinstance(invoke, InvokeV1) assert invoke.type == TransactionType.INVOKE assert isinstance(serialized_invoke, dict) assert serialized_invoke["type"] == "INVOKE_FUNCTION" diff --git a/starknet_py/net/schemas/common.py b/starknet_py/net/schemas/common.py index 3850199e0..887badd4d 100644 --- a/starknet_py/net/schemas/common.py +++ b/starknet_py/net/schemas/common.py @@ -6,7 +6,9 @@ from starknet_py.net.client_models import ( BlockStatus, CallType, + DAMode, EntryPointType, + PriceUnit, StorageEntry, TransactionExecutionStatus, TransactionFinalityStatus, @@ -215,6 +217,44 @@ def _deserialize( return CallType(value) +class PriceUnitField(fields.Field): + def _serialize(self, value: Any, attr: str, obj: Any, **kwargs): + return value.name if value is not None else "" + + def _deserialize( + self, + value: Any, + attr: Optional[str], + data: Optional[Mapping[str, Any]], + **kwargs, + ) -> PriceUnit: + values = [v.value for v in PriceUnit] + + if value not in values: + raise ValidationError(f"Invalid value provided for PriceUnit: {value}.") + + return PriceUnit(value) + + +class DAModeField(fields.Field): + def _serialize(self, value: Any, attr: str, obj: Any, **kwargs): + return value.name if value is not None else "" + + def _deserialize( + self, + value: Any, + attr: Optional[str], + data: Optional[Mapping[str, Any]], + **kwargs, + ) -> DAMode: + names = [v.name for v in DAMode] + + if value not in names: + raise ValidationError(f"Invalid value provided for DAMode: {value}.") + + return DAMode[value] + + class StorageEntrySchema(Schema): key = Felt(data_key="key", required=True) value = Felt(data_key="value", required=True) diff --git a/starknet_py/net/schemas/rpc.py b/starknet_py/net/schemas/rpc.py index 878ab90c3..18bbf12a7 100644 --- a/starknet_py/net/schemas/rpc.py +++ b/starknet_py/net/schemas/rpc.py @@ -8,13 +8,16 @@ BlockTransactionTrace, ContractClass, ContractsNonce, + DAMode, DeclaredContractHash, DeclareTransaction, DeclareTransactionResponse, DeclareTransactionTrace, + DeclareTransactionV3, DeployAccountTransaction, DeployAccountTransactionResponse, DeployAccountTransactionTrace, + DeployAccountTransactionV3, DeployedContract, DeployTransaction, EntryPoint, @@ -23,9 +26,11 @@ Event, EventsChunk, ExecutionResources, + FeePayment, FunctionInvocation, InvokeTransaction, InvokeTransactionTrace, + InvokeTransactionV3, L1HandlerTransaction, L1HandlerTransactionTrace, L2toL1Message, @@ -35,6 +40,8 @@ PendingStarknetBlock, PendingStarknetBlockWithTxHashes, ReplacedClass, + ResourceBounds, + ResourceBoundsMapping, ResourcePrice, RevertedFunctionInvocation, SentTransactionResponse, @@ -53,16 +60,19 @@ from starknet_py.net.schemas.common import ( BlockStatusField, CallTypeField, + DAModeField, EntryPointTypeField, ExecutionStatusField, Felt, FinalityStatusField, NonPrefixedHex, + PriceUnitField, StatusField, StorageEntrySchema, TransactionTypeField, ) from starknet_py.net.schemas.utils import ( + _extract_tx_version, _replace_invoke_contract_address_with_sender_address, ) @@ -105,25 +115,25 @@ def make_dataclass(self, data, **kwargs) -> L2toL1Message: class ExecutionResourcesSchema(Schema): steps = Felt(data_key="steps", required=True) range_check_builtin_applications = Felt( - data_key="range_check_builtin_applications", required=True + data_key="range_check_builtin_applications", load_default=None ) pedersen_builtin_applications = Felt( - data_key="pedersen_builtin_applications", required=True + data_key="pedersen_builtin_applications", load_default=None ) poseidon_builtin_applications = Felt( - data_key="poseidon_builtin_applications", required=True + data_key="poseidon_builtin_applications", load_default=None ) ec_op_builtin_applications = Felt( - data_key="ec_op_builtin_applications", required=True + data_key="ec_op_builtin_applications", load_default=None ) ecdsa_builtin_applications = Felt( - data_key="ecdsa_builtin_applications", required=True + data_key="ecdsa_builtin_applications", load_default=None ) bitwise_builtin_applications = Felt( - data_key="bitwise_builtin_applications", required=True + data_key="bitwise_builtin_applications", load_default=None ) keccak_builtin_applications = Felt( - data_key="keccak_builtin_applications", required=True + data_key="keccak_builtin_applications", load_default=None ) memory_holes = Felt(data_key="memory_holes", load_default=None) @@ -132,14 +142,23 @@ def make_dataclass(self, data, **kwargs) -> ExecutionResources: return ExecutionResources(**data) +class FeePaymentSchema(Schema): + amount = Felt(data_key="amount", required=True) + unit = PriceUnitField(data_key="unit", required=True) + + @post_load + def make_dataclass(self, data, **kwargs) -> FeePayment: + return FeePayment(**data) + + class TransactionReceiptSchema(Schema): transaction_hash = Felt(data_key="transaction_hash", required=True) execution_status = ExecutionStatusField(data_key="execution_status", required=True) finality_status = FinalityStatusField(data_key="finality_status", required=True) 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) - type = TransactionTypeField(data_key="type", load_default=None) + actual_fee = fields.Nested(FeePaymentSchema(), data_key="actual_fee", required=True) + type = TransactionTypeField(data_key="type", required=True) contract_address = Felt(data_key="contract_address", load_default=None) revert_reason = fields.String(data_key="revert_reason", load_default=None) events = fields.List( @@ -161,7 +180,8 @@ def make_dataclass(self, data, **kwargs) -> TransactionReceipt: class EstimatedFeeSchema(Schema): overall_fee = Felt(data_key="overall_fee", required=True) gas_price = Felt(data_key="gas_price", required=True) - gas_usage = Felt(data_key="gas_consumed", required=True) + gas_consumed = Felt(data_key="gas_consumed", required=True) + unit = PriceUnitField(data_key="unit", required=True) @post_load def make_dataclass(self, data, **kwargs): @@ -180,7 +200,7 @@ def make_dataclass(self, data, **kwargs) -> TransactionStatusResponse: class ResourcePriceSchema(Schema): - price_in_strk = Felt(data_key="price_in_strk", load_default=None) + price_in_fri = Felt(data_key="price_in_fri", required=True) price_in_wei = Felt(data_key="price_in_wei", required=True) @post_load @@ -188,14 +208,49 @@ def make_dataclass(self, data, **kwargs) -> ResourcePrice: return ResourcePrice(**data) +class ResourceBoundsSchema(Schema): + max_amount = Felt(data_key="max_amount", required=True) + max_price_per_unit = Felt(data_key="max_price_per_unit", required=True) + + @post_load + def make_dataclass(self, data, **kwargs) -> ResourceBounds: + return ResourceBounds(**data) + + +class ResourceBoundsMappingSchema(Schema): + l1_gas = fields.Nested(ResourceBoundsSchema(), data_key="l1_gas", required=True) + l2_gas = fields.Nested(ResourceBoundsSchema(), data_key="l2_gas", required=True) + + @post_load + def make_dataclass(self, data, **kwargs) -> ResourceBoundsMapping: + return ResourceBoundsMapping(**data) + + class TransactionSchema(Schema): hash = Felt(data_key="transaction_hash", load_default=None) signature = fields.List(Felt(), data_key="signature", load_default=[]) - max_fee = Felt(data_key="max_fee", load_default=0) version = Felt(data_key="version", required=True) -class InvokeTransactionSchema(TransactionSchema): +class DeprecatedTransactionSchema(TransactionSchema): + max_fee = Felt(data_key="max_fee", required=True) + + +class TransactionV3Schema(TransactionSchema): + tip = Felt(data_key="tip", load_default=0) + nonce_data_availability_mode = DAModeField( + data_key="nonce_data_availability_mode", load_default=DAMode.L1 + ) + fee_data_availability_mode = DAModeField( + data_key="fee_data_availability_mode", load_default=DAMode.L1 + ) + paymaster_data = fields.List(Felt(), data_key="paymaster_data", load_default=[]) + resource_bounds = fields.Nested( + ResourceBoundsMappingSchema(), data_key="resource_bounds", required=True + ) + + +class DeprecatedInvokeTransactionSchema(DeprecatedTransactionSchema): contract_address = Felt(data_key="contract_address", load_default=None) sender_address = Felt(data_key="sender_address", load_default=None) entry_point_selector = Felt(data_key="entry_point_selector", load_default=None) @@ -208,7 +263,20 @@ def make_transaction(self, data, **kwargs) -> InvokeTransaction: return InvokeTransaction(**data) -class DeclareTransactionSchema(TransactionSchema): +class InvokeTransactionV3Schema(TransactionV3Schema): + sender_address = Felt(data_key="sender_address", required=True) + calldata = fields.List(Felt(), data_key="calldata", required=True) + account_deployment_data = fields.List( + Felt(), data_key="account_deployment_data", load_default=[] + ) + nonce = Felt(data_key="nonce", required=True) + + @post_load + def make_transaction(self, data, **kwargs) -> InvokeTransactionV3: + return InvokeTransactionV3(**data) + + +class DeprecatedDeclareTransactionSchema(DeprecatedTransactionSchema): class_hash = Felt(data_key="class_hash", required=True) sender_address = Felt(data_key="sender_address", required=True) nonce = Felt(data_key="nonce", load_default=None) @@ -219,6 +287,20 @@ def make_dataclass(self, data, **kwargs) -> DeclareTransaction: return DeclareTransaction(**data) +class DeclareTransactionV3Schema(TransactionV3Schema): + class_hash = Felt(data_key="class_hash", required=True) + compiled_class_hash = Felt(data_key="compiled_class_hash", load_default=None) + nonce = Felt(data_key="nonce", required=True) + sender_address = Felt(data_key="sender_address", required=True) + account_deployment_data = fields.List( + Felt(), data_key="account_deployment_data", load_default=[] + ) + + @post_load + def make_dataclass(self, data, **kwargs) -> DeclareTransactionV3: + return DeclareTransactionV3(**data) + + class DeployTransactionSchema(TransactionSchema): contract_address_salt = Felt(data_key="contract_address_salt", required=True) constructor_calldata = fields.List( @@ -231,7 +313,7 @@ def make_dataclass(self, data, **kwargs) -> DeployTransaction: return DeployTransaction(**data) -class DeployAccountTransactionSchema(TransactionSchema): +class DeprecatedDeployAccountTransactionSchema(DeprecatedTransactionSchema): contract_address_salt = Felt(data_key="contract_address_salt", required=True) constructor_calldata = fields.List( Felt(), data_key="constructor_calldata", required=True @@ -244,6 +326,52 @@ def make_dataclass(self, data, **kwargs) -> DeployAccountTransaction: return DeployAccountTransaction(**data) +class DeployAccountTransactionV3Schema(TransactionV3Schema): + contract_address_salt = Felt(data_key="contract_address_salt", required=True) + constructor_calldata = fields.List( + Felt(), data_key="constructor_calldata", required=True + ) + class_hash = Felt(data_key="class_hash", required=True) + nonce = Felt(data_key="nonce", required=True) + + @post_load + def make_dataclass(self, data, **kwargs) -> DeployAccountTransactionV3: + return DeployAccountTransactionV3(**data) + + +class DeclareTransactionSchema(OneOfSchema): + type_schemas = { + 0: DeprecatedDeclareTransactionSchema, + 1: DeprecatedDeclareTransactionSchema, + 2: DeprecatedDeclareTransactionSchema, + 3: DeclareTransactionV3Schema, + } + + def get_data_type(self, data): + return _extract_tx_version(data.get("version")) + + +class InvokeTransactionSchema(OneOfSchema): + type_schemas = { + 0: DeprecatedInvokeTransactionSchema, + 1: DeprecatedInvokeTransactionSchema, + 3: InvokeTransactionV3Schema, + } + + def get_data_type(self, data): + return _extract_tx_version(data.get("version")) + + +class DeployAccountTransactionSchema(OneOfSchema): + type_schemas = { + 1: DeprecatedDeployAccountTransactionSchema, + 3: DeployAccountTransactionV3Schema, + } + + def get_data_type(self, data): + return _extract_tx_version(data.get("version")) + + class L1HandlerTransactionSchema(TransactionSchema): contract_address = Felt(data_key="contract_address", required=True) calldata = fields.List(Felt(), data_key="calldata", required=True) @@ -627,6 +755,9 @@ class FunctionInvocationSchema(Schema): messages = fields.List( fields.Nested(L2toL1MessageSchema()), data_key="messages", required=True ) + execution_resources = fields.Nested( + ExecutionResourcesSchema(), data_key="execution_resources", required=True + ) @post_load def make_dataclass(self, data, **kwargs) -> FunctionInvocation: diff --git a/starknet_py/net/schemas/utils.py b/starknet_py/net/schemas/utils.py index 3b4b7e94b..418c6cf91 100644 --- a/starknet_py/net/schemas/utils.py +++ b/starknet_py/net/schemas/utils.py @@ -1,3 +1,8 @@ +from typing import Union + +from starknet_py.constants import QUERY_VERSION_BASE + + def _replace_invoke_contract_address_with_sender_address(data: dict): data["sender_address"] = data.get("sender_address") or data.get("contract_address") @@ -7,3 +12,9 @@ def _replace_invoke_contract_address_with_sender_address(data: dict): ) del data["contract_address"] + + +def _extract_tx_version(version: Union[int, str]): + if isinstance(version, str): + version = int(version, 16) + return version % QUERY_VERSION_BASE diff --git a/starknet_py/net/signer/stark_curve_signer.py b/starknet_py/net/signer/stark_curve_signer.py index 5cd1ebc42..349cb848c 100644 --- a/starknet_py/net/signer/stark_curve_signer.py +++ b/starknet_py/net/signer/stark_curve_signer.py @@ -1,25 +1,10 @@ from dataclasses import dataclass -from typing import List, cast +from typing import List -from starknet_py.constants import DEFAULT_ENTRY_POINT_SELECTOR -from starknet_py.hash.address import compute_address -from starknet_py.hash.transaction import ( - TransactionHashPrefix, - compute_declare_transaction_hash, - compute_declare_v2_transaction_hash, - compute_deploy_account_transaction_hash, - compute_transaction_hash, -) from starknet_py.hash.utils import message_signature, private_to_stark_key from starknet_py.net.client_models import Hash from starknet_py.net.models import AddressRepresentation, StarknetChainId, parse_address -from starknet_py.net.models.transaction import ( - AccountTransaction, - Declare, - DeclareV2, - DeployAccount, - Invoke, -) +from starknet_py.net.models.transaction import AccountTransaction from starknet_py.net.signer.base_signer import BaseSigner from starknet_py.utils.typed_data import TypedData @@ -76,73 +61,7 @@ def sign_transaction( self, transaction: AccountTransaction, ) -> List[int]: - if isinstance(transaction, Declare): - return self._sign_declare_transaction(transaction) - if isinstance(transaction, DeclareV2): - return self._sign_declare_v2_transaction(transaction) - if isinstance(transaction, DeployAccount): - return self._sign_deploy_account_transaction(transaction) - return self._sign_transaction(cast(Invoke, transaction)) - - def _sign_transaction(self, transaction: Invoke): - tx_hash = compute_transaction_hash( - tx_hash_prefix=TransactionHashPrefix.INVOKE, - version=transaction.version, - contract_address=self.address, - entry_point_selector=DEFAULT_ENTRY_POINT_SELECTOR, - calldata=transaction.calldata, - max_fee=transaction.max_fee, - chain_id=self.chain_id, - additional_data=[transaction.nonce], - ) - # pylint: disable=invalid-name - r, s = message_signature(msg_hash=tx_hash, priv_key=self.private_key) - return [r, s] - - def _sign_declare_transaction(self, transaction: Declare) -> List[int]: - tx_hash = compute_declare_transaction_hash( - contract_class=transaction.contract_class, - chain_id=self.chain_id, - sender_address=self.address, - max_fee=transaction.max_fee, - version=transaction.version, - nonce=transaction.nonce, - ) - # pylint: disable=invalid-name - r, s = message_signature(msg_hash=tx_hash, priv_key=self.private_key) - return [r, s] - - def _sign_declare_v2_transaction(self, transaction: DeclareV2) -> List[int]: - tx_hash = compute_declare_v2_transaction_hash( - contract_class=transaction.contract_class, - compiled_class_hash=transaction.compiled_class_hash, - chain_id=self.chain_id, - sender_address=self.address, - max_fee=transaction.max_fee, - version=transaction.version, - nonce=transaction.nonce, - ) - # pylint: disable=invalid-name - r, s = message_signature(msg_hash=tx_hash, priv_key=self.private_key) - return [r, s] - - def _sign_deploy_account_transaction(self, transaction: DeployAccount) -> List[int]: - contract_address = compute_address( - salt=transaction.contract_address_salt, - class_hash=transaction.class_hash, - constructor_calldata=transaction.constructor_calldata, - deployer_address=0, - ) - tx_hash = compute_deploy_account_transaction_hash( - contract_address=contract_address, - class_hash=transaction.class_hash, - constructor_calldata=transaction.constructor_calldata, - salt=transaction.contract_address_salt, - max_fee=transaction.max_fee, - version=transaction.version, - chain_id=self.chain_id, - nonce=transaction.nonce, - ) + tx_hash = transaction.calculate_hash(self.chain_id) # pylint: disable=invalid-name r, s = message_signature(msg_hash=tx_hash, priv_key=self.private_key) return [r, s] diff --git a/starknet_py/net/signer/test_stark_curve_signer.py b/starknet_py/net/signer/test_stark_curve_signer.py index 4f5e7c4ca..62b23dc0a 100644 --- a/starknet_py/net/signer/test_stark_curve_signer.py +++ b/starknet_py/net/signer/test_stark_curve_signer.py @@ -2,7 +2,7 @@ from starknet_py.common import create_compiled_contract from starknet_py.net.models import StarknetChainId -from starknet_py.net.models.transaction import Declare, DeployAccount, Invoke +from starknet_py.net.models.transaction import DeclareV1, DeployAccountV1, InvokeV1 from starknet_py.net.signer.stark_curve_signer import KeyPair, StarkCurveSigner from starknet_py.tests.e2e.fixtures.constants import CONTRACTS_COMPILED_V0_DIR from starknet_py.tests.e2e.fixtures.misc import read_contract @@ -15,7 +15,7 @@ @pytest.mark.parametrize( "transaction", [ - Invoke( + InvokeV1( sender_address=0x1, calldata=[1, 2, 3], max_fee=10000, @@ -23,7 +23,7 @@ nonce=23, version=1, ), - DeployAccount( + DeployAccountV1( class_hash=0x1, contract_address_salt=0x2, constructor_calldata=[1, 2, 3, 4], @@ -32,7 +32,7 @@ nonce=23, version=1, ), - Declare( + DeclareV1( contract_class=create_compiled_contract( compiled_contract=compiled_contract ), diff --git a/starknet_py/tests/e2e/account/account_test.py b/starknet_py/tests/e2e/account/account_test.py index 5d55c4e85..bff1e011e 100644 --- a/starknet_py/tests/e2e/account/account_test.py +++ b/starknet_py/tests/e2e/account/account_test.py @@ -14,16 +14,29 @@ DeployAccountTransaction, DeployAccountTransactionResponse, EstimatedFee, + InvokeTransactionV3, + ResourceBounds, + ResourceBoundsMapping, SierraContractClass, TransactionExecutionStatus, TransactionFinalityStatus, ) from starknet_py.net.full_node_client import FullNodeClient from starknet_py.net.models import StarknetChainId -from starknet_py.net.models.transaction import Declare, DeclareV2 +from starknet_py.net.models.transaction import ( + DeclareV1, + DeclareV2, + DeclareV3, + DeployAccountV3, + InvokeV3, +) from starknet_py.net.signer.stark_curve_signer import KeyPair from starknet_py.net.udc_deployer.deployer import Deployer -from starknet_py.tests.e2e.fixtures.constants import MAX_FEE +from starknet_py.tests.e2e.fixtures.constants import ( + MAX_FEE, + MAX_RESOURCE_BOUNDS, + MAX_RESOURCE_BOUNDS_L1, +) @pytest.mark.run_on_devnet @@ -63,13 +76,14 @@ async def test_estimated_fee_greater_than_zero(account, erc20_contract): assert estimated_fee.overall_fee > 0 assert ( - estimated_fee.gas_price * estimated_fee.gas_usage == estimated_fee.overall_fee + estimated_fee.gas_price * estimated_fee.gas_consumed + == estimated_fee.overall_fee ) @pytest.mark.asyncio async def test_estimate_fee_for_declare_transaction(account, map_compiled_contract): - declare_tx = await account.sign_declare_transaction( + declare_tx = await account.sign_declare_v1_transaction( compiled_contract=map_compiled_contract, max_fee=MAX_FEE ) @@ -78,7 +92,8 @@ async def test_estimate_fee_for_declare_transaction(account, map_compiled_contra assert isinstance(estimated_fee.overall_fee, int) assert estimated_fee.overall_fee > 0 assert ( - estimated_fee.gas_usage * estimated_fee.gas_price == estimated_fee.overall_fee + estimated_fee.gas_consumed * estimated_fee.gas_price + == estimated_fee.overall_fee ) @@ -160,8 +175,8 @@ async def test_get_nonce(account, map_contract): @pytest.mark.parametrize( "calls", [[Call(10, 20, [30])], [Call(10, 20, [30]), Call(40, 50, [60])]] ) -async def test_sign_invoke_transaction(account, calls): - signed_tx = await account.sign_invoke_transaction(calls, max_fee=MAX_FEE) +async def test_sign_invoke_v1_transaction(account, calls): + signed_tx = await account.sign_invoke_v1_transaction(calls, max_fee=MAX_FEE) assert isinstance(signed_tx.signature, list) assert len(signed_tx.signature) > 0 @@ -169,8 +184,8 @@ async def test_sign_invoke_transaction(account, calls): @pytest.mark.asyncio -async def test_sign_invoke_transaction_auto_estimate(account, map_contract): - signed_tx = await account.sign_invoke_transaction( +async def test_sign_invoke_v1_transaction_auto_estimate(account, map_contract): + signed_tx = await account.sign_invoke_v1_transaction( Call(map_contract.address, get_selector_from_name("put"), [3, 4]), auto_estimate=True, ) @@ -180,13 +195,48 @@ async def test_sign_invoke_transaction_auto_estimate(account, map_contract): assert signed_tx.max_fee > 0 +@pytest.mark.asyncio +@pytest.mark.parametrize( + "calls", [[Call(10, 20, [30])], [Call(10, 20, [30]), Call(40, 50, [60])]] +) +async def test_sign_invoke_v3_transaction(account, calls): + signed_tx = await account.sign_invoke_v3_transaction( + calls, l1_resource_bounds=MAX_RESOURCE_BOUNDS_L1 + ) + + assert isinstance(signed_tx, InvokeV3) + assert isinstance(signed_tx.signature, list) + assert len(signed_tx.signature) == 2 + assert signed_tx.resource_bounds == MAX_RESOURCE_BOUNDS + assert signed_tx.version == 3 + + +@pytest.mark.asyncio +async def test_sign_invoke_v3_transaction_auto_estimate(account, map_contract): + signed_tx = await account.sign_invoke_v3_transaction( + Call(map_contract.address, get_selector_from_name("put"), [3, 4]), + auto_estimate=True, + ) + + assert isinstance(signed_tx, InvokeV3) + assert signed_tx.version == 3 + + assert isinstance(signed_tx.signature, list) + assert len(signed_tx.signature) == 2 + + assert isinstance(signed_tx.resource_bounds, ResourceBoundsMapping) + assert signed_tx.resource_bounds.l1_gas.max_amount > 0 + assert signed_tx.resource_bounds.l1_gas.max_price_per_unit > 0 + assert signed_tx.resource_bounds.l2_gas == ResourceBounds.init_with_zeros() + + @pytest.mark.asyncio async def test_sign_declare_transaction(account, map_compiled_contract): - signed_tx = await account.sign_declare_transaction( + signed_tx = await account.sign_declare_v1_transaction( map_compiled_contract, max_fee=MAX_FEE ) - assert isinstance(signed_tx, Declare) + assert isinstance(signed_tx, DeclareV1) assert signed_tx.version == 1 assert isinstance(signed_tx.signature, list) assert len(signed_tx.signature) > 0 @@ -195,11 +245,11 @@ async def test_sign_declare_transaction(account, map_compiled_contract): @pytest.mark.asyncio async def test_sign_declare_transaction_auto_estimate(account, map_compiled_contract): - signed_tx = await account.sign_declare_transaction( + signed_tx = await account.sign_declare_v1_transaction( map_compiled_contract, auto_estimate=True ) - assert isinstance(signed_tx, Declare) + assert isinstance(signed_tx, DeclareV1) assert signed_tx.version == 1 assert isinstance(signed_tx.signature, list) assert len(signed_tx.signature) > 0 @@ -250,6 +300,55 @@ async def test_sign_declare_v2_transaction_auto_estimate( assert signed_tx.max_fee > 0 +@pytest.mark.asyncio +async def test_sign_declare_v3_transaction( + account, sierra_minimal_compiled_contract_and_class_hash +): + ( + compiled_contract, + compiled_class_hash, + ) = sierra_minimal_compiled_contract_and_class_hash + + signed_tx = await account.sign_declare_v3_transaction( + compiled_contract, + compiled_class_hash, + l1_resource_bounds=MAX_RESOURCE_BOUNDS_L1, + ) + + assert isinstance(signed_tx, DeclareV3) + assert signed_tx.version == 3 + assert isinstance(signed_tx.signature, list) + assert len(signed_tx.signature) == 2 + assert signed_tx.nonce is not None + assert signed_tx.resource_bounds == MAX_RESOURCE_BOUNDS + assert signed_tx.version == 3 + + +@pytest.mark.asyncio +async def test_sign_declare_v3_transaction_auto_estimate( + account, sierra_minimal_compiled_contract_and_class_hash +): + ( + compiled_contract, + compiled_class_hash, + ) = sierra_minimal_compiled_contract_and_class_hash + + signed_tx = await account.sign_declare_v3_transaction( + compiled_contract, compiled_class_hash, auto_estimate=True + ) + + assert isinstance(signed_tx, DeclareV3) + assert signed_tx.version == 3 + + assert isinstance(signed_tx.signature, list) + assert len(signed_tx.signature) == 2 + + assert isinstance(signed_tx.resource_bounds, ResourceBoundsMapping) + assert signed_tx.resource_bounds.l1_gas.max_amount > 0 + assert signed_tx.resource_bounds.l1_gas.max_price_per_unit > 0 + assert signed_tx.resource_bounds.l2_gas == ResourceBounds.init_with_zeros() + + @pytest.mark.asyncio async def test_declare_contract_raises_on_sierra_contract_without_compiled_class_hash( account, sierra_minimal_compiled_contract_and_class_hash @@ -259,7 +358,7 @@ async def test_declare_contract_raises_on_sierra_contract_without_compiled_class ValueError, match="Signing sierra contracts requires using `sign_declare_v2_transaction` method.", ): - await account.sign_declare_transaction(compiled_contract=compiled_contract) + await account.sign_declare_v1_transaction(compiled_contract=compiled_contract) @pytest.mark.asyncio @@ -267,7 +366,7 @@ async def test_sign_deploy_account_transaction(account): class_hash = 0x1234 salt = 0x123 calldata = [1, 2, 3] - signed_tx = await account.sign_deploy_account_transaction( + signed_tx = await account.sign_deploy_account_v1_transaction( class_hash, salt, calldata, max_fee=MAX_FEE ) @@ -286,7 +385,7 @@ async def test_sign_deploy_account_transaction_auto_estimate( class_hash = account_with_validate_deploy_class_hash salt = 0x1234 calldata = [account.signer.public_key] - signed_tx = await account.sign_deploy_account_transaction( + signed_tx = await account.sign_deploy_account_v1_transaction( class_hash, salt, calldata, auto_estimate=True ) @@ -298,6 +397,53 @@ async def test_sign_deploy_account_transaction_auto_estimate( assert signed_tx.constructor_calldata == calldata +@pytest.mark.asyncio +async def test_sign_deploy_account_v3_transaction(account): + class_hash = 0x1234 + salt = 0x123 + calldata = [1, 2, 3] + signed_tx = await account.sign_deploy_account_v3_transaction( + class_hash, + salt, + l1_resource_bounds=MAX_RESOURCE_BOUNDS_L1, + constructor_calldata=calldata, + ) + + assert isinstance(signed_tx, DeployAccountV3) + assert signed_tx.version == 3 + + assert isinstance(signed_tx.signature, list) + assert len(signed_tx.signature) == 2 + + assert signed_tx.resource_bounds == MAX_RESOURCE_BOUNDS + assert signed_tx.class_hash == class_hash + assert signed_tx.contract_address_salt == salt + assert signed_tx.constructor_calldata == calldata + + +@pytest.mark.asyncio +async def test_sign_deploy_account_v3_transaction_auto_estimate( + account, account_with_validate_deploy_class_hash +): + class_hash = account_with_validate_deploy_class_hash + salt = 0x123 + calldata = [account.signer.public_key] + signed_tx = await account.sign_deploy_account_v3_transaction( + class_hash, salt, constructor_calldata=calldata, auto_estimate=True + ) + + assert isinstance(signed_tx, DeployAccountV3) + assert signed_tx.version == 3 + + assert isinstance(signed_tx.signature, list) + assert len(signed_tx.signature) == 2 + + assert isinstance(signed_tx.resource_bounds, ResourceBoundsMapping) + assert signed_tx.resource_bounds.l1_gas.max_amount > 0 + assert signed_tx.resource_bounds.l1_gas.max_price_per_unit > 0 + assert signed_tx.resource_bounds.l2_gas == ResourceBounds.init_with_zeros() + + @pytest.mark.asyncio async def test_deploy_account(client, deploy_account_details_factory, map_contract): address, key_pair, salt, class_hash = await deploy_account_details_factory.get() @@ -458,7 +604,7 @@ async def test_sign_deploy_account_tx_for_fee_estimation( chain=StarknetChainId.TESTNET, ) - transaction = await account.sign_deploy_account_transaction( + transaction = await account.sign_deploy_account_v1_transaction( class_hash=class_hash, contract_address_salt=salt, constructor_calldata=[key_pair.public_key], @@ -483,10 +629,12 @@ async def test_sign_deploy_account_tx_for_fee_estimation( @pytest.mark.asyncio async def test_sign_transaction_custom_nonce(account, cairo1_hello_starknet_class_hash): deployment = Deployer().create_contract_deployment(cairo1_hello_starknet_class_hash) - deploy_tx = await account.sign_invoke_transaction(deployment.call, max_fee=MAX_FEE) + deploy_tx = await account.sign_invoke_v1_transaction( + deployment.call, max_fee=MAX_FEE + ) new_balance = 30 - invoke_tx = await account.sign_invoke_transaction( + invoke_tx = await account.sign_invoke_v1_transaction( Call( deployment.address, get_selector_from_name("increase_balance"), @@ -587,3 +735,38 @@ async def test_argent_cairo1_account_execute( ) assert get_balance[0] == value + + +@pytest.mark.asyncio +async def test_account_execute_v3(account, deployed_balance_contract): + get_balance_call = Call( + to_addr=deployed_balance_contract.address, + selector=get_selector_from_name("get_balance"), + calldata=[], + ) + increase_balance_call = Call( + to_addr=deployed_balance_contract.address, + selector=get_selector_from_name("increase_balance"), + calldata=[100], + ) + + (initial_balance,) = await account.client.call_contract(call=get_balance_call) + + execute_increase_balance = await account.execute_v3( + calls=increase_balance_call, l1_resource_bounds=MAX_RESOURCE_BOUNDS_L1 + ) + receipt = await account.client.wait_for_tx( + tx_hash=execute_increase_balance.transaction_hash + ) + + assert receipt.execution_status == TransactionExecutionStatus.SUCCEEDED + + tx_details = await account.client.get_transaction( + tx_hash=execute_increase_balance.transaction_hash + ) + assert isinstance(tx_details, InvokeTransactionV3) + + (balance_after_increase,) = await account.client.call_contract( + call=get_balance_call + ) + assert initial_balance + 100 == balance_after_increase diff --git a/starknet_py/tests/e2e/client/client_test.py b/starknet_py/tests/e2e/client/client_test.py index 646ab8c91..2de408849 100644 --- a/starknet_py/tests/e2e/client/client_test.py +++ b/starknet_py/tests/e2e/client/client_test.py @@ -15,10 +15,14 @@ DeclaredContractHash, DeclareTransaction, DeployAccountTransaction, + EstimatedFee, ExecutionResources, + FeePayment, InvokeTransaction, L1HandlerTransaction, PendingBlockStateUpdate, + PriceUnit, + ResourceBounds, SierraContractClass, SierraEntryPointsByType, TransactionExecutionStatus, @@ -133,7 +137,7 @@ async def test_get_transaction_receipt( @pytest.mark.asyncio async def test_estimate_fee_invoke(account, contract_address): - invoke_tx = await account.sign_invoke_transaction( + invoke_tx = await account.sign_invoke_v1_transaction( calls=Call( to_addr=contract_address, selector=get_selector_from_name("increase_balance"), @@ -144,13 +148,36 @@ async def test_estimate_fee_invoke(account, contract_address): invoke_tx = await account.sign_for_fee_estimate(invoke_tx) estimate_fee = await account.client.estimate_fee(tx=invoke_tx) - assert isinstance(estimate_fee.overall_fee, int) + assert isinstance(estimate_fee, EstimatedFee) + assert estimate_fee.unit == PriceUnit.WEI assert estimate_fee.overall_fee > 0 + assert estimate_fee.gas_price > 0 + assert estimate_fee.gas_consumed > 0 + + +@pytest.mark.asyncio +async def test_estimate_fee_invoke_v3(account, contract_address): + invoke_tx = await account.sign_invoke_v3_transaction( + calls=Call( + to_addr=contract_address, + selector=get_selector_from_name("increase_balance"), + calldata=[123], + ), + l1_resource_bounds=ResourceBounds.init_with_zeros(), + ) + invoke_tx = await account.sign_for_fee_estimate(invoke_tx) + estimate_fee = await account.client.estimate_fee(tx=invoke_tx) + + assert isinstance(estimate_fee, EstimatedFee) + assert estimate_fee.unit == PriceUnit.FRI + assert estimate_fee.overall_fee > 0 + assert estimate_fee.gas_price > 0 + assert estimate_fee.gas_consumed > 0 @pytest.mark.asyncio async def test_estimate_fee_declare(account): - declare_tx = await account.sign_declare_transaction( + declare_tx = await account.sign_declare_v1_transaction( compiled_contract=read_contract( "map_compiled.json", directory=CONTRACTS_COMPILED_V0_DIR ), @@ -159,23 +186,29 @@ async def test_estimate_fee_declare(account): declare_tx = await account.sign_for_fee_estimate(declare_tx) estimate_fee = await account.client.estimate_fee(tx=declare_tx) - assert isinstance(estimate_fee.overall_fee, int) + assert isinstance(estimate_fee, EstimatedFee) + assert estimate_fee.unit == PriceUnit.WEI assert estimate_fee.overall_fee > 0 + assert estimate_fee.gas_price > 0 + assert estimate_fee.gas_consumed > 0 @pytest.mark.asyncio async def test_estimate_fee_deploy_account(client, deploy_account_transaction): estimate_fee = await client.estimate_fee(tx=deploy_account_transaction) - assert isinstance(estimate_fee.overall_fee, int) + assert isinstance(estimate_fee, EstimatedFee) + assert estimate_fee.unit == PriceUnit.WEI assert estimate_fee.overall_fee > 0 + assert estimate_fee.gas_price > 0 + assert estimate_fee.gas_consumed > 0 @pytest.mark.asyncio async def test_estimate_fee_for_multiple_transactions( client, deploy_account_transaction, contract_address, account ): - invoke_tx = await account.sign_invoke_transaction( + invoke_tx = await account.sign_invoke_v1_transaction( calls=Call( to_addr=contract_address, selector=get_selector_from_name("increase_balance"), @@ -185,7 +218,7 @@ async def test_estimate_fee_for_multiple_transactions( ) invoke_tx = await account.sign_for_fee_estimate(invoke_tx) - declare_tx = await account.sign_declare_transaction( + declare_tx = await account.sign_declare_v1_transaction( compiled_contract=read_contract( "map_compiled.json", directory=CONTRACTS_COMPILED_V0_DIR ), @@ -201,8 +234,11 @@ async def test_estimate_fee_for_multiple_transactions( assert isinstance(estimated_fees, list) for estimated_fee in estimated_fees: - assert isinstance(estimated_fee.overall_fee, int) + assert isinstance(estimated_fee, EstimatedFee) + assert estimated_fee.unit == PriceUnit.WEI assert estimated_fee.overall_fee > 0 + assert estimated_fee.gas_price > 0 + assert estimated_fee.gas_consumed > 0 @pytest.mark.asyncio @@ -221,7 +257,7 @@ async def test_call_contract(client, contract_address): @pytest.mark.asyncio async def test_add_transaction(map_contract, client, account): prepared_function_call = map_contract.functions["put"].prepare(key=73, value=12) - signed_invoke = await account.sign_invoke_transaction( + signed_invoke = await account.sign_invoke_v1_transaction( calls=prepared_function_call, max_fee=MAX_FEE ) @@ -264,6 +300,7 @@ async def test_wait_for_tx_accepted(client, get_tx_receipt_path, get_tx_status_p execution_status=TransactionExecutionStatus.SUCCEEDED, finality_status=TransactionFinalityStatus.ACCEPTED_ON_L2, execution_resources=Mock(spec=ExecutionResources), + actual_fee=FeePayment(amount=1, unit=PriceUnit.WEI), ) mocked_status.return_value = TransactionStatusResponse( @@ -290,6 +327,7 @@ async def test_wait_for_tx_reverted(client, get_tx_receipt_path, get_tx_status_p finality_status=Mock(spec=TransactionFinalityStatus), execution_resources=Mock(spec=ExecutionResources), revert_reason=exc_message, + actual_fee=FeePayment(amount=1, unit=PriceUnit.WEI), ) mocked_status.return_value = TransactionStatusResponse( @@ -332,7 +370,7 @@ async def test_wait_for_tx_unknown_error( @pytest.mark.asyncio async def test_declare_contract(account, map_compiled_contract): - declare_tx = await account.sign_declare_transaction( + declare_tx = await account.sign_declare_v1_transaction( compiled_contract=map_compiled_contract, max_fee=MAX_FEE ) @@ -343,7 +381,7 @@ async def test_declare_contract(account, map_compiled_contract): assert transaction_receipt.execution_status == TransactionExecutionStatus.SUCCEEDED assert transaction_receipt.transaction_hash - assert 0 < transaction_receipt.actual_fee <= MAX_FEE + assert 0 < transaction_receipt.actual_fee.amount <= MAX_FEE assert transaction_receipt.type == TransactionType.DECLARE @@ -460,7 +498,7 @@ async def test_state_update_deployed_contracts( # setup deployer = Deployer() contract_deployment = deployer.create_contract_deployment(class_hash=class_hash) - deploy_invoke_tx = await account.sign_invoke_transaction( + deploy_invoke_tx = await account.sign_invoke_v1_transaction( contract_deployment.call, max_fee=MAX_FEE ) resp = await account.client.send_transaction(deploy_invoke_tx) diff --git a/starknet_py/tests/e2e/client/fixtures/transactions.py b/starknet_py/tests/e2e/client/fixtures/transactions.py index c859a5833..c367acd38 100644 --- a/starknet_py/tests/e2e/client/fixtures/transactions.py +++ b/starknet_py/tests/e2e/client/fixtures/transactions.py @@ -7,7 +7,7 @@ from starknet_py.contract import Contract from starknet_py.net.account.account import Account from starknet_py.net.full_node_client import FullNodeClient -from starknet_py.net.models.transaction import DeployAccount +from starknet_py.net.models.transaction import DeployAccountV1 from starknet_py.net.udc_deployer.deployer import Deployer from starknet_py.tests.e2e.client.fixtures.prepare_net_for_gateway_test import ( PreparedNetworkData, @@ -22,13 +22,18 @@ @pytest_asyncio.fixture(scope="package") async def deploy_account_transaction( - account_with_validate_deploy_class_hash: int, fee_contract: Contract, network: str -) -> DeployAccount: + account_with_validate_deploy_class_hash: int, + eth_fee_contract: Contract, + strk_fee_contract: Contract, + network: str, +) -> DeployAccountV1: """ Returns a DeployAccount transaction """ address, key_pair, salt, class_hash = await get_deploy_account_details( - class_hash=account_with_validate_deploy_class_hash, fee_contract=fee_contract + class_hash=account_with_validate_deploy_class_hash, + eth_fee_contract=eth_fee_contract, + strk_fee_contract=strk_fee_contract, ) return await get_deploy_account_transaction( address=address, @@ -69,7 +74,7 @@ async def hello_starknet_deploy_transaction_address( contract_deployment = deployer.create_contract_deployment_raw( class_hash=cairo1_hello_starknet_class_hash ) - deploy_invoke_transaction = await account.sign_invoke_transaction( + deploy_invoke_transaction = await account.sign_invoke_v1_transaction( calls=contract_deployment.call, max_fee=MAX_FEE ) resp = await account.client.send_transaction(deploy_invoke_transaction) diff --git a/starknet_py/tests/e2e/client/full_node_test.py b/starknet_py/tests/e2e/client/full_node_test.py index fd36e8365..3446b5eed 100644 --- a/starknet_py/tests/e2e/client/full_node_test.py +++ b/starknet_py/tests/e2e/client/full_node_test.py @@ -408,7 +408,7 @@ async def test_simulate_transactions_skip_validate(account, deployed_balance_con selector=get_selector_from_name("increase_balance"), calldata=[0x10], ) - invoke_tx = await account.sign_invoke_transaction(calls=call, auto_estimate=True) + invoke_tx = await account.sign_invoke_v1_transaction(calls=call, auto_estimate=True) invoke_tx = dataclasses.replace(invoke_tx, signature=[]) simulated_txs = await account.client.simulate_transactions( @@ -432,7 +432,7 @@ async def test_simulate_transactions_skip_fee_charge( selector=get_selector_from_name("increase_balance"), calldata=[0x10], ) - invoke_tx = await account.sign_invoke_transaction(calls=call, auto_estimate=True) + invoke_tx = await account.sign_invoke_v1_transaction(calls=call, auto_estimate=True) simulated_txs = await account.client.simulate_transactions( transactions=[invoke_tx], skip_fee_charge=True, block_number="latest" @@ -448,7 +448,7 @@ async def test_simulate_transactions_invoke(account, deployed_balance_contract): selector=get_selector_from_name("increase_balance"), calldata=[0x10], ) - invoke_tx = await account.sign_invoke_transaction(calls=call, auto_estimate=True) + invoke_tx = await account.sign_invoke_v1_transaction(calls=call, auto_estimate=True) simulated_txs = await account.client.simulate_transactions( transactions=[invoke_tx], block_number="latest" ) @@ -457,7 +457,7 @@ async def test_simulate_transactions_invoke(account, deployed_balance_contract): assert isinstance(simulated_txs[0].transaction_trace, InvokeTransactionTrace) assert simulated_txs[0].transaction_trace.execute_invocation is not None - invoke_tx = await account.sign_invoke_transaction( + invoke_tx = await account.sign_invoke_v1_transaction( calls=[call, call], auto_estimate=True ) simulated_txs = await account.client.simulate_transactions( @@ -474,7 +474,7 @@ async def test_simulate_transactions_declare(account): compiled_contract = read_contract( "map_compiled.json", directory=CONTRACTS_COMPILED_V0_DIR ) - declare_tx = await account.sign_declare_transaction( + declare_tx = await account.sign_declare_v1_transaction( compiled_contract, max_fee=int(1e16) ) @@ -495,7 +495,7 @@ async def test_simulate_transactions_two_txs(account, deployed_balance_contract) selector=get_selector_from_name("increase_balance"), calldata=[0x10], ) - invoke_tx = await account.sign_invoke_transaction(calls=call, auto_estimate=True) + invoke_tx = await account.sign_invoke_v1_transaction(calls=call, auto_estimate=True) compiled_v2_contract = read_contract( "test_contract_declare_compiled.json", directory=CONTRACTS_COMPILED_V1_DIR @@ -545,7 +545,7 @@ async def test_simulate_transactions_deploy_account( key_pair=key_pair, chain=StarknetChainId.TESTNET, ) - deploy_account_tx = await account.sign_deploy_account_transaction( + deploy_account_tx = await account.sign_deploy_account_v1_transaction( class_hash=class_hash, contract_address_salt=salt, constructor_calldata=[key_pair.public_key], diff --git a/starknet_py/tests/e2e/declare/declare_test.py b/starknet_py/tests/e2e/declare/declare_test.py index aef5f1b5a..048aa049d 100644 --- a/starknet_py/tests/e2e/declare/declare_test.py +++ b/starknet_py/tests/e2e/declare/declare_test.py @@ -1,11 +1,13 @@ import pytest -from starknet_py.tests.e2e.fixtures.constants import MAX_FEE +from starknet_py.net.client_models import TransactionExecutionStatus +from starknet_py.net.models.transaction import DeclareV3 +from starknet_py.tests.e2e.fixtures.constants import MAX_FEE, MAX_RESOURCE_BOUNDS_L1 @pytest.mark.asyncio async def test_declare_tx(account, map_compiled_contract): - declare_tx = await account.sign_declare_transaction( + declare_tx = await account.sign_declare_v1_transaction( compiled_contract=map_compiled_contract, max_fee=MAX_FEE ) result = await account.client.declare(declare_tx) @@ -17,3 +19,18 @@ async def test_declare_tx(account, map_compiled_contract): async def test_declare_v2_tx(cairo1_minimal_contract_class_hash: int): assert isinstance(cairo1_minimal_contract_class_hash, int) assert cairo1_minimal_contract_class_hash != 0 + + +@pytest.mark.asyncio +async def test_declare_v3_tx(account, abi_types_compiled_contract_and_class_hash): + declare_tx = await account.sign_declare_v3_transaction( + compiled_contract=abi_types_compiled_contract_and_class_hash[0], + compiled_class_hash=abi_types_compiled_contract_and_class_hash[1], + l1_resource_bounds=MAX_RESOURCE_BOUNDS_L1, + ) + assert isinstance(declare_tx, DeclareV3) + + result = await account.client.declare(declare_tx) + tx_receipt = await account.client.wait_for_tx(tx_hash=result.transaction_hash) + + assert tx_receipt.execution_status == TransactionExecutionStatus.SUCCEEDED diff --git a/starknet_py/tests/e2e/deploy/deployer_test.py b/starknet_py/tests/e2e/deploy/deployer_test.py index 52cac4c66..51db149d4 100644 --- a/starknet_py/tests/e2e/deploy/deployer_test.py +++ b/starknet_py/tests/e2e/deploy/deployer_test.py @@ -14,7 +14,7 @@ async def test_default_deploy_with_class_hash(account, map_class_hash): contract_deployment = deployer.create_contract_deployment(class_hash=map_class_hash) - deploy_invoke_tx = await account.sign_invoke_transaction( + deploy_invoke_tx = await account.sign_invoke_v1_transaction( contract_deployment.call, max_fee=MAX_FEE ) resp = await account.client.send_transaction(deploy_invoke_tx) @@ -74,7 +74,7 @@ async def test_constructor_arguments_contract_deploy( calldata=calldata, ) - deploy_invoke_transaction = await account.sign_invoke_transaction( + deploy_invoke_transaction = await account.sign_invoke_v1_transaction( deploy_call, max_fee=MAX_FEE ) resp = await account.client.send_transaction(deploy_invoke_transaction) @@ -113,7 +113,7 @@ async def test_address_computation(salt, pass_account_address, account, map_clas salt=salt, ) - deploy_invoke_tx = await account.sign_invoke_transaction( + deploy_invoke_tx = await account.sign_invoke_v1_transaction( deploy_call, max_fee=MAX_FEE ) resp = await account.client.send_transaction(deploy_invoke_tx) @@ -158,7 +158,7 @@ async def test_create_deployment_call_raw( raw_calldata=raw_calldata, ) - deploy_invoke_transaction = await account.sign_invoke_transaction( + deploy_invoke_transaction = await account.sign_invoke_v1_transaction( deploy_call, max_fee=MAX_FEE ) resp = await account.client.send_transaction(deploy_invoke_transaction) @@ -202,7 +202,7 @@ async def test_create_deployment_call_raw_supports_seed_0( salt=0, ) - deploy_invoke_transaction = await account.sign_invoke_transaction( + deploy_invoke_transaction = await account.sign_invoke_v1_transaction( deploy_call, max_fee=MAX_FEE ) resp = await account.client.send_transaction(deploy_invoke_transaction) diff --git a/starknet_py/tests/e2e/deploy_account/deploy_account_test.py b/starknet_py/tests/e2e/deploy_account/deploy_account_test.py index cd637fdeb..cf47516e0 100644 --- a/starknet_py/tests/e2e/deploy_account/deploy_account_test.py +++ b/starknet_py/tests/e2e/deploy_account/deploy_account_test.py @@ -2,7 +2,8 @@ from starknet_py.net.account.account import Account from starknet_py.net.models import StarknetChainId -from starknet_py.tests.e2e.fixtures.constants import MAX_FEE +from starknet_py.net.models.transaction import DeployAccountV3 +from starknet_py.tests.e2e.fixtures.constants import MAX_FEE, MAX_RESOURCE_BOUNDS_L1 @pytest.mark.asyncio @@ -16,7 +17,7 @@ async def test_general_flow(client, deploy_account_details_factory): chain=StarknetChainId.TESTNET, ) - deploy_account_tx = await account.sign_deploy_account_transaction( + deploy_account_tx = await account.sign_deploy_account_v1_transaction( class_hash=class_hash, contract_address_salt=salt, constructor_calldata=[key_pair.public_key], @@ -25,3 +26,28 @@ async def test_general_flow(client, deploy_account_details_factory): resp = await account.client.deploy_account(transaction=deploy_account_tx) assert resp.address == address + + +@pytest.mark.asyncio +async def test_deploy_account_v3(client, deploy_account_details_factory): + address, key_pair, salt, class_hash = await deploy_account_details_factory.get() + + account = Account( + address=address, + client=client, + key_pair=key_pair, + chain=StarknetChainId.TESTNET, + ) + + deploy_account_tx = await account.sign_deploy_account_v3_transaction( + class_hash=class_hash, + contract_address_salt=salt, + constructor_calldata=[key_pair.public_key], + l1_resource_bounds=MAX_RESOURCE_BOUNDS_L1, + ) + + assert isinstance(deploy_account_tx, DeployAccountV3) + + resp = await account.client.deploy_account(transaction=deploy_account_tx) + + assert resp.address == address diff --git a/starknet_py/tests/e2e/docs/account_creation/test_deploy_prefunded_account.py b/starknet_py/tests/e2e/docs/account_creation/test_deploy_prefunded_account.py index 7195c57ab..d95848be6 100644 --- a/starknet_py/tests/e2e/docs/account_creation/test_deploy_prefunded_account.py +++ b/starknet_py/tests/e2e/docs/account_creation/test_deploy_prefunded_account.py @@ -11,7 +11,7 @@ async def test_deploy_prefunded_account( account_with_validate_deploy_class_hash: int, network: str, - fee_contract: Contract, + eth_fee_contract: Contract, client: Client, ): # pylint: disable=import-outside-toplevel, too-many-locals @@ -43,7 +43,7 @@ async def test_deploy_prefunded_account( # Prefund the address (using the token bridge or by sending fee tokens to the computed address) # Make sure the tx has been accepted on L2 before proceeding # docs: end - res = await fee_contract.functions["transfer"].invoke( + res = await eth_fee_contract.functions["transfer"].invoke( recipient=address, amount=int(1e16), max_fee=MAX_FEE ) await res.wait_for_acceptance() diff --git a/starknet_py/tests/e2e/docs/code_examples/test_full_node_client.py b/starknet_py/tests/e2e/docs/code_examples/test_full_node_client.py index c8029ce0d..2a6dd53db 100644 --- a/starknet_py/tests/e2e/docs/code_examples/test_full_node_client.py +++ b/starknet_py/tests/e2e/docs/code_examples/test_full_node_client.py @@ -233,7 +233,7 @@ async def test_simulate_transactions( selector=get_selector_from_name("method_name"), calldata=[0xCA11DA7A], ) - first_transaction = await account.sign_invoke_transaction( + first_transaction = await account.sign_invoke_v1_transaction( calls=call, max_fee=int(1e16) ) # docs-end: simulate_transactions @@ -243,7 +243,7 @@ async def test_simulate_transactions( selector=get_selector_from_name("increase_balance"), calldata=[0x10], ) - first_transaction = await account.sign_invoke_transaction( + first_transaction = await account.sign_invoke_v1_transaction( calls=call, auto_estimate=True ) diff --git a/starknet_py/tests/e2e/docs/guide/test_account_sign_without_execute.py b/starknet_py/tests/e2e/docs/guide/test_account_sign_without_execute.py index 6311bca9e..db3517399 100644 --- a/starknet_py/tests/e2e/docs/guide/test_account_sign_without_execute.py +++ b/starknet_py/tests/e2e/docs/guide/test_account_sign_without_execute.py @@ -1,7 +1,7 @@ import pytest from starknet_py.net.account.account import Account -from starknet_py.net.models.transaction import Declare, DeployAccount, Invoke +from starknet_py.net.models.transaction import DeclareV1, DeployAccountV1, InvokeV1 @pytest.mark.asyncio @@ -18,15 +18,15 @@ async def test_account_sign_without_execute(account, map_compiled_contract): # Create a signed Invoke transaction call = Call(to_addr=address, selector=selector, calldata=calldata) - invoke_transaction = await account.sign_invoke_transaction(call, max_fee=max_fee) + invoke_transaction = await account.sign_invoke_v1_transaction(call, max_fee=max_fee) # Create a signed Declare transaction - declare_transaction = await account.sign_declare_transaction( + declare_transaction = await account.sign_declare_v1_transaction( compiled_contract=compiled_contract, max_fee=max_fee ) # Create a signed DeployAccount transaction - deploy_account_transaction = await account.sign_deploy_account_transaction( + deploy_account_transaction = await account.sign_deploy_account_v1_transaction( class_hash=class_hash, contract_address_salt=salt, constructor_calldata=calldata, @@ -34,6 +34,6 @@ async def test_account_sign_without_execute(account, map_compiled_contract): ) # docs: end - assert isinstance(invoke_transaction, Invoke) - assert isinstance(declare_transaction, Declare) - assert isinstance(deploy_account_transaction, DeployAccount) + assert isinstance(invoke_transaction, InvokeV1) + assert isinstance(declare_transaction, DeclareV1) + assert isinstance(deploy_account_transaction, DeployAccountV1) diff --git a/starknet_py/tests/e2e/docs/guide/test_cairo1_contract.py b/starknet_py/tests/e2e/docs/guide/test_cairo1_contract.py index 59b1da49d..79fc063b5 100644 --- a/starknet_py/tests/e2e/docs/guide/test_cairo1_contract.py +++ b/starknet_py/tests/e2e/docs/guide/test_cairo1_contract.py @@ -38,7 +38,7 @@ async def test_cairo1_contract( # docs: start - # Create Declare v2 transaction + # Create Declare v2 transaction (to create Declare v3 transaction use `sign_declare_v3_transaction` method) declare_v2_transaction = await account.sign_declare_v2_transaction( # compiled_contract is a string containing the content of the starknet-compile (.json file) compiled_contract=compiled_contract, @@ -46,7 +46,7 @@ async def test_cairo1_contract( max_fee=MAX_FEE, ) - # Send Declare v2 transaction + # Send transaction resp = await account.client.declare(transaction=declare_v2_transaction) await account.client.wait_for_tx(resp.transaction_hash) diff --git a/starknet_py/tests/e2e/docs/guide/test_contract_account_compatibility.py b/starknet_py/tests/e2e/docs/guide/test_contract_account_compatibility.py index e9911b387..53535096c 100644 --- a/starknet_py/tests/e2e/docs/guide/test_contract_account_compatibility.py +++ b/starknet_py/tests/e2e/docs/guide/test_contract_account_compatibility.py @@ -1,6 +1,6 @@ import pytest -from starknet_py.net.models import Invoke +from starknet_py.net.models import InvokeV1 @pytest.mark.asyncio @@ -17,7 +17,7 @@ async def test_create_invoke_from_contract(map_contract, account): assert issubclass(type(call), Call) # Crate an Invoke transaction from call - invoke_transaction = await account.sign_invoke_transaction(call, max_fee=max_fee) + invoke_transaction = await account.sign_invoke_v1_transaction(call, max_fee=max_fee) # docs: end - assert isinstance(invoke_transaction, Invoke) + assert isinstance(invoke_transaction, InvokeV1) diff --git a/starknet_py/tests/e2e/docs/guide/test_custom_nonce.py b/starknet_py/tests/e2e/docs/guide/test_custom_nonce.py index 45c6e2801..acba7e8aa 100644 --- a/starknet_py/tests/e2e/docs/guide/test_custom_nonce.py +++ b/starknet_py/tests/e2e/docs/guide/test_custom_nonce.py @@ -61,5 +61,7 @@ async def get_nonce( # docs: end assert account.nonce_counter == 0 - await account.sign_invoke_transaction(calls=Call(0x1, 0x1, []), max_fee=10000000000) + await account.sign_invoke_v1_transaction( + calls=Call(0x1, 0x1, []), max_fee=10000000000 + ) assert account.nonce_counter == 1 diff --git a/starknet_py/tests/e2e/docs/guide/test_declaring_contracts.py b/starknet_py/tests/e2e/docs/guide/test_declaring_contracts.py index 4f42c59d2..392fdb5cb 100644 --- a/starknet_py/tests/e2e/docs/guide/test_declaring_contracts.py +++ b/starknet_py/tests/e2e/docs/guide/test_declaring_contracts.py @@ -6,9 +6,9 @@ async def test_declaring_contracts(account, map_compiled_contract): contract_compiled = map_compiled_contract # docs: start - # Account.sign_declare_transaction takes contract source code or compiled contract + # Account.sign_declare_v1_transaction takes contract source code or compiled contract # and returns Declare transaction - declare_transaction = await account.sign_declare_transaction( + declare_transaction = await account.sign_declare_v1_transaction( compiled_contract=contract_compiled, max_fee=int(1e16) ) diff --git a/starknet_py/tests/e2e/docs/guide/test_deploying_in_multicall.py b/starknet_py/tests/e2e/docs/guide/test_deploying_in_multicall.py index af9224899..426a56163 100644 --- a/starknet_py/tests/e2e/docs/guide/test_deploying_in_multicall.py +++ b/starknet_py/tests/e2e/docs/guide/test_deploying_in_multicall.py @@ -30,7 +30,7 @@ async def test_deploying_in_multicall(account, map_class_hash, map_compiled_cont # After that multicall transaction can be sent # Note that `deploy_call` and `put_call` are two regular calls! - invoke_tx = await account.sign_invoke_transaction( + invoke_tx = await account.sign_invoke_v1_transaction( calls=[deploy_call, put_call], max_fee=int(1e16) ) diff --git a/starknet_py/tests/e2e/docs/guide/test_deploying_with_udc.py b/starknet_py/tests/e2e/docs/guide/test_deploying_with_udc.py index 2b62e9735..79059cacd 100644 --- a/starknet_py/tests/e2e/docs/guide/test_deploying_with_udc.py +++ b/starknet_py/tests/e2e/docs/guide/test_deploying_with_udc.py @@ -80,7 +80,7 @@ async def test_deploying_with_udc( ) # docs: start # Or signed and send with an account - invoke_tx = await account.sign_invoke_transaction(deploy_call, max_fee=int(1e16)) + invoke_tx = await account.sign_invoke_v1_transaction(deploy_call, max_fee=int(1e16)) resp = await account.client.send_transaction(invoke_tx) # Wait for transaction diff --git a/starknet_py/tests/e2e/docs/guide/test_sign_for_fee_estimate.py b/starknet_py/tests/e2e/docs/guide/test_sign_for_fee_estimate.py index 686fc5944..bb058bdfa 100644 --- a/starknet_py/tests/e2e/docs/guide/test_sign_for_fee_estimate.py +++ b/starknet_py/tests/e2e/docs/guide/test_sign_for_fee_estimate.py @@ -6,7 +6,7 @@ async def test_signing_fee_estimate(account, map_contract): # docs: start # Create a transaction call = map_contract.functions["put"].prepare(key=10, value=20) - transaction = await account.sign_invoke_transaction(calls=call, max_fee=0) + transaction = await account.sign_invoke_v1_transaction(calls=call, max_fee=0) # Re-sign a transaction for fee estimation estimate_transaction = await account.sign_for_fee_estimate(transaction) @@ -20,7 +20,7 @@ async def test_signing_fee_estimate(account, map_contract): assert estimate.overall_fee > 0 # Use a new fee in original transaction - transaction = await account.sign_invoke_transaction( + transaction = await account.sign_invoke_v1_transaction( calls=call, max_fee=estimate.overall_fee ) diff --git a/starknet_py/tests/e2e/docs/migration_guide/test_account_comparison.py b/starknet_py/tests/e2e/docs/migration_guide/test_account_comparison.py index 32f5c0a28..c37af4611 100644 --- a/starknet_py/tests/e2e/docs/migration_guide/test_account_comparison.py +++ b/starknet_py/tests/e2e/docs/migration_guide/test_account_comparison.py @@ -27,12 +27,12 @@ async def test_account_comparison(gateway_account, map_contract): # docs-2: start # Sending transactions - tx = await account_client.sign_invoke_transaction(call, max_fee) + tx = await account_client.sign_invoke_v1_transaction(call, max_fee) await account_client.send_transaction(tx) # becomes - tx = await account.sign_invoke_transaction(call, max_fee=max_fee) + tx = await account.sign_invoke_v1_transaction(call, max_fee=max_fee) # Note that max_fee is now keyword-only argument await account.client.send_transaction(tx) # docs-2: end diff --git a/starknet_py/tests/e2e/fixtures/accounts.py b/starknet_py/tests/e2e/fixtures/accounts.py index feca50063..818a69daf 100644 --- a/starknet_py/tests/e2e/fixtures/accounts.py +++ b/starknet_py/tests/e2e/fixtures/accounts.py @@ -10,6 +10,7 @@ from starknet_py.hash.address import compute_address from starknet_py.net.account.account import Account from starknet_py.net.account.base_account import BaseAccount +from starknet_py.net.client_models import PriceUnit from starknet_py.net.full_node_client import FullNodeClient from starknet_py.net.http_client import HttpMethod, RpcHttpClient from starknet_py.net.models import StarknetChainId @@ -50,15 +51,8 @@ async def devnet_account_details( deployer_address=0, ) - http_client = RpcHttpClient(network) - await http_client.request( - http_method=HttpMethod.POST, - address=f"{network}/mint", - payload={ - "address": hex(address), - "amount": int(1e30), - }, - ) + await mint_token_on_devnet(network, address, int(1e30), PriceUnit.WEI.value) + await mint_token_on_devnet(network, address, int(1e30), PriceUnit.FRI.value) deploy_account_tx = await get_deploy_account_transaction( address=address, @@ -80,6 +74,15 @@ async def devnet_account_details( return hex(address), hex(key_pair.private_key) +async def mint_token_on_devnet(url: str, address: int, amount: int, unit: str): + http_client = RpcHttpClient(url) + await http_client.request( + http_method=HttpMethod.POST, + address=f"{url}/mint", + payload={"address": hex(address), "amount": amount, "unit": unit}, + ) + + @pytest_asyncio.fixture(scope="package") async def address_and_private_key( pytestconfig, @@ -131,14 +134,16 @@ def full_node_account( @dataclass class AccountToBeDeployedDetailsFactory: class_hash: int - fee_contract: Contract + eth_fee_contract: Contract + strk_fee_contract: Contract async def get( self, *, class_hash: Optional[int] = None, argent_calldata: bool = False ) -> AccountToBeDeployedDetails: return await get_deploy_account_details( class_hash=class_hash if class_hash is not None else self.class_hash, - fee_contract=self.fee_contract, + eth_fee_contract=self.eth_fee_contract, + strk_fee_contract=self.strk_fee_contract, argent_calldata=argent_calldata, ) @@ -146,7 +151,8 @@ async def get( @pytest_asyncio.fixture(scope="package") async def deploy_account_details_factory( account_with_validate_deploy_class_hash: int, - fee_contract: Contract, + eth_fee_contract: Contract, + strk_fee_contract: Contract, ) -> AccountToBeDeployedDetailsFactory: """ Returns AccountToBeDeployedDetailsFactory. @@ -157,7 +163,8 @@ async def deploy_account_details_factory( """ return AccountToBeDeployedDetailsFactory( class_hash=account_with_validate_deploy_class_hash, - fee_contract=fee_contract, + eth_fee_contract=eth_fee_contract, + strk_fee_contract=strk_fee_contract, ) diff --git a/starknet_py/tests/e2e/fixtures/constants.py b/starknet_py/tests/e2e/fixtures/constants.py index 79d2d23f1..c12fc97fe 100644 --- a/starknet_py/tests/e2e/fixtures/constants.py +++ b/starknet_py/tests/e2e/fixtures/constants.py @@ -3,6 +3,8 @@ from dotenv import load_dotenv +from starknet_py.net.client_models import ResourceBounds, ResourceBoundsMapping + load_dotenv(dotenv_path=Path(os.path.dirname(__file__)) / "../test-variables.env") @@ -56,8 +58,17 @@ def _get_env_lambda(env_name): ) DEVNET_PRE_DEPLOYED_ACCOUNT_PRIVATE_KEY = "0xc10662b7b247c7cecf7e8a30726cff12" +STRK_FEE_CONTRACT_ADDRESS = ( + "0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d" +) + MAX_FEE = int(1e16) +MAX_RESOURCE_BOUNDS_L1 = ResourceBounds(max_amount=5000, max_price_per_unit=int(2e12)) +MAX_RESOURCE_BOUNDS = ResourceBoundsMapping( + l1_gas=MAX_RESOURCE_BOUNDS_L1, l2_gas=ResourceBounds.init_with_zeros() +) + MOCK_DIR = Path(os.path.dirname(__file__)) / "../mock" TYPED_DATA_DIR = MOCK_DIR / "typed_data" CONTRACTS_DIR = MOCK_DIR / "contracts" diff --git a/starknet_py/tests/e2e/fixtures/contracts.py b/starknet_py/tests/e2e/fixtures/contracts.py index a6284cc67..632456210 100644 --- a/starknet_py/tests/e2e/fixtures/contracts.py +++ b/starknet_py/tests/e2e/fixtures/contracts.py @@ -20,6 +20,7 @@ CONTRACTS_COMPILED_V2_DIR, CONTRACTS_DIR, MAX_FEE, + STRK_FEE_CONTRACT_ADDRESS, ) from starknet_py.tests.e2e.fixtures.misc import read_contract @@ -54,6 +55,24 @@ def sierra_minimal_compiled_contract_and_class_hash() -> Tuple[str, int]: ) +@pytest.fixture(scope="package") +def abi_types_compiled_contract_and_class_hash() -> Tuple[str, int]: + """ + Returns abi_types contract compiled to sierra and its compiled class hash. + """ + compiled_contract = read_contract( + "abi_types_compiled.json", directory=CONTRACTS_COMPILED_V2_DIR + ) + compiled_contract_casm = read_contract( + "abi_types_compiled.casm", directory=CONTRACTS_COMPILED_V2_DIR + ) + + return ( + compiled_contract, + compute_casm_class_hash(create_casm_class(compiled_contract_casm)), + ) + + @pytest.fixture(scope="package") def simple_storage_with_event_compiled_contract() -> str: """ @@ -215,11 +234,34 @@ async def deploy_erc20_contract( @pytest.fixture(scope="package") -def fee_contract(account: BaseAccount) -> Contract: +def eth_fee_contract(account: BaseAccount, fee_contract_abi) -> Contract: """ - Returns an instance of the fee contract. It is used to transfer tokens. + Returns an instance of the ETH fee contract. It is used to transfer tokens. """ - abi = [ + + return Contract( + address=FEE_CONTRACT_ADDRESS, + abi=fee_contract_abi, + provider=account, + ) + + +@pytest.fixture(scope="package") +def strk_fee_contract(account: BaseAccount, fee_contract_abi) -> Contract: + """ + Returns an instance of the STRK fee contract. It is used to transfer tokens. + """ + + return Contract( + address=STRK_FEE_CONTRACT_ADDRESS, + abi=fee_contract_abi, + provider=account, + ) + + +@pytest.fixture(scope="package") +def fee_contract_abi(): + return [ { "inputs": [ {"name": "recipient", "type": "felt"}, @@ -240,12 +282,6 @@ def fee_contract(account: BaseAccount) -> Contract: }, ] - return Contract( - address=FEE_CONTRACT_ADDRESS, - abi=abi, - provider=account, - ) - @pytest.fixture(name="balance_contract") def fixture_balance_contract() -> str: @@ -260,7 +296,7 @@ async def declare_account(account: BaseAccount, compiled_account_contract: str) Declares a specified account. """ - declare_tx = await account.sign_declare_transaction( + declare_tx = await account.sign_declare_v1_transaction( compiled_contract=compiled_account_contract, max_fee=MAX_FEE, ) @@ -326,7 +362,7 @@ async def map_class_hash(account: BaseAccount, map_compiled_contract: str) -> in """ Returns class_hash of the map.cairo. """ - declare = await account.sign_declare_transaction( + declare = await account.sign_declare_v1_transaction( compiled_contract=map_compiled_contract, max_fee=int(1e16), ) @@ -342,7 +378,7 @@ async def simple_storage_with_event_class_hash( """ Returns class_hash of the simple_storage_with_event.cairo """ - declare = await account.sign_declare_transaction( + declare = await account.sign_declare_v1_transaction( compiled_contract=simple_storage_with_event_compiled_contract, max_fee=int(1e16), ) @@ -356,7 +392,7 @@ async def erc20_class_hash(account: BaseAccount, erc20_compiled_contract: str) - """ Returns class_hash of the erc20.cairo. """ - declare = await account.sign_declare_transaction( + declare = await account.sign_declare_v1_transaction( compiled_contract=erc20_compiled_contract, max_fee=int(1e16), ) @@ -402,7 +438,7 @@ async def constructor_with_arguments_class_hash( """ Returns a class_hash of the constructor_with_arguments.cairo. """ - declare = await account.sign_declare_transaction( + declare = await account.sign_declare_v1_transaction( compiled_contract=constructor_with_arguments_compiled, max_fee=int(1e16), ) diff --git a/starknet_py/tests/e2e/fixtures/devnet.py b/starknet_py/tests/e2e/fixtures/devnet.py index 7a4068a65..8fa09d078 100644 --- a/starknet_py/tests/e2e/fixtures/devnet.py +++ b/starknet_py/tests/e2e/fixtures/devnet.py @@ -33,6 +33,8 @@ def get_start_devnet_command(devnet_port: int) -> List[str]: str(1), "--seed", # generates same accounts each time str(1), + "--state-archive-capacity", + "full", ] diff --git a/starknet_py/tests/e2e/fixtures/proxy.py b/starknet_py/tests/e2e/fixtures/proxy.py index c9cfc79fd..e89eb8a8d 100644 --- a/starknet_py/tests/e2e/fixtures/proxy.py +++ b/starknet_py/tests/e2e/fixtures/proxy.py @@ -110,7 +110,7 @@ async def deploy_proxy_to_contract( compiled_contract_name, directory=CONTRACTS_COMPILED_V0_DIR ) - declare_tx = await account.sign_declare_transaction( + declare_tx = await account.sign_declare_v1_transaction( compiled_contract=compiled_contract, max_fee=MAX_FEE ) declare_result = await account.client.declare(declare_tx) diff --git a/starknet_py/tests/e2e/tests_on_networks/account_test.py b/starknet_py/tests/e2e/tests_on_networks/account_test.py index 7760a597a..4c8566903 100644 --- a/starknet_py/tests/e2e/tests_on_networks/account_test.py +++ b/starknet_py/tests/e2e/tests_on_networks/account_test.py @@ -22,7 +22,7 @@ async def test_sign_invoke_tx_for_fee_estimation(full_node_account_integration): ) call = map_contract.functions["put"].prepare(key=40, value=50) - transaction = await account.sign_invoke_transaction(calls=call, max_fee=MAX_FEE) + transaction = await account.sign_invoke_v1_transaction(calls=call, max_fee=MAX_FEE) estimate_fee_transaction = await account.sign_for_fee_estimate(transaction) @@ -44,7 +44,7 @@ async def test_sign_declare_tx_for_fee_estimation( ): account = full_node_account_integration - transaction = await account.sign_declare_transaction( + transaction = await account.sign_declare_v1_transaction( compiled_contract=map_compiled_contract, max_fee=MAX_FEE ) diff --git a/starknet_py/tests/e2e/tests_on_networks/client_test.py b/starknet_py/tests/e2e/tests_on_networks/client_test.py index 6daca5c8b..15b8e8dde 100644 --- a/starknet_py/tests/e2e/tests_on_networks/client_test.py +++ b/starknet_py/tests/e2e/tests_on_networks/client_test.py @@ -7,7 +7,12 @@ from starknet_py.net.client_errors import ClientError from starknet_py.net.client_models import ( Call, + DAMode, + DeclareTransactionV3, + DeployAccountTransactionV3, EstimatedFee, + InvokeTransactionV3, + ResourceBoundsMapping, TransactionExecutionStatus, TransactionFinalityStatus, TransactionReceipt, @@ -49,7 +54,9 @@ async def test_wait_for_tx_reverted(full_node_account_testnet): selector=get_selector_from_name("empty"), calldata=[0x1, 0x2, 0x3, 0x4, 0x5], ) - sign_invoke = await account.sign_invoke_transaction(calls=call, max_fee=int(1e16)) + sign_invoke = await account.sign_invoke_v1_transaction( + calls=call, max_fee=int(1e16) + ) invoke = await account.client.send_transaction(sign_invoke) with pytest.raises(TransactionRevertedError, match="Input too long for arguments"): @@ -64,7 +71,9 @@ async def test_wait_for_tx_accepted(full_node_account_testnet): selector=get_selector_from_name("empty"), calldata=[], ) - sign_invoke = await account.sign_invoke_transaction(calls=call, max_fee=int(1e16)) + sign_invoke = await account.sign_invoke_v1_transaction( + calls=call, max_fee=int(1e16) + ) invoke = await account.client.send_transaction(sign_invoke) result = await account.client.wait_for_tx(tx_hash=invoke.transaction_hash) @@ -81,7 +90,9 @@ async def test_transaction_not_received_max_fee_too_small(full_node_account_test selector=get_selector_from_name("empty"), calldata=[], ) - sign_invoke = await account.sign_invoke_transaction(calls=call, max_fee=int(1e10)) + sign_invoke = await account.sign_invoke_v1_transaction( + calls=call, max_fee=int(1e10) + ) with pytest.raises(ClientError, match=r".*Max fee.*"): await account.client.send_transaction(sign_invoke) @@ -95,7 +106,9 @@ async def test_transaction_not_received_max_fee_too_big(full_node_account_testne selector=get_selector_from_name("empty"), calldata=[], ) - sign_invoke = await account.sign_invoke_transaction(calls=call, max_fee=sys.maxsize) + sign_invoke = await account.sign_invoke_v1_transaction( + calls=call, max_fee=sys.maxsize + ) with pytest.raises(ClientError, match=r".*max_fee.*"): await account.client.send_transaction(sign_invoke) @@ -109,7 +122,7 @@ async def test_transaction_not_received_invalid_nonce(full_node_account_testnet) selector=get_selector_from_name("empty"), calldata=[], ) - sign_invoke = await account.sign_invoke_transaction( + sign_invoke = await account.sign_invoke_v1_transaction( calls=call, max_fee=int(1e16), nonce=0 ) @@ -127,7 +140,9 @@ async def test_transaction_not_received_invalid_signature(full_node_account_test selector=get_selector_from_name("empty"), calldata=[], ) - sign_invoke = await account.sign_invoke_transaction(calls=call, max_fee=int(1e16)) + sign_invoke = await account.sign_invoke_v1_transaction( + calls=call, max_fee=int(1e16) + ) sign_invoke = dataclasses.replace(sign_invoke, signature=[0x21, 0x37]) with pytest.raises(ClientError, match=r"(.*Signature.*)|(.*An unexpected error.*)"): @@ -160,7 +175,8 @@ async def test_estimate_message_fee(full_node_client_integration): assert isinstance(estimated_message, EstimatedFee) assert estimated_message.overall_fee > 0 assert estimated_message.gas_price > 0 - assert estimated_message.gas_usage > 0 + assert estimated_message.gas_consumed > 0 + assert estimated_message.unit is not None @pytest.mark.asyncio @@ -361,3 +377,32 @@ async def test_get_tx_receipt_new_fields(full_node_client_testnet): ) assert receipt.execution_resources is not None + + +@pytest.mark.parametrize( + "tx_hash, tx_type", + [ + ( + 0x7B31376D1C4F467242616530901E1B441149F1106EF765F202A50A6F917762B, + DeclareTransactionV3, + ), + ( + 0x750DC0D6B64D29E7F0CA6399802BA46C6FCA0E37FB977706DFD1DD42B63D757, + DeployAccountTransactionV3, + ), + ( + 0x15F2CF38832542602E2D1C8BF0634893E6B43ACB6879E8A8F892F5A9B03C907, + InvokeTransactionV3, + ), + ], +) +@pytest.mark.asyncio +async def test_get_transaction_v3(full_node_client_testnet, tx_hash, tx_type): + tx = await full_node_client_testnet.get_transaction(tx_hash=tx_hash) + assert isinstance(tx, tx_type) + assert tx.version == 3 + assert isinstance(tx.resource_bounds, ResourceBoundsMapping) + assert tx.paymaster_data == [] + assert tx.tip == 0 + assert tx.nonce_data_availability_mode == DAMode.L1 + assert tx.fee_data_availability_mode == DAMode.L1 diff --git a/starknet_py/tests/e2e/tests_on_networks/trace_api_test.py b/starknet_py/tests/e2e/tests_on_networks/trace_api_test.py index c0641b0fc..7ed6df994 100644 --- a/starknet_py/tests/e2e/tests_on_networks/trace_api_test.py +++ b/starknet_py/tests/e2e/tests_on_networks/trace_api_test.py @@ -105,7 +105,7 @@ async def test_simulate_transactions_declare_on_network(full_node_account_testne compiled_contract = read_contract( "map_compiled.json", directory=CONTRACTS_COMPILED_V0_DIR ) - declare_tx = await full_node_account_testnet.sign_declare_transaction( + declare_tx = await full_node_account_testnet.sign_declare_v1_transaction( compiled_contract, max_fee=int(1e16) ) diff --git a/starknet_py/tests/e2e/utils.py b/starknet_py/tests/e2e/utils.py index 8c7a0d774..23e5088c4 100644 --- a/starknet_py/tests/e2e/utils.py +++ b/starknet_py/tests/e2e/utils.py @@ -8,7 +8,7 @@ from starknet_py.net.client import Client from starknet_py.net.http_client import HttpClient, HttpMethod from starknet_py.net.models import StarknetChainId -from starknet_py.net.models.transaction import DeployAccount +from starknet_py.net.models.transaction import DeployAccountV1 from starknet_py.net.signer.stark_curve_signer import KeyPair from starknet_py.net.udc_deployer.deployer import _get_random_salt from starknet_py.tests.e2e.fixtures.constants import MAX_FEE @@ -17,13 +17,18 @@ async def get_deploy_account_details( - *, class_hash: int, fee_contract: Contract, argent_calldata: bool = False + *, + class_hash: int, + eth_fee_contract: Contract, + strk_fee_contract: Contract, + argent_calldata: bool = False, ) -> AccountToBeDeployedDetails: """ Returns address, key_pair, salt and class_hash of the account with validate deploy. :param class_hash: Class hash of account to be deployed. - :param fee_contract: Contract for prefunding deployments. + :param eth_fee_contract: Contract for prefunding deployments in ETH. + :param strk_fee_contract: Contract for prefunding deployments in STRK. :param argent_calldata: Flag deciding whether calldata should be in Argent-account format. """ priv_key = _get_random_private_key_unsafe() @@ -42,17 +47,22 @@ async def get_deploy_account_details( deployer_address=0, ) - res = await fee_contract.functions["transfer"].invoke( + transfer_wei_res = await eth_fee_contract.functions["transfer"].invoke( recipient=address, amount=int(1e19), max_fee=MAX_FEE ) - await res.wait_for_acceptance() + await transfer_wei_res.wait_for_acceptance() + + transfer_fri_res = await strk_fee_contract.functions["transfer"].invoke( + recipient=address, amount=int(1e19), max_fee=MAX_FEE + ) + await transfer_fri_res.wait_for_acceptance() return address, key_pair, salt, class_hash async def get_deploy_account_transaction( *, address: int, key_pair: KeyPair, salt: int, class_hash: int, client: Client -) -> DeployAccount: +) -> DeployAccountV1: """ Get a signed DeployAccount transaction from provided details """ @@ -63,7 +73,7 @@ async def get_deploy_account_transaction( key_pair=key_pair, chain=StarknetChainId.TESTNET, ) - return await account.sign_deploy_account_transaction( + return await account.sign_deploy_account_v1_transaction( class_hash=class_hash, contract_address_salt=salt, constructor_calldata=[key_pair.public_key], From 881f5ae3f44d7bfe896787a5d17a9f4d52dc4f77 Mon Sep 17 00:00:00 2001 From: ddoktorski <45050160+ddoktorski@users.noreply.github.com> Date: Wed, 10 Jan 2024 20:24:53 +0100 Subject: [PATCH 5/7] Add `data` info to RPC error message (#1244) --- starknet_py/net/client_errors.py | 11 +++++++++-- starknet_py/net/http_client.py | 4 +++- starknet_py/tests/e2e/client/full_node_test.py | 2 +- .../tests/e2e/tests_on_networks/client_test.py | 7 ++++--- 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/starknet_py/net/client_errors.py b/starknet_py/net/client_errors.py index 7ab8923f9..31310f182 100644 --- a/starknet_py/net/client_errors.py +++ b/starknet_py/net/client_errors.py @@ -9,9 +9,16 @@ class ClientError(Exception): Base class for all errors raised while attempting to communicate with Starknet through Client. """ - def __init__(self, message: str, code: Optional[str] = None): + def __init__( + self, message: str, code: Optional[str] = None, data: Optional[str] = None + ): self.code = code - self.message = f"Client failed{f' with code {code}' if code is not None else ''}: {message}." + self.data = data + self.message = ( + f"Client failed{f' with code {code}' if code is not None else ''}. " + f"Message: {message}.{f' Data: {data}' if data is not None else ''}" + ) + super().__init__(self.message) diff --git a/starknet_py/net/http_client.py b/starknet_py/net/http_client.py index 4a4498a53..2da01fcf3 100644 --- a/starknet_py/net/http_client.py +++ b/starknet_py/net/http_client.py @@ -80,7 +80,9 @@ def handle_rpc_error(result: dict): if "error" not in result: raise ServerError(body=result) raise ClientError( - code=result["error"]["code"], message=result["error"]["message"] + code=result["error"]["code"], + message=result["error"]["message"], + data=result["error"].get("data"), ) async def handle_request_error(self, request: ClientResponse): diff --git a/starknet_py/tests/e2e/client/full_node_test.py b/starknet_py/tests/e2e/client/full_node_test.py index 3446b5eed..f5d9f9c35 100644 --- a/starknet_py/tests/e2e/client/full_node_test.py +++ b/starknet_py/tests/e2e/client/full_node_test.py @@ -86,7 +86,7 @@ async def test_get_class_at( @pytest.mark.asyncio async def test_get_class_at_throws_on_wrong_address(client): with pytest.raises( - ClientError, match="Client failed with code 20: Contract not found." + ClientError, match="Client failed with code 20. Message: Contract not found." ): await client.get_class_at(contract_address=0, block_hash="latest") diff --git a/starknet_py/tests/e2e/tests_on_networks/client_test.py b/starknet_py/tests/e2e/tests_on_networks/client_test.py index 15b8e8dde..2d31448bf 100644 --- a/starknet_py/tests/e2e/tests_on_networks/client_test.py +++ b/starknet_py/tests/e2e/tests_on_networks/client_test.py @@ -130,8 +130,6 @@ async def test_transaction_not_received_invalid_nonce(full_node_account_testnet) await account.client.send_transaction(sign_invoke) -# TODO (#1219): fix this error once ClientError returns data field -@pytest.mark.skip @pytest.mark.asyncio async def test_transaction_not_received_invalid_signature(full_node_account_testnet): account = full_node_account_testnet @@ -145,9 +143,12 @@ async def test_transaction_not_received_invalid_signature(full_node_account_test ) sign_invoke = dataclasses.replace(sign_invoke, signature=[0x21, 0x37]) - with pytest.raises(ClientError, match=r"(.*Signature.*)|(.*An unexpected error.*)"): + with pytest.raises(ClientError, match=r"Signature.*is invalid") as exc: await account.client.send_transaction(sign_invoke) + assert exc.value.data is not None + assert "Data:" in exc.value.message + # ------------------------------------ FULL_NODE_CLIENT TESTS ------------------------------------ From 020a0a9a00cd7f7cc0a171503ee1eda8d70ffa60 Mon Sep 17 00:00:00 2001 From: ddoktorski <45050160+ddoktorski@users.noreply.github.com> Date: Wed, 10 Jan 2024 22:29:50 +0100 Subject: [PATCH 6/7] Add `Uint64` and `Uint128` classes (#1247) * Add classes for `u64` and `u128` types * Add regex validation to `Felt` class * Use serliazation logic to get broadcasted tx v3 common properties * Add tests for `DAModeField` * Add `NumberAsHex` class * Use `NumberAsHex` instead of `Felt` for some fields to align with RPC spec --- starknet_py/net/full_node_client.py | 17 +-- starknet_py/net/schemas/common.py | 73 +++++++-- starknet_py/net/schemas/common_test.py | 195 +++++++++++++++++++++---- starknet_py/net/schemas/rpc.py | 15 +- 4 files changed, 245 insertions(+), 55 deletions(-) diff --git a/starknet_py/net/full_node_client.py b/starknet_py/net/full_node_client.py index 6c453d6b6..44b2170bc 100644 --- a/starknet_py/net/full_node_client.py +++ b/starknet_py/net/full_node_client.py @@ -68,7 +68,6 @@ PendingBlockStateUpdateSchema, PendingStarknetBlockSchema, PendingStarknetBlockWithTxHashesSchema, - ResourceBoundsMappingSchema, SentTransactionSchema, SierraContractClassSchema, SimulatedTransactionSchema, @@ -78,6 +77,7 @@ TransactionReceiptSchema, TransactionStatusResponseSchema, TransactionTraceSchema, + TransactionV3Schema, TypesOfTransactionsSchema, ) from starknet_py.net.schemas.utils import _extract_tx_version @@ -955,16 +955,7 @@ def _create_broadcasted_txn_common_properties(transaction: AccountTransaction) - def _create_broadcasted_txn_v3_common_properties( transaction: Union[DeclareV3, InvokeV3, DeployAccountV3] ) -> dict: - resource_bonds = cast( - Dict, ResourceBoundsMappingSchema().dump(obj=transaction.resource_bounds) + return cast( + Dict, + TransactionV3Schema(exclude=["version", "signature"]).dump(obj=transaction), ) - - broadcasted_txn_v3_common_properties = { - "resource_bounds": resource_bonds, - "tip": _to_rpc_felt(transaction.tip), - "paymaster_data": [_to_rpc_felt(data) for data in transaction.paymaster_data], - "nonce_data_availability_mode": transaction.nonce_data_availability_mode.name, - "fee_data_availability_mode": transaction.fee_data_availability_mode.name, - } - - return broadcasted_txn_v3_common_properties diff --git a/starknet_py/net/schemas/common.py b/starknet_py/net/schemas/common.py index 887badd4d..d7e9ebb38 100644 --- a/starknet_py/net/schemas/common.py +++ b/starknet_py/net/schemas/common.py @@ -1,4 +1,5 @@ import re +import sys from typing import Any, Mapping, Optional, Union from marshmallow import Schema, ValidationError, fields, post_load @@ -25,13 +26,30 @@ def _pascal_to_screaming_upper(checked_string: str) -> str: return re.sub(r"(? bool: + return isinstance(value, int) and 0 <= value < self.MAX_VALUE + + def _is_str_and_valid_pattern(self, value: Any) -> bool: + return ( + isinstance(value, str) + and re.fullmatch(self.REGEX_PATTERN, value) is not None + ) + + +class Felt(NumberAsHex): + """ + Field used to serialize and deserialize felt type. + """ + + MAX_VALUE = 2**252 + REGEX_PATTERN = r"^0x(0|[a-fA-F1-9]{1}[a-fA-F0-9]{0,62})$" + + +class Uint64(NumberAsHex): + """ + Field used to serialize and deserialize RPC u64 type. + """ + + MAX_VALUE = 2**64 + REGEX_PATTERN = r"^0x(0|[a-fA-F1-9]{1}[a-fA-F0-9]{0,15})$" + + +class Uint128(NumberAsHex): + """ + Field used to serialize and deserialize RPC u128 type. + """ + + MAX_VALUE = 2**128 + REGEX_PATTERN = r"^0x(0|[a-fA-F1-9]{1}[a-fA-F0-9]{0,31})$" class NonPrefixedHex(fields.Field): diff --git a/starknet_py/net/schemas/common_test.py b/starknet_py/net/schemas/common_test.py index 80ae9ea4a..ef3bd526f 100644 --- a/starknet_py/net/schemas/common_test.py +++ b/starknet_py/net/schemas/common_test.py @@ -1,55 +1,170 @@ +from typing import Optional, Type, Union + import pytest from marshmallow import Schema, ValidationError -from starknet_py.net.client_models import BlockStatus, TransactionStatus -from starknet_py.net.schemas.common import NonPrefixedHex -from starknet_py.net.schemas.rpc import BlockStatusField, Felt, StatusField +from starknet_py.net.client_models import BlockStatus, DAMode, Hash, TransactionStatus +from starknet_py.net.schemas.common import ( + BlockStatusField, + DAModeField, + Felt, + NonPrefixedHex, + StatusField, + Uint64, + Uint128, +) -def test_serialize_felt(): - class SchemaWithFelt(Schema): - value1 = Felt(data_key="value1") +class SchemaWithUint64(Schema): + value = Uint64(data_key="value") - data = {"value1": 2137} - serialized = SchemaWithFelt().dumps(data) - assert '"value1": "0x859"' in serialized +class SchemaWithUint128(Schema): + value = Uint128(data_key="value") + + +class SchemaWithFelt(Schema): + value = Felt(data_key="value") -def test_serialize_felt_throws_on_none(): - class SchemaWithFelt(Schema): - value1 = Felt(data_key="value1") +class SchemaWithDAModeField(Schema): + value = DAModeField(data_key="value") - data = {"value1": None} - with pytest.raises(TypeError): + +def test_serialize_felt(): + data = {"value": 2137} + + serialized = SchemaWithFelt().dumps(data) + assert '"value": "0x859"' in serialized + + +@pytest.mark.parametrize( + "data", + [ + {"value": None}, + {"value": 2**252}, + ], +) +def test_serialize_felt_throws_on_invalid_data(data): + with pytest.raises(ValidationError, match="Invalid value provided for Felt"): SchemaWithFelt().dumps(data) def test_deserialize_felt(): - class SchemaWithFelt(Schema): - value1 = Felt(data_key="value1") - - data = {"value1": "0x859"} + data = {"value": "0x859"} deserialized = SchemaWithFelt().load(data) assert isinstance(deserialized, dict) - assert deserialized["value1"] == 2137 + assert deserialized["value"] == 2137 def test_deserialize_felt_throws_on_invalid_data(): - class SchemaWithFelt(Schema): - value1 = Felt(data_key="value1") - - data = {"value1": "2137"} + data = {"value": "2137"} - with pytest.raises(ValidationError, match="Invalid value provided for felt"): + with pytest.raises(ValidationError, match="Invalid value provided for Felt"): SchemaWithFelt().load(data) - data = {"value1": "0xwww"} - with pytest.raises(ValidationError, match="Invalid felt."): + data = {"value": "0xwww"} + with pytest.raises(ValidationError, match="Invalid value provided for Felt"): SchemaWithFelt().load(data) +@pytest.mark.parametrize( + "data, expected_serialized", + ( + ({"value": 0}, "0x0"), + ({"value": "0x100"}, "0x100"), + ({"value": 2**32}, "0x100000000"), + ), +) +def test_serialize_uint64(data, expected_serialized): + serialized = SchemaWithUint64().dumps(data) + assert f'"value": "{expected_serialized}"' in serialized + + +@pytest.mark.parametrize( + "data", + [{"value": -1}, {"value": 2**64}, {"value": None}], +) +def test_serialize_uint64_throws_on_invalid_data(data): + with pytest.raises( + ValidationError, + match=get_uint_error_message(Uint64, data["value"]), + ): + SchemaWithUint64().dumps(data) + + +@pytest.mark.parametrize( + "data", + [{"value": "0x100000000"}, {"value": 2**32}], +) +def test_deserialize_uint64(data): + deserialized = SchemaWithUint64().load(data) + assert isinstance(deserialized, dict) + assert deserialized["value"] == 2**32 + + +@pytest.mark.parametrize( + "data", + [ + {"value": -1}, + {"value": "1000"}, + {"value": 2**64}, + {"value": "0xwrong"}, + {"value": ""}, + ], +) +def test_deserialize_uint64_throws_on_invalid_data(data): + with pytest.raises( + ValidationError, + match=get_uint_error_message(Uint64, data["value"]), + ): + SchemaWithUint64().load(data) + + +def test_serialize_uint128(): + data = {"value": 2**64} + serialized = SchemaWithUint128().dumps(data) + assert '"value": "0x10000000000000000"' in serialized + + +def test_serialize_uint128_throws_on_invalid_data(): + data = {"value": 2**128} + with pytest.raises( + ValidationError, + match=get_uint_error_message(Uint128, data["value"]), + ): + SchemaWithUint128().dumps(data) + + +@pytest.mark.parametrize( + "data", + [{"value": "0x10000000000000000"}, {"value": 2**64}], +) +def test_deserialize_uint128(data): + deserialized = SchemaWithUint128().load(data) + assert isinstance(deserialized, dict) + assert deserialized["value"] == 2**64 + + +@pytest.mark.parametrize( + "data", + [ + {"value": -1}, + {"value": "1000"}, + {"value": 2**128}, + {"value": "0xwrong"}, + {"value": ""}, + ], +) +def test_deserialize_uint128_throws_on_invalid_data(data): + with pytest.raises( + ValidationError, + match=get_uint_error_message(Uint128, data["value"]), + ): + SchemaWithUint128().load(data) + + def test_serialize_hex(): class SchemaWithHex(Schema): value1 = NonPrefixedHex(data_key="value1") @@ -134,3 +249,31 @@ class SchemaWithBlockStatusField(Schema): with pytest.raises(ValidationError, match="Invalid value for BlockStatus provided"): SchemaWithBlockStatusField().load(data) + + +@pytest.mark.parametrize( + "data", + [{"value": DAMode.L1}, {"value": DAMode.L2}], +) +def test_serialize_damode_field(data): + serialized = SchemaWithDAModeField().dumps(data) + assert f'"value": "{data["value"].name}"' in serialized + + +@pytest.mark.parametrize( + "data, expected_deserialized", + ( + ({"value": DAMode.L1.name}, DAMode.L1), + ({"value": DAMode.L2.name}, DAMode.L2), + ), +) +def test_deserialize_damode_field(data, expected_deserialized): + deserialized = SchemaWithDAModeField().load(data) + assert isinstance(deserialized, dict) + assert deserialized["value"] == expected_deserialized + + +def get_uint_error_message( + class_type: Union[Type[Uint64], Type[Uint128]], value: Optional[Hash] +) -> str: + return f"Invalid value provided for {class_type.__name__}: {str(value)}" diff --git a/starknet_py/net/schemas/rpc.py b/starknet_py/net/schemas/rpc.py index 18bbf12a7..79c398b80 100644 --- a/starknet_py/net/schemas/rpc.py +++ b/starknet_py/net/schemas/rpc.py @@ -66,10 +66,13 @@ Felt, FinalityStatusField, NonPrefixedHex, + NumberAsHex, PriceUnitField, StatusField, StorageEntrySchema, TransactionTypeField, + Uint64, + Uint128, ) from starknet_py.net.schemas.utils import ( _extract_tx_version, @@ -167,7 +170,7 @@ class TransactionReceiptSchema(Schema): messages_sent = fields.List( fields.Nested(L2toL1MessageSchema()), data_key="messages_sent", load_default=[] ) - message_hash = Felt(data_key="message_hash", load_default=None) + message_hash = NumberAsHex(data_key="message_hash", load_default=None) execution_resources = fields.Nested( ExecutionResourcesSchema(), data_key="execution_resources", required=True ) @@ -209,8 +212,8 @@ def make_dataclass(self, data, **kwargs) -> ResourcePrice: class ResourceBoundsSchema(Schema): - max_amount = Felt(data_key="max_amount", required=True) - max_price_per_unit = Felt(data_key="max_price_per_unit", required=True) + max_amount = Uint64(data_key="max_amount", required=True) + max_price_per_unit = Uint128(data_key="max_price_per_unit", required=True) @post_load def make_dataclass(self, data, **kwargs) -> ResourceBounds: @@ -237,7 +240,7 @@ class DeprecatedTransactionSchema(TransactionSchema): class TransactionV3Schema(TransactionSchema): - tip = Felt(data_key="tip", load_default=0) + tip = Uint64(data_key="tip", load_default=0) nonce_data_availability_mode = DAModeField( data_key="nonce_data_availability_mode", load_default=DAMode.L1 ) @@ -376,7 +379,7 @@ class L1HandlerTransactionSchema(TransactionSchema): contract_address = Felt(data_key="contract_address", required=True) calldata = fields.List(Felt(), data_key="calldata", required=True) entry_point_selector = Felt(data_key="entry_point_selector", required=True) - nonce = Felt(data_key="nonce", required=True) + nonce = NumberAsHex(data_key="nonce", required=True) @post_load def make_dataclass(self, data, **kwargs) -> L1HandlerTransaction: @@ -615,7 +618,7 @@ def make_dataclass(self, data, **kwargs) -> SierraEntryPoint: class EntryPointSchema(Schema): - offset = Felt(data_key="offset", required=True) + offset = NumberAsHex(data_key="offset", required=True) selector = Felt(data_key="selector", required=True) @post_load From b12812e694b1827cf969e0b422eedadb3c40f3f6 Mon Sep 17 00:00:00 2001 From: ddoktorski <45050160+ddoktorski@users.noreply.github.com> Date: Thu, 11 Jan 2024 10:19:07 +0100 Subject: [PATCH 7/7] v0.19.0-alpha (#1253) --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 8f24eff25..3c01cf4f9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "starknet-py" -version = "0.18.3" +version = "0.19.0-alpha" description = "A python SDK for Starknet" authors = ["Tomasz Rejowski ", "Jakub Ptak "] include = ["starknet_py", "starknet_py/utils/crypto/libcrypto_c_exports.*"]