From f2d52258f8e12483ce382d13b6b317969fd220d9 Mon Sep 17 00:00:00 2001 From: Dariusz Doktorski Date: Fri, 12 Jan 2024 11:28:30 +0100 Subject: [PATCH 01/24] Remove TODO #1182 comment that was already done --- starknet_py/contract.py | 1 - 1 file changed, 1 deletion(-) diff --git a/starknet_py/contract.py b/starknet_py/contract.py index eb2c730ff..ce56b1ca3 100644 --- a/starknet_py/contract.py +++ b/starknet_py/contract.py @@ -338,7 +338,6 @@ async def call( result = await self.call_raw(block_hash=block_hash, block_number=block_number) return self._payload_transformer.deserialize(result) - # TODO (#1182): add `cairo_version` parameter async def invoke( self, max_fee: Optional[int] = None, From 2b2dc1e9b8d67ab55ff085bfa0f749a17bac0dcf Mon Sep 17 00:00:00 2001 From: Dariusz Doktorski Date: Mon, 15 Jan 2024 12:26:42 +0100 Subject: [PATCH 02/24] Move contract deploy related test to `test_deploy` file --- .../e2e/contract_interaction/deploy_test.py | 17 ++++++++++ .../contract_interaction/interaction_test.py | 33 ------------------- 2 files changed, 17 insertions(+), 33 deletions(-) diff --git a/starknet_py/tests/e2e/contract_interaction/deploy_test.py b/starknet_py/tests/e2e/contract_interaction/deploy_test.py index fc479ec8f..d5d50e740 100644 --- a/starknet_py/tests/e2e/contract_interaction/deploy_test.py +++ b/starknet_py/tests/e2e/contract_interaction/deploy_test.py @@ -80,3 +80,20 @@ async def test_deploy_contract_flow(account, cairo1_hello_starknet_class_hash: i assert isinstance(contract.address, int) assert len(contract.functions) != 0 + + +@pytest.mark.asyncio +async def test_general_simplified_deployment_flow(account, map_compiled_contract): + declare_result = await Contract.declare( + account=account, + compiled_contract=map_compiled_contract, + max_fee=MAX_FEE, + ) + await declare_result.wait_for_acceptance() + deployment = await declare_result.deploy(max_fee=MAX_FEE) + await deployment.wait_for_acceptance() + + contract = deployment.deployed_contract + + assert isinstance(contract.address, int) + assert len(contract.functions) != 0 diff --git a/starknet_py/tests/e2e/contract_interaction/interaction_test.py b/starknet_py/tests/e2e/contract_interaction/interaction_test.py index 76e69d43f..b362a4440 100644 --- a/starknet_py/tests/e2e/contract_interaction/interaction_test.py +++ b/starknet_py/tests/e2e/contract_interaction/interaction_test.py @@ -1,6 +1,5 @@ import pytest -from starknet_py.common import create_compiled_contract from starknet_py.contract import Contract from starknet_py.hash.selector import get_selector_from_name from starknet_py.net.client_errors import ClientError @@ -157,35 +156,3 @@ async def test_error_when_estimating_fee_while_not_using_account(client, map_con match="Contract instance was created without providing an Account.", ): await contract.functions["put"].prepare(key=10, value=10).estimate_fee() - - -@pytest.mark.asyncio -async def test_general_simplified_deployment_flow(account, map_compiled_contract): - declare_result = await Contract.declare( - account=account, - compiled_contract=map_compiled_contract, - max_fee=MAX_FEE, - ) - await declare_result.wait_for_acceptance() - deployment = await declare_result.deploy(max_fee=MAX_FEE) - await deployment.wait_for_acceptance() - - contract = deployment.deployed_contract - - assert isinstance(contract.address, int) - assert len(contract.functions) != 0 - - -@pytest.mark.asyncio -async def test_deploy_contract_flow(account, map_compiled_contract, map_class_hash): - abi = create_compiled_contract(compiled_contract=map_compiled_contract).abi - - deploy_result = await Contract.deploy_contract( - class_hash=map_class_hash, account=account, abi=abi, max_fee=MAX_FEE - ) - await deploy_result.wait_for_acceptance() - - contract = deploy_result.deployed_contract - - assert isinstance(contract.address, int) - assert len(contract.functions) != 0 From d0aafc6dac2e6b9c050304226756e83735c75ce7 Mon Sep 17 00:00:00 2001 From: Dariusz Doktorski Date: Tue, 23 Jan 2024 16:19:54 +0100 Subject: [PATCH 03/24] Split `prepare` method for: call, invoke_v1 and invoke_v3 --- starknet_py/contract.py | 290 ++++++++++++++++++++++++++++++---------- 1 file changed, 219 insertions(+), 71 deletions(-) diff --git a/starknet_py/contract.py b/starknet_py/contract.py index ce56b1ca3..8f3fdcc16 100644 --- a/starknet_py/contract.py +++ b/starknet_py/contract.py @@ -3,6 +3,7 @@ import dataclasses import json import warnings +from abc import ABC, abstractmethod from dataclasses import dataclass from functools import cached_property from typing import Dict, List, Optional, Tuple, TypeVar, Union @@ -33,8 +34,9 @@ from starknet_py.hash.selector import get_selector_from_name 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, InvokeV1, parse_address +from starknet_py.net.client_models import Call, EstimatedFee, Hash, ResourceBounds, Tag +from starknet_py.net.models import AddressRepresentation, parse_address +from starknet_py.net.models.transaction import Invoke from starknet_py.net.udc_deployer.deployer import Deployer from starknet_py.proxy.contract_abi_resolver import ( ContractAbiResolver, @@ -153,7 +155,7 @@ class InvokeResult(SentTransaction): contract: ContractData = None # pyright: ignore """Additional information about the Contract that made the transaction.""" - invoke_transaction: InvokeV1 = None # pyright: ignore + invoke_transaction: Invoke = None # pyright: ignore """A InvokeTransaction instance used.""" def __post_init__(self): @@ -161,9 +163,6 @@ def __post_init__(self): assert self.invoke_transaction is not None -InvocationResult = InvokeResult - - @add_sync_methods @dataclass(frozen=True) class DeclareResult(SentTransaction): @@ -277,36 +276,15 @@ def __post_init__(self): raise ValueError("Argument deployed_contract can't be None.") -# pylint: disable=too-many-instance-attributes -@add_sync_methods -class PreparedFunctionCall(Call): - def __init__( - self, - calldata: List[int], - selector: int, - client: Client, - account: Optional[BaseAccount], - payload_transformer: FunctionSerializationAdapter, - contract_data: ContractData, - max_fee: Optional[int], - ): - # pylint: disable=too-many-arguments - super().__init__( - to_addr=contract_data.address, selector=selector, calldata=calldata - ) - self._client = client - self._internal_account = account - self._payload_transformer = payload_transformer - self._contract_data = contract_data - self.max_fee = max_fee - - @property - def _account(self) -> BaseAccount: - if self._internal_account is not None: - return self._internal_account +@dataclass +class CallToSend(Call): + _client: Client + _payload_transformer: FunctionSerializationAdapter - raise ValueError("Contract instance was created without providing an Account.") +@add_sync_methods +@dataclass +class PreparedFunctionCall(CallToSend): async def call_raw( self, block_hash: Optional[str] = None, @@ -338,6 +316,66 @@ async def call( result = await self.call_raw(block_hash=block_hash, block_number=block_number) return self._payload_transformer.deserialize(result) + +@add_sync_methods +@dataclass +class PreparedFunctionInvoke(ABC, CallToSend): + _contract_data: ContractData + _account: Optional[BaseAccount] + + def __post_init__(self): + if self._account is None: + raise ValueError( + "Contract instance was created without providing an Account. " + "It is not possible to prepare and send an invoke transaction." + ) + + @property + def get_account(self): + if self._account is not None: + return self._account + + raise ValueError( + "The account is not defined. It is not possible to send an invoke transaction." + ) + + @abstractmethod + async def estimate_fee( + self, + block_hash: Optional[Union[Hash, Tag]] = None, + block_number: Optional[Union[int, Tag]] = None, + *, + nonce: Optional[int] = None, + ) -> EstimatedFee: + """ + Estimate fee for prepared function call. + + :param block_hash: Estimate fee at specific block hash. + :param block_number: Estimate fee at given block number + (or "latest" / "pending" for the latest / pending block), default is "pending". + :param nonce: Nonce of the transaction. + :return: Estimated amount of the transaction cost, either in Wei or Fri associated with executing the + specified transaction. + """ + + async def _invoke(self, transaction: Invoke) -> InvokeResult: + response = await self._client.send_transaction(transaction) + + invoke_result = InvokeResult( + hash=response.transaction_hash, # noinspection PyTypeChecker + _client=self._client, + contract=self._contract_data, + invoke_transaction=transaction, + ) + + return invoke_result + + +@add_sync_methods +@dataclass +class PreparedFunctionInvokeV1(PreparedFunctionInvoke): + max_fee: Optional[int] + async def invoke( self, max_fee: Optional[int] = None, @@ -346,33 +384,22 @@ async def invoke( nonce: Optional[int] = None, ) -> InvokeResult: """ - Invokes a method. + Send an Invoke transaction version 1 for a prepared data. :param max_fee: Max amount of Wei to be paid when executing transaction. :param auto_estimate: Use automatic fee estimation, not recommend as it may lead to high costs. :param nonce: Nonce of the transaction. :return: InvokeResult. """ - if max_fee is not None: - self.max_fee = max_fee - transaction = await self._account.sign_invoke_v1_transaction( + transaction = await self.get_account.sign_invoke_v1_transaction( calls=self, nonce=nonce, - max_fee=self.max_fee, + max_fee=max_fee or self.max_fee, auto_estimate=auto_estimate, ) - response = await self._client.send_transaction(transaction) - - invoke_result = InvokeResult( - hash=response.transaction_hash, # noinspection PyTypeChecker - _client=self._client, - contract=self._contract_data, - invoke_transaction=transaction, - ) - - return invoke_result + return await self._invoke(transaction) async def estimate_fee( self, @@ -381,17 +408,60 @@ async def estimate_fee( *, nonce: Optional[int] = None, ) -> EstimatedFee: + tx = await self.get_account.sign_invoke_v1_transaction( + calls=self, nonce=nonce, max_fee=0 + ) + + estimated_fee = await self._client.estimate_fee( + tx=tx, + block_hash=block_hash, + block_number=block_number, + ) + + assert isinstance(estimated_fee, EstimatedFee) + return estimated_fee + + +@add_sync_methods +@dataclass +class PreparedFunctionInvokeV3(PreparedFunctionInvoke): + l1_resource_bounds: Optional[ResourceBounds] + + async def invoke( + self, + l1_resource_bounds: Optional[ResourceBounds] = None, + auto_estimate: bool = False, + *, + nonce: Optional[int] = None, + ) -> InvokeResult: """ - Estimate fee for prepared function call. + Send an Invoke transaction version 3 for a prepared data. - :param block_hash: Estimate fee at specific block hash. - :param block_number: Estimate fee at given block number - (or "latest" / "pending" for the latest / pending block), default is "pending". + :param l1_resource_bounds: Max amount and max price per unit of L1 gas (in Wei) used when executing + this transaction. + :param auto_estimate: Use automatic fee estimation, not recommend as it may lead to high costs. :param nonce: Nonce of the transaction. - :return: Estimated amount of Wei executing specified transaction will cost. + :return: InvokeResult. """ - tx = await self._account.sign_invoke_v1_transaction( - calls=self, nonce=nonce, max_fee=0 + + transaction = await self.get_account.sign_invoke_v3_transaction( + calls=self, + nonce=nonce, + l1_resource_bounds=l1_resource_bounds or self.l1_resource_bounds, + auto_estimate=auto_estimate, + ) + + return await self._invoke(transaction) + + async def estimate_fee( + self, + block_hash: Optional[Union[Hash, Tag]] = None, + block_number: Optional[Union[int, Tag]] = None, + *, + nonce: Optional[int] = None, + ) -> EstimatedFee: + tx = await self.get_account.sign_invoke_v3_transaction( + calls=self, nonce=nonce, l1_resource_bounds=ResourceBounds.init_with_zeros() ) estimated_fee = await self._client.estimate_fee( @@ -399,8 +469,8 @@ async def estimate_fee( block_hash=block_hash, block_number=block_number, ) - assert isinstance(estimated_fee, EstimatedFee) + assert isinstance(estimated_fee, EstimatedFee) return estimated_fee @@ -445,10 +515,9 @@ def __init__( assert isinstance(function, Abi.Function) and function is not None self._payload_transformer = serializer_for_function(function) - def prepare( + def prepare_call( self, *args, - max_fee: Optional[int] = None, **kwargs, ) -> PreparedFunctionCall: """ @@ -456,19 +525,16 @@ def prepare( Creates a ``PreparedFunctionCall`` instance which exposes calldata for every argument and adds more arguments when calling methods. - :param max_fee: Max amount of Wei to be paid when executing transaction. :return: PreparedFunctionCall. """ calldata = self._payload_transformer.serialize(*args, **kwargs) return PreparedFunctionCall( + to_addr=self.contract_data.address, calldata=calldata, - contract_data=self.contract_data, - client=self.client, - account=self.account, - payload_transformer=self._payload_transformer, selector=self.get_selector(self.name), - max_fee=max_fee, + _client=self.client, + _payload_transformer=self._payload_transformer, ) async def call( @@ -481,16 +547,44 @@ async def call( """ Call contract's function. ``*args`` and ``**kwargs`` are translated into Cairo calldata. The result is translated from Cairo data to python values. - Equivalent of ``.prepare(*args, **kwargs).call()``. + Equivalent of ``.prepare_call(*args, **kwargs).call()``. :param block_hash: Block hash to perform the call to the contract at specific point of time. :param block_number: Block number to perform the call to the contract at specific point of time. + :return: TupleDataclass representing call result. """ - return await self.prepare(max_fee=0, *args, **kwargs).call( + return await self.prepare_call(*args, **kwargs).call( block_hash=block_hash, block_number=block_number ) - async def invoke( + def prepare_invoke_v1( + self, + *args, + max_fee: Optional[int] = None, + **kwargs, + ) -> PreparedFunctionInvokeV1: + """ + ``*args`` and ``**kwargs`` are translated into Cairo calldata. + Creates a ``PreparedFunctionInvokeV1`` instance which exposes calldata for every argument + and adds more arguments when calling methods. + + :param max_fee: Max amount of Wei to be paid when executing transaction. + :return: PreparedFunctionCall. + """ + + calldata = self._payload_transformer.serialize(*args, **kwargs) + return PreparedFunctionInvokeV1( + to_addr=self.contract_data.address, + calldata=calldata, + selector=self.get_selector(self.name), + max_fee=max_fee, + _contract_data=self.contract_data, + _client=self.client, + _account=self.account, + _payload_transformer=self._payload_transformer, + ) + + async def invoke_v1( self, *args, max_fee: Optional[int] = None, @@ -500,17 +594,71 @@ async def invoke( ) -> InvokeResult: """ Invoke contract's function. ``*args`` and ``**kwargs`` are translated into Cairo calldata. - Equivalent of ``.prepare(*args, **kwargs).invoke()``. + Equivalent of ``.prepare_invoke_v1(*args, **kwargs).invoke()``. :param max_fee: Max amount of Wei to be paid when executing transaction. :param auto_estimate: Use automatic fee estimation, not recommend as it may lead to high costs. :param nonce: Nonce of the transaction. + :return: InvokeResult. """ - prepared_call = self.prepare(*args, **kwargs) - return await prepared_call.invoke( + prepared_invoke = self.prepare_invoke_v1(*args, **kwargs) + return await prepared_invoke.invoke( max_fee=max_fee, nonce=nonce, auto_estimate=auto_estimate ) + def prepare_invoke_v3( + self, + *args, + l1_resource_bounds: Optional[ResourceBounds] = None, + **kwargs, + ) -> PreparedFunctionInvokeV3: + """ + ``*args`` and ``**kwargs`` are translated into Cairo calldata. + Creates a ``PreparedFunctionInvokeV3`` instance which exposes calldata for every argument + and adds more arguments when calling methods. + + :param l1_resource_bounds: Max amount and max price per unit of L1 gas (in Wei) used when executing + this transaction. + :return: PreparedFunctionInvokeV3. + """ + + calldata = self._payload_transformer.serialize(*args, **kwargs) + return PreparedFunctionInvokeV3( + to_addr=self.contract_data.address, + calldata=calldata, + selector=self.get_selector(self.name), + l1_resource_bounds=l1_resource_bounds, + _contract_data=self.contract_data, + _client=self.client, + _account=self.account, + _payload_transformer=self._payload_transformer, + ) + + async def invoke_v3( + self, + *args, + l1_resource_bounds: Optional[ResourceBounds] = None, + auto_estimate: bool = False, + nonce: Optional[int] = None, + **kwargs, + ) -> InvokeResult: + """ + Invoke contract's function. ``*args`` and ``**kwargs`` are translated into Cairo calldata. + Equivalent of ``.prepare_invoke_v3(*args, **kwargs).invoke()``. + + :param l1_resource_bounds: Max amount and max price per unit of L1 gas (in Wei) used when executing + this transaction. + :param auto_estimate: Use automatic fee estimation, not recommend as it may lead to high costs. + :param nonce: Nonce of the transaction. + :return: InvokeResult. + """ + prepared_invoke = self.prepare_invoke_v3(*args, **kwargs) + return await prepared_invoke.invoke( + l1_resource_bounds=l1_resource_bounds, + nonce=nonce, + auto_estimate=auto_estimate, + ) + @staticmethod def get_selector(function_name: str): """ From 2026549068ad2f5637a149de79a53984819ba9d7 Mon Sep 17 00:00:00 2001 From: Dariusz Doktorski Date: Wed, 24 Jan 2024 12:14:05 +0100 Subject: [PATCH 04/24] Adjust interaction tests to the new `Contract` interface --- .../contract_interaction/interaction_test.py | 192 +++++++++++++----- 1 file changed, 138 insertions(+), 54 deletions(-) diff --git a/starknet_py/tests/e2e/contract_interaction/interaction_test.py b/starknet_py/tests/e2e/contract_interaction/interaction_test.py index b362a4440..1ffdd29c6 100644 --- a/starknet_py/tests/e2e/contract_interaction/interaction_test.py +++ b/starknet_py/tests/e2e/contract_interaction/interaction_test.py @@ -1,115 +1,201 @@ import pytest -from starknet_py.contract import Contract +from starknet_py.contract import ( + Contract, + PreparedFunctionInvokeV1, + PreparedFunctionInvokeV3, +) 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.tests.e2e.fixtures.constants import MAX_FEE +from starknet_py.net.client_models import Call, ResourceBounds +from starknet_py.net.models import InvokeV1, InvokeV3 +from starknet_py.tests.e2e.fixtures.constants import ( + MAX_FEE, + MAX_RESOURCE_BOUNDS, + MAX_RESOURCE_BOUNDS_L1, +) @pytest.mark.asyncio -async def test_max_fee_is_set_in_sent_invoke(map_contract): - key = 2 - value = 3 - - prepared_call = map_contract.functions["put"].prepare(key, value, max_fee=MAX_FEE) - assert prepared_call.max_fee == MAX_FEE +async def test_prepare_and_invoke_v1(map_contract): + prepared_invoke = map_contract.functions["put"].prepare_invoke_v1( + key=1, value=2, max_fee=MAX_FEE + ) + assert isinstance(prepared_invoke, PreparedFunctionInvokeV1) - invocation = await prepared_call.invoke() + invocation = await prepared_invoke.invoke() + assert isinstance(invocation.invoke_transaction, InvokeV1) assert invocation.invoke_transaction.max_fee == MAX_FEE - invocation = await map_contract.functions["put"].invoke( - key, value, max_fee=MAX_FEE + 100 - ) - assert invocation.invoke_transaction.max_fee == MAX_FEE + 100 - prepared_call = map_contract.functions["put"].prepare( - key, value, max_fee=MAX_FEE + 200 +@pytest.mark.asyncio +async def test_prepare_and_invoke_v3(map_contract): + prepared_invoke = map_contract.functions["put"].prepare_invoke_v3( + key=1, value=2, l1_resource_bounds=MAX_RESOURCE_BOUNDS_L1 ) - assert prepared_call.max_fee == MAX_FEE + 200 + assert isinstance(prepared_invoke, PreparedFunctionInvokeV3) - invocation = await prepared_call.invoke(max_fee=MAX_FEE + 300) - assert invocation.invoke_transaction.max_fee == MAX_FEE + 300 + invocation = await prepared_invoke.invoke() + assert isinstance(invocation.invoke_transaction, InvokeV3) + assert invocation.invoke_transaction.resource_bounds == MAX_RESOURCE_BOUNDS @pytest.mark.asyncio -async def test_auto_fee_estimation(map_contract): - key = 2 - value = 3 +async def test_invoke_v1(map_contract): + invocation = await map_contract.functions["put"].invoke_v1( + key=1, value=2, max_fee=MAX_FEE + ) + assert isinstance(invocation.invoke_transaction, InvokeV1) + assert invocation.invoke_transaction.max_fee == MAX_FEE - prepared_call = map_contract.functions["put"].prepare(key, value) - invocation = await prepared_call.invoke(auto_estimate=True) +@pytest.mark.asyncio +async def test_invoke_v3(map_contract): + invocation = await map_contract.functions["put"].invoke_v3( + key=1, value=2, l1_resource_bounds=MAX_RESOURCE_BOUNDS_L1 + ) + assert isinstance(invocation.invoke_transaction, InvokeV3) + assert invocation.invoke_transaction.resource_bounds == MAX_RESOURCE_BOUNDS + + +@pytest.mark.asyncio +async def test_auto_fee_estimation_v1(map_contract): + prepared_invoke = map_contract.functions["put"].prepare_invoke_v1(key=1, value=2) + assert isinstance(prepared_invoke, PreparedFunctionInvokeV1) + + invocation = await prepared_invoke.invoke(auto_estimate=True) + assert isinstance(invocation.invoke_transaction, InvokeV1) assert invocation.invoke_transaction.max_fee is not None @pytest.mark.asyncio -async def test_throws_invoke_without_max_fee(map_contract): +async def test_auto_fee_estimation_v3(map_contract): + prepared_invoke = map_contract.functions["put"].prepare_invoke_v3(key=1, value=2) + assert isinstance(prepared_invoke, PreparedFunctionInvokeV3) + + invocation = await prepared_invoke.invoke(auto_estimate=True) + assert isinstance(invocation.invoke_transaction, InvokeV3) + assert invocation.invoke_transaction.resource_bounds is not None + + +@pytest.mark.asyncio +async def test_throws_invoke_v1_without_max_fee(map_contract): error_message = "Argument max_fee must be specified when invoking a transaction." with pytest.raises(ValueError, match=error_message): - await map_contract.functions["put"].invoke(2, 3) + await map_contract.functions["put"].invoke_v1(2, 3) + + +@pytest.mark.asyncio +async def test_throws_invoke_v3_without_resource_bounds(map_contract): + error_message = ( + "One of arguments: " + "l1_resource_bounds or auto_estimate must be specified when invoking a transaction." + ) + + with pytest.raises(ValueError, match=error_message): + await map_contract.functions["put"].invoke_v3(2, 3) @pytest.mark.asyncio -async def test_throws_prepared_call_invoke_without_max_fee(map_contract): +async def test_throws_prepared_invoke_v1_without_max_fee(map_contract): error_message = "Argument max_fee must be specified when invoking a transaction." - prepared_call = map_contract.functions["put"].prepare(2, 3) + prepared_invoke = map_contract.functions["put"].prepare_invoke_v1(2, 3) + assert isinstance(prepared_invoke, PreparedFunctionInvokeV1) + with pytest.raises(ValueError, match=error_message): - await prepared_call.invoke() + await prepared_invoke.invoke() @pytest.mark.asyncio -async def test_throws_prepared_call_with_max_fee_invoke_with_auto_estimate( +async def test_throws_prepared_invoke_v3_without_resource_bounds(map_contract): + error_message = ( + "One of arguments: " + "l1_resource_bounds or auto_estimate must be specified when invoking a transaction." + ) + + prepared_invoke = map_contract.functions["put"].prepare_invoke_v3(2, 3) + assert isinstance(prepared_invoke, PreparedFunctionInvokeV3) + + with pytest.raises(ValueError, match=error_message): + await prepared_invoke.invoke() + + +@pytest.mark.asyncio +async def test_throws_prepared_invoke_v1_with_max_fee_and_invoke_with_auto_estimate( map_contract, ): error_message = "Arguments max_fee and auto_estimate are mutually exclusive." - invocation = map_contract.functions["put"].prepare(2, 3, max_fee=2000) + invocation = map_contract.functions["put"].prepare_invoke_v1( + key=2, value=3, max_fee=2000 + ) with pytest.raises(ValueError, match=error_message): await invocation.invoke(auto_estimate=True) @pytest.mark.asyncio -async def test_throws_on_call_without_max_fee(map_contract): +async def test_throws_when_invoke_v1_with_max_fee_and_auto_estimate(map_contract): error_message = "Arguments max_fee and auto_estimate are mutually exclusive." - prepared_call = map_contract.functions["put"].prepare(2, 3) + prepared_invoke = map_contract.functions["put"].prepare_invoke_v1(key=2, value=3) with pytest.raises(ValueError, match=error_message): - await prepared_call.invoke(max_fee=10, auto_estimate=True) + await prepared_invoke.invoke(max_fee=10, auto_estimate=True) @pytest.mark.asyncio async def test_latest_max_fee_takes_precedence(map_contract): - key = 2 - value = 3 - - prepared_function = map_contract.functions["put"].prepare( - key, value, max_fee=MAX_FEE + prepared_function = map_contract.functions["put"].prepare_invoke_v1( + key=1, value=2, max_fee=MAX_FEE ) invocation = await prepared_function.invoke(max_fee=MAX_FEE + 30) + assert isinstance(invocation.invoke_transaction, InvokeV1) assert invocation.invoke_transaction.max_fee == MAX_FEE + 30 @pytest.mark.asyncio -async def test_prepare_without_max_fee(map_contract): - key = 2 - value = 3 +async def test_latest_resource_bounds_take_precedence(map_contract): + prepared_function = map_contract.functions["put"].prepare_invoke_v3( + key=1, value=2, l1_resource_bounds=MAX_RESOURCE_BOUNDS_L1 + ) + + updated_resource_bounds = ResourceBounds( + max_amount=MAX_RESOURCE_BOUNDS_L1.max_amount + 100, + max_price_per_unit=MAX_RESOURCE_BOUNDS_L1.max_price_per_unit + 200, + ) + invocation = await prepared_function.invoke( + l1_resource_bounds=updated_resource_bounds + ) + + assert isinstance(invocation.invoke_transaction, InvokeV3) + assert ( + invocation.invoke_transaction.resource_bounds.l1_gas == updated_resource_bounds + ) + assert ( + invocation.invoke_transaction.resource_bounds.l2_gas + == ResourceBounds.init_with_zeros() + ) + - prepared_call = map_contract.functions["put"].prepare(key, value) +@pytest.mark.asyncio +async def test_prepare_without_max_fee(map_contract): + prepared_invoke = map_contract.functions["put"].prepare_invoke_v1(key=1, value=2) - assert prepared_call.max_fee is None + assert prepared_invoke.max_fee is None @pytest.mark.asyncio @pytest.mark.parametrize("key, value", ((2, 13), (412312, 32134), (12345, 3567))) -async def test_invoke_and_call(key, value, map_contract): - invocation = await map_contract.functions["put"].invoke(key, value, max_fee=MAX_FEE) +async def test_invoke_v1_and_call(key, value, map_contract): + invocation = await map_contract.functions["put"].invoke_v1( + key, value, max_fee=MAX_FEE + ) await invocation.wait_for_acceptance() - (response,) = await map_contract.functions["get"].call(key) + assert isinstance(invocation.invoke_transaction, InvokeV1) + (response,) = await map_contract.functions["get"].call(key) assert response == value @@ -128,31 +214,29 @@ async def test_call_uninitialized_contract(client): @pytest.mark.asyncio async def test_wait_for_tx(client, map_contract): - transaction = await map_contract.functions["put"].invoke( + transaction = await map_contract.functions["put"].invoke_v1( key=10, value=20, max_fee=MAX_FEE ) await client.wait_for_tx(transaction.hash) @pytest.mark.asyncio -async def test_error_when_invoking_without_account(client, map_contract): +async def test_error_when_prepare_without_account(client, map_contract): contract = await Contract.from_address(map_contract.address, client) with pytest.raises( ValueError, match="Contract instance was created without providing an Account.", ): - await contract.functions["put"].prepare(key=10, value=10).invoke( - max_fee=MAX_FEE - ) + contract.functions["put"].prepare_invoke_v1(key=10, value=10) @pytest.mark.asyncio -async def test_error_when_estimating_fee_while_not_using_account(client, map_contract): +async def test_error_when_invoke_without_account(client, map_contract): contract = await Contract.from_address(map_contract.address, client) with pytest.raises( ValueError, match="Contract instance was created without providing an Account.", ): - await contract.functions["put"].prepare(key=10, value=10).estimate_fee() + await contract.functions["put"].invoke_v1(key=10, value=10) From a5936ef34427791da8bb79ae8f8f10a8ee6984c7 Mon Sep 17 00:00:00 2001 From: Dariusz Doktorski Date: Wed, 24 Jan 2024 12:48:46 +0100 Subject: [PATCH 05/24] Adjust remaining tests --- starknet_py/net/account/account_test.py | 2 +- starknet_py/tests/e2e/account/account_test.py | 8 ++++---- starknet_py/tests/e2e/cairo1v2_test.py | 20 ++++++++++--------- starknet_py/tests/e2e/client/client_test.py | 10 +++++++--- .../fixtures/prepare_net_for_gateway_test.py | 2 +- .../tests/e2e/client/fixtures/transactions.py | 2 +- .../tests/e2e/client/full_node_test.py | 16 +++++++-------- .../v1_interaction_test.py | 4 ++-- .../test_deploy_prefunded_account.py | 2 +- .../code_examples/test_contract_function.py | 6 ++++-- .../test_prepared_function_call.py | 8 +++++--- .../test_contract_account_compatibility.py | 2 +- .../test_contract_client_compatibility.py | 4 ++-- .../docs/guide/test_deploying_in_multicall.py | 2 +- .../e2e/docs/guide/test_full_node_client.py | 2 +- .../docs/guide/test_sign_for_fee_estimate.py | 2 +- .../guide/test_using_existing_contracts.py | 8 ++++---- .../docs/quickstart/test_synchronous_api.py | 2 +- .../e2e/docs/quickstart/test_using_account.py | 6 +++--- .../docs/quickstart/test_using_contract.py | 2 +- .../quickstart/test_using_full_node_client.py | 2 +- starknet_py/tests/e2e/proxy/proxy_test.py | 2 +- .../e2e/tests_on_networks/account_test.py | 2 +- starknet_py/tests/e2e/utils.py | 4 ++-- 24 files changed, 65 insertions(+), 55 deletions(-) diff --git a/starknet_py/net/account/account_test.py b/starknet_py/net/account/account_test.py index a6652e47f..a8132dcfa 100644 --- a/starknet_py/net/account/account_test.py +++ b/starknet_py/net/account/account_test.py @@ -42,7 +42,7 @@ async def test_account_get_balance(account, map_contract): balance = await account.get_balance() block = await account.client.get_block(block_number="latest") - await map_contract.functions["put"].invoke(key=10, value=10, max_fee=MAX_FEE) + await map_contract.functions["put"].invoke_v1(key=10, value=10, max_fee=MAX_FEE) new_balance = await account.get_balance() old_balance = await account.get_balance(block_number=block.block_number) diff --git a/starknet_py/tests/e2e/account/account_test.py b/starknet_py/tests/e2e/account/account_test.py index 0abe0c5d8..01119172d 100644 --- a/starknet_py/tests/e2e/account/account_test.py +++ b/starknet_py/tests/e2e/account/account_test.py @@ -70,7 +70,7 @@ async def test_estimated_fee_greater_than_zero(account, erc20_contract): estimated_fee = ( await erc20_contract.functions["balanceOf"] - .prepare("1234", max_fee=0) + .prepare_invoke_v1("1234", max_fee=0) .estimate_fee(block_hash="latest") ) @@ -101,8 +101,8 @@ async def test_estimate_fee_for_declare_transaction(account, map_compiled_contra @pytest.mark.parametrize("key, val", [(20, 20), (30, 30)]) async def test_sending_multicall(account, map_contract, key, val): calls = [ - map_contract.functions["put"].prepare(key=10, value=10), - map_contract.functions["put"].prepare(key=key, value=val), + map_contract.functions["put"].prepare_invoke_v1(key=10, value=10), + map_contract.functions["put"].prepare_invoke_v1(key=key, value=val), ] res = await account.execute(calls=calls, max_fee=int(1e20)) @@ -118,7 +118,7 @@ async def test_rejection_reason_in_transaction_receipt(map_contract): 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) + await map_contract.functions["put"].invoke_v1(key=10, value=20, max_fee=1) def test_sign_and_verify_offchain_message_fail(account, typed_data): diff --git a/starknet_py/tests/e2e/cairo1v2_test.py b/starknet_py/tests/e2e/cairo1v2_test.py index da10fff8b..22bbf006d 100644 --- a/starknet_py/tests/e2e/cairo1v2_test.py +++ b/starknet_py/tests/e2e/cairo1v2_test.py @@ -50,12 +50,12 @@ async def test_deploy_cairo2(contract): @pytest.mark.asyncio async def test_cairo2_interaction(contract): - invoke_res = await contract.functions["increase_balance"].invoke( + invoke_res = await contract.functions["increase_balance"].invoke_v1( amount=100, auto_estimate=True ) await invoke_res.wait_for_acceptance() - invoke_res = await contract.functions["increase_balance"].invoke( + invoke_res = await contract.functions["increase_balance"].invoke_v1( amount=100, auto_estimate=True ) await invoke_res.wait_for_acceptance() @@ -66,7 +66,7 @@ async def test_cairo2_interaction(contract): @pytest.mark.asyncio async def test_cairo2_interaction2(contract): - invoke_res = await contract.functions["increase_balance_u8"].invoke( + invoke_res = await contract.functions["increase_balance_u8"].invoke_v1( 255, auto_estimate=True ) await invoke_res.wait_for_acceptance() @@ -95,7 +95,7 @@ async def test_cairo2_u256(contract): @pytest.mark.asyncio async def test_cairo2_contract_address(contract): - invoke_res = await contract.functions["set_ca"].invoke( + invoke_res = await contract.functions["set_ca"].invoke_v1( address=contract.account.address, auto_estimate=True ) await invoke_res.wait_for_acceptance() @@ -106,7 +106,7 @@ async def test_cairo2_contract_address(contract): @pytest.mark.asyncio async def test_cairo2_interaction3(contract): - invoke_res = await contract.functions["increase_balance"].invoke( + invoke_res = await contract.functions["increase_balance"].invoke_v1( 100, auto_estimate=True ) await invoke_res.wait_for_acceptance() @@ -115,7 +115,7 @@ async def test_cairo2_interaction3(contract): storage = await contract.client.get_storage_at(contract.address, key) assert storage == balance - invoke_res = await contract.functions["set_ca"].invoke( + invoke_res = await contract.functions["set_ca"].invoke_v1( contract.account.address, auto_estimate=True ) await invoke_res.wait_for_acceptance() @@ -124,14 +124,16 @@ async def test_cairo2_interaction3(contract): storage = await contract.client.get_storage_at(contract.address, key) assert storage == ca - invoke_res = await contract.functions["set_status"].invoke(True, auto_estimate=True) + invoke_res = await contract.functions["set_status"].invoke_v1( + True, auto_estimate=True + ) await invoke_res.wait_for_acceptance() (status,) = await contract.functions["get_status"].call() key = get_storage_var_address("status") storage = await contract.client.get_storage_at(contract.address, key) assert storage == status - invoke_res = await contract.functions["set_user1"].invoke( + invoke_res = await contract.functions["set_user1"].invoke_v1( { "address": contract.account.address, "is_claimed": True, @@ -169,7 +171,7 @@ async def test_cairo2_echo_struct(contract): @pytest.mark.asyncio async def test_cairo2_echo_complex_struct(contract): - invoke_result = await contract.functions["set_bet"].invoke(auto_estimate=True) + invoke_result = await contract.functions["set_bet"].invoke_v1(auto_estimate=True) await invoke_result.wait_for_acceptance() (bet,) = await contract.functions["get_bet"].call(1) diff --git a/starknet_py/tests/e2e/client/client_test.py b/starknet_py/tests/e2e/client/client_test.py index 2de408849..c7edfe6cf 100644 --- a/starknet_py/tests/e2e/client/client_test.py +++ b/starknet_py/tests/e2e/client/client_test.py @@ -256,7 +256,9 @@ 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) + prepared_function_call = map_contract.functions["put"].prepare_invoke_v1( + key=73, value=12 + ) signed_invoke = await account.sign_invoke_v1_transaction( calls=prepared_function_call, max_fee=MAX_FEE ) @@ -394,7 +396,7 @@ async def test_custom_session_client(map_contract, network): tx_hash = ( await ( - await map_contract.functions["put"].invoke( + await map_contract.functions["put"].invoke_v1( key=10, value=20, max_fee=MAX_FEE ) ).wait_for_acceptance() @@ -480,7 +482,9 @@ async def test_state_update_storage_diffs( client, map_contract, ): - resp = await map_contract.functions["put"].invoke(key=10, value=20, max_fee=MAX_FEE) + resp = await map_contract.functions["put"].invoke_v1( + key=10, value=20, max_fee=MAX_FEE + ) await resp.wait_for_acceptance() state_update = await client.get_state_update() diff --git a/starknet_py/tests/e2e/client/fixtures/prepare_net_for_gateway_test.py b/starknet_py/tests/e2e/client/fixtures/prepare_net_for_gateway_test.py index 4c82b708b..8031f4efd 100755 --- a/starknet_py/tests/e2e/client/fixtures/prepare_net_for_gateway_test.py +++ b/starknet_py/tests/e2e/client/fixtures/prepare_net_for_gateway_test.py @@ -43,7 +43,7 @@ async def prepare_net_for_tests( contract = deploy_result.deployed_contract - invoke_res = await contract.functions["increase_balance"].invoke( + invoke_res = await contract.functions["increase_balance"].invoke_v1( amount=1234, max_fee=int(1e20) ) await invoke_res.wait_for_acceptance() diff --git a/starknet_py/tests/e2e/client/fixtures/transactions.py b/starknet_py/tests/e2e/client/fixtures/transactions.py index c367acd38..637b5d344 100644 --- a/starknet_py/tests/e2e/client/fixtures/transactions.py +++ b/starknet_py/tests/e2e/client/fixtures/transactions.py @@ -115,7 +115,7 @@ async def replaced_class(account: Account, map_class_hash: int) -> Tuple[int, in contract = deploy_result.deployed_contract resp = await ( - await contract.functions["replace_implementation"].invoke( + await contract.functions["replace_implementation"].invoke_v1( new_class=map_class_hash, max_fee=MAX_FEE ) ).wait_for_acceptance() diff --git a/starknet_py/tests/e2e/client/full_node_test.py b/starknet_py/tests/e2e/client/full_node_test.py index ec08704e8..1d3e943da 100644 --- a/starknet_py/tests/e2e/client/full_node_test.py +++ b/starknet_py/tests/e2e/client/full_node_test.py @@ -152,7 +152,7 @@ async def test_get_events_without_following_continuation_token( simple_storage_with_event_contract: Contract, ): for i in range(4): - await simple_storage_with_event_contract.functions[FUNCTION_ONE_NAME].invoke( + await simple_storage_with_event_contract.functions[FUNCTION_ONE_NAME].invoke_v1( i, i, auto_estimate=True ) @@ -178,7 +178,7 @@ async def test_get_events_follow_continuation_token( ): total_invokes = 2 for i in range(total_invokes): - await simple_storage_with_event_contract.functions[FUNCTION_ONE_NAME].invoke( + await simple_storage_with_event_contract.functions[FUNCTION_ONE_NAME].invoke_v1( i, i + 1, auto_estimate=True ) @@ -201,7 +201,7 @@ async def test_get_events_nonexistent_event_name( client, simple_storage_with_event_contract: Contract, ): - await simple_storage_with_event_contract.functions[FUNCTION_ONE_NAME].invoke( + await simple_storage_with_event_contract.functions[FUNCTION_ONE_NAME].invoke_v1( 1, 1, auto_estimate=True ) @@ -227,11 +227,11 @@ async def test_get_events_with_two_events( invokes_of_one = 1 invokes_of_two = 2 invokes_of_all = invokes_of_one + invokes_of_two - await simple_storage_with_event_contract.functions[FUNCTION_ONE_NAME].invoke( + await simple_storage_with_event_contract.functions[FUNCTION_ONE_NAME].invoke_v1( 1, 2, auto_estimate=True ) for i in range(invokes_of_two): - await simple_storage_with_event_contract.functions[FUNCTION_TWO_NAME].invoke( + await simple_storage_with_event_contract.functions[FUNCTION_TWO_NAME].invoke_v1( i, i + 1, auto_estimate=True ) @@ -274,7 +274,7 @@ async def test_get_events_start_from_continuation_token( simple_storage_with_event_contract: Contract, ): for i in range(5): - await simple_storage_with_event_contract.functions[FUNCTION_ONE_NAME].invoke( + await simple_storage_with_event_contract.functions[FUNCTION_ONE_NAME].invoke_v1( i, i + 1, auto_estimate=True ) @@ -302,10 +302,10 @@ async def test_get_events_no_params( ): default_chunk_size = 1 for i in range(3): - await simple_storage_with_event_contract.functions[FUNCTION_ONE_NAME].invoke( + await simple_storage_with_event_contract.functions[FUNCTION_ONE_NAME].invoke_v1( i, i + 1, auto_estimate=True ) - await simple_storage_with_event_contract.functions[FUNCTION_TWO_NAME].invoke( + await simple_storage_with_event_contract.functions[FUNCTION_TWO_NAME].invoke_v1( i, i + 1, auto_estimate=True ) events_response = await client.get_events() 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 23281e10a..67ecc92b1 100644 --- a/starknet_py/tests/e2e/contract_interaction/v1_interaction_test.py +++ b/starknet_py/tests/e2e/contract_interaction/v1_interaction_test.py @@ -40,7 +40,7 @@ async def test_general_v1_interaction(account, cairo1_erc20_class_hash: int): transfer_amount = 10 await ( - await erc20.functions["transfer"].invoke( + await erc20.functions["transfer"].invoke_v1( recipient=0x11, amount=transfer_amount, max_fee=MAX_FEE ) ).wait_for_acceptance() @@ -70,7 +70,7 @@ async def test_serializing_struct(account, cairo1_token_bridge_class_hash: int): ) await ( - await bridge.functions["set_l1_bridge"].invoke( + await bridge.functions["set_l1_bridge"].invoke_v1( l1_bridge_address={"address": 0x11}, max_fee=MAX_FEE ) ).wait_for_acceptance() 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 2aad15618..ec3474357 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 @@ -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 eth_fee_contract.functions["transfer"].invoke( + res = await eth_fee_contract.functions["transfer"].invoke_v1( 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_contract_function.py b/starknet_py/tests/e2e/docs/code_examples/test_contract_function.py index 971f9120d..84d49e81f 100644 --- a/starknet_py/tests/e2e/docs/code_examples/test_contract_function.py +++ b/starknet_py/tests/e2e/docs/code_examples/test_contract_function.py @@ -6,7 +6,9 @@ def test_prepare(map_contract: Contract): # docs-start: prepare - prepared_function_call = map_contract.functions["put"].prepare(key=10, value=20) + prepared_function_call = map_contract.functions["put"].prepare_invoke_v1( + key=10, value=20 + ) # docs-end: prepare @@ -21,7 +23,7 @@ async def test_call(map_contract: Contract): def test_invoke(map_contract: Contract): # docs-start: invoke - invoke_result = map_contract.functions["put"].invoke( + invoke_result = map_contract.functions["put"].invoke_v1( key=10, value=20, max_fee=int(1e15) ) # docs-end: invoke diff --git a/starknet_py/tests/e2e/docs/code_examples/test_prepared_function_call.py b/starknet_py/tests/e2e/docs/code_examples/test_prepared_function_call.py index 290910ef5..c91f7a722 100644 --- a/starknet_py/tests/e2e/docs/code_examples/test_prepared_function_call.py +++ b/starknet_py/tests/e2e/docs/code_examples/test_prepared_function_call.py @@ -6,7 +6,7 @@ @pytest.mark.asyncio async def test_call_raw(map_contract: Contract): - prepared_function_call = map_contract.functions["get"].prepare(key=10) + prepared_function_call = map_contract.functions["get"].prepare_call(key=10) # docs-start: call_raw raw_response = await prepared_function_call.call_raw(block_number="latest") # or @@ -16,7 +16,7 @@ async def test_call_raw(map_contract: Contract): @pytest.mark.asyncio async def test_call(map_contract: Contract): - prepared_function_call = map_contract.functions["get"].prepare(key=10) + prepared_function_call = map_contract.functions["get"].prepare_call(key=10) # docs-start: call response = await prepared_function_call.call(block_number="latest") # or @@ -26,7 +26,9 @@ async def test_call(map_contract: Contract): @pytest.mark.asyncio async def test_invoke(map_contract: Contract): - prepared_function_call = map_contract.functions["put"].prepare(key=10, value=20) + prepared_function_call = map_contract.functions["put"].prepare_invoke_v1( + key=10, value=20 + ) # docs-start: invoke invoke_result = await prepared_function_call.invoke(max_fee=int(1e15)) # docs-end: invoke 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 53535096c..e4b9d8f06 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 @@ -13,7 +13,7 @@ async def test_create_invoke_from_contract(map_contract, account): from starknet_py.net.client_models import Call # Prepare a call through Contract - call = contract.functions["put"].prepare(key=20, value=30) + call = contract.functions["put"].prepare_invoke_v1(key=20, value=30) assert issubclass(type(call), Call) # Crate an Invoke transaction from call diff --git a/starknet_py/tests/e2e/docs/guide/test_contract_client_compatibility.py b/starknet_py/tests/e2e/docs/guide/test_contract_client_compatibility.py index 356607bd4..37c56f22e 100644 --- a/starknet_py/tests/e2e/docs/guide/test_contract_client_compatibility.py +++ b/starknet_py/tests/e2e/docs/guide/test_contract_client_compatibility.py @@ -7,7 +7,7 @@ async def test_create_call_from_contract(map_contract, account): contract = map_contract client = account.client - res = await map_contract.functions["put"].invoke( + res = await map_contract.functions["put"].invoke_v1( key=1234, value=9999, auto_estimate=True ) await res.wait_for_acceptance() @@ -16,7 +16,7 @@ async def test_create_call_from_contract(map_contract, account): from starknet_py.net.client_models import Call # Prepare a call through Contract - call = contract.functions["get"].prepare(key=1234) + call = contract.functions["get"].prepare_invoke_v1(key=1234) assert issubclass(type(call), Call) # Use call directly through Client 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 426a56163..533f0392a 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 @@ -26,7 +26,7 @@ async def test_deploying_in_multicall(account, map_class_hash, map_compiled_cont map_contract = Contract(address=address, abi=map_abi, provider=account) # And now we can prepare a call - put_call = map_contract.functions["put"].prepare(key=10, value=20) + put_call = map_contract.functions["put"].prepare_call(key=10, value=20) # After that multicall transaction can be sent # Note that `deploy_call` and `put_call` are two regular calls! 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 82817111f..49ae6f53f 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 @@ -12,7 +12,7 @@ async def test_full_node_client(client, map_contract): client = FullNodeClient(node_url=node_url) # docs: end - await map_contract.functions["put"].prepare(key=10, value=10).invoke( + await map_contract.functions["put"].prepare_invoke_v1(key=10, value=10).invoke( max_fee=int(1e20) ) 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 bb058bdfa..896fc6dfe 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 @@ -5,7 +5,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) + call = map_contract.functions["put"].prepare_invoke_v1(key=10, value=20) transaction = await account.sign_invoke_v1_transaction(calls=call, max_fee=0) # Re-sign a transaction for fee estimation diff --git a/starknet_py/tests/e2e/docs/guide/test_using_existing_contracts.py b/starknet_py/tests/e2e/docs/guide/test_using_existing_contracts.py index f20936e9d..6180360e6 100644 --- a/starknet_py/tests/e2e/docs/guide/test_using_existing_contracts.py +++ b/starknet_py/tests/e2e/docs/guide/test_using_existing_contracts.py @@ -45,7 +45,7 @@ async def test_using_existing_contracts(account, erc20_contract): recipient = "123" # Using only positional arguments - invocation = await contract.functions["transferFrom"].invoke( + invocation = await contract.functions["transferFrom"].invoke_v1( sender, recipient, 10000, max_fee=int(1e16) ) # docs: end @@ -53,7 +53,7 @@ async def test_using_existing_contracts(account, erc20_contract): # docs: start # Using only keyword arguments - invocation = await contract.functions["transferFrom"].invoke( + invocation = await contract.functions["transferFrom"].invoke_v1( sender=sender, recipient=recipient, amount=10000, max_fee=int(1e16) ) # docs: end @@ -61,7 +61,7 @@ async def test_using_existing_contracts(account, erc20_contract): # docs: start # Mixing positional with keyword arguments - invocation = await contract.functions["transferFrom"].invoke( + invocation = await contract.functions["transferFrom"].invoke_v1( sender, recipient, amount=10000, max_fee=int(1e16) ) # docs: end @@ -70,7 +70,7 @@ async def test_using_existing_contracts(account, erc20_contract): # Creating a PreparedFunctionCall - creates a function call with arguments - useful for signing transactions and # specifying additional options - transfer = contract.functions["transferFrom"].prepare( + transfer = contract.functions["transferFrom"].prepare_invoke_v1( sender, recipient, amount=10000, max_fee=int(1e16) ) invocation = await transfer.invoke() diff --git a/starknet_py/tests/e2e/docs/quickstart/test_synchronous_api.py b/starknet_py/tests/e2e/docs/quickstart/test_synchronous_api.py index f5c5b3e06..ab3a9a322 100644 --- a/starknet_py/tests/e2e/docs/quickstart/test_synchronous_api.py +++ b/starknet_py/tests/e2e/docs/quickstart/test_synchronous_api.py @@ -17,7 +17,7 @@ def test_synchronous_api(account, map_contract): key = 1234 contract = Contract.from_address_sync(address=contract_address, provider=account) - invocation = contract.functions["put"].invoke_sync(key, 7, max_fee=int(1e16)) + invocation = contract.functions["put"].invoke_v1_sync(key, 7, max_fee=int(1e16)) invocation.wait_for_acceptance_sync() (saved,) = contract.functions["get"].call_sync(key) # 7 diff --git a/starknet_py/tests/e2e/docs/quickstart/test_using_account.py b/starknet_py/tests/e2e/docs/quickstart/test_using_account.py index 38d3afa9c..4322f18a1 100644 --- a/starknet_py/tests/e2e/docs/quickstart/test_using_account.py +++ b/starknet_py/tests/e2e/docs/quickstart/test_using_account.py @@ -30,7 +30,7 @@ async def test_using_account(account, map_compiled_contract): # Adds a transaction to mutate the state of k-v store. The call goes through account proxy, because we've used # Account to create the contract object await ( - await map_contract.functions["put"].invoke(k, v, max_fee=int(1e16)) + await map_contract.functions["put"].invoke_v1(k, v, max_fee=int(1e16)) ).wait_for_acceptance() # Retrieves the value, which is equal to 4324 in this case @@ -40,8 +40,8 @@ async def test_using_account(account, map_compiled_contract): # Creates a list of prepared function calls calls = [ - map_contract.functions["put"].prepare(key=10, value=20), - map_contract.functions["put"].prepare(key=30, value=40), + map_contract.functions["put"].prepare_invoke_v1(key=10, value=20), + map_contract.functions["put"].prepare_invoke_v1(key=30, value=40), ] # Executes only one transaction with prepared calls diff --git a/starknet_py/tests/e2e/docs/quickstart/test_using_contract.py b/starknet_py/tests/e2e/docs/quickstart/test_using_contract.py index c5850cc0f..1203e1298 100644 --- a/starknet_py/tests/e2e/docs/quickstart/test_using_contract.py +++ b/starknet_py/tests/e2e/docs/quickstart/test_using_contract.py @@ -38,7 +38,7 @@ async def test_using_contract(account, map_contract): # All exposed functions are available at contract.functions. # Here we invoke a function, creating a new transaction. - invocation = await contract.functions["put"].invoke(key, 7, max_fee=int(1e16)) + invocation = await contract.functions["put"].invoke_v1(key, 7, max_fee=int(1e16)) # Invocation returns InvokeResult object. It exposes a helper for waiting until transaction is accepted. await invocation.wait_for_acceptance() 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 89ba993c4..07ce5d167 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 @@ -16,7 +16,7 @@ async def test_using_full_node_client(client, map_contract): client = FullNodeClient(node_url=node_url) # docs: end - await map_contract.functions["put"].prepare(key=10, value=10).invoke( + await map_contract.functions["put"].prepare_invoke_v1(key=10, value=10).invoke( max_fee=int(1e20) ) diff --git a/starknet_py/tests/e2e/proxy/proxy_test.py b/starknet_py/tests/e2e/proxy/proxy_test.py index 53809aa58..043dbff40 100644 --- a/starknet_py/tests/e2e/proxy/proxy_test.py +++ b/starknet_py/tests/e2e/proxy/proxy_test.py @@ -15,7 +15,7 @@ async def is_map_working_properly(map_contract: Contract, key: int, val: int) -> bool: """Put (key, val) into map_contract's storage and check if value under the key is val""" await ( - await map_contract.functions["put"].invoke(key, val, max_fee=int(1e16)) + await map_contract.functions["put"].invoke_v1(key, val, max_fee=int(1e16)) ).wait_for_acceptance() (result,) = await map_contract.functions["get"].call(key=key) return result == val 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 42a34ef7f..90053ecff 100644 --- a/starknet_py/tests/e2e/tests_on_networks/account_test.py +++ b/starknet_py/tests/e2e/tests_on_networks/account_test.py @@ -18,7 +18,7 @@ async def test_sign_invoke_tx_for_fee_estimation(account_goerli_integration): address=MAP_CONTRACT_ADDRESS_GOERLI_INTEGRATION, provider=account ) - call = map_contract.functions["put"].prepare(key=40, value=50) + call = map_contract.functions["put"].prepare_invoke_v1(key=40, value=50) transaction = await account.sign_invoke_v1_transaction(calls=call, max_fee=MAX_FEE) estimate_fee_transaction = await account.sign_for_fee_estimate(transaction) diff --git a/starknet_py/tests/e2e/utils.py b/starknet_py/tests/e2e/utils.py index bb006673b..cdda24975 100644 --- a/starknet_py/tests/e2e/utils.py +++ b/starknet_py/tests/e2e/utils.py @@ -47,12 +47,12 @@ async def get_deploy_account_details( deployer_address=0, ) - transfer_wei_res = await eth_fee_contract.functions["transfer"].invoke( + transfer_wei_res = await eth_fee_contract.functions["transfer"].invoke_v1( recipient=address, amount=int(1e19), max_fee=MAX_FEE ) await transfer_wei_res.wait_for_acceptance() - transfer_fri_res = await strk_fee_contract.functions["transfer"].invoke( + transfer_fri_res = await strk_fee_contract.functions["transfer"].invoke_v1( recipient=address, amount=int(1e19), max_fee=MAX_FEE ) await transfer_fri_res.wait_for_acceptance() From 9009d6c149769922bdb36e6b90445d1e678b29f1 Mon Sep 17 00:00:00 2001 From: Dariusz Doktorski Date: Wed, 24 Jan 2024 15:28:03 +0100 Subject: [PATCH 06/24] Break down `Contract.declare` into v1, v2 and v3 --- starknet_py/contract.py | 151 +++++++++++++++++++++++++--------- starknet_py/contract_utils.py | 23 ++++++ 2 files changed, 135 insertions(+), 39 deletions(-) create mode 100644 starknet_py/contract_utils.py diff --git a/starknet_py/contract.py b/starknet_py/contract.py index 8f3fdcc16..02a0a1adf 100644 --- a/starknet_py/contract.py +++ b/starknet_py/contract.py @@ -22,21 +22,17 @@ INTERFACE_ENTRY, L1_HANDLER_ENTRY, ) -from starknet_py.common import ( - create_casm_class, - create_compiled_contract, - create_sierra_compiled_contract, -) +from starknet_py.common import create_compiled_contract, create_sierra_compiled_contract from starknet_py.constants import DEFAULT_DEPLOYER_ADDRESS +from starknet_py.contract_utils import _extract_compiled_class_hash from starknet_py.hash.address import compute_address -from starknet_py.hash.casm_class_hash import compute_casm_class_hash from starknet_py.hash.class_hash import compute_class_hash from starknet_py.hash.selector import get_selector_from_name 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, ResourceBounds, Tag from starknet_py.net.models import AddressRepresentation, parse_address -from starknet_py.net.models.transaction import Invoke +from starknet_py.net.models.transaction import Declare, Invoke from starknet_py.net.udc_deployer.deployer import Deployer from starknet_py.proxy.contract_abi_resolver import ( ContractAbiResolver, @@ -179,6 +175,9 @@ class DeclareResult(SentTransaction): compiled_contract: str = None # pyright: ignore """Compiled contract that was declared.""" + declare_transaction: Declare = None # pyright: ignore + """A Declare transaction that has been sent.""" + def __post_init__(self): if self._account is None: raise ValueError("Argument _account can't be None.") @@ -189,6 +188,9 @@ def __post_init__(self): if self.compiled_contract is None: raise ValueError("Argument compiled_contract can't be None.") + if self.declare_transaction is None: + raise ValueError("Argument declare_transaction can't be None.") + async def deploy( self, *, @@ -769,12 +771,50 @@ async def from_address( ) @staticmethod - async def declare( + async def declare_v1( + account: BaseAccount, + compiled_contract: str, + *, + nonce: Optional[int] = None, + max_fee: Optional[int] = None, + auto_estimate: bool = False, + ) -> DeclareResult: + """ + Declares a contract. + + :param account: BaseAccount used to sign and send declare transaction. + :param compiled_contract: String containing compiled contract. + :param nonce: Nonce of the transaction. + :param max_fee: Max amount of Wei to be paid when executing transaction. + :param auto_estimate: Use automatic fee estimation (not recommended, as it may lead to high costs). + :return: DeclareResult instance. + """ + + declare_tx = await account.sign_declare_v1_transaction( + compiled_contract=compiled_contract, + nonce=nonce, + max_fee=max_fee, + auto_estimate=auto_estimate, + ) + res = await account.client.declare(transaction=declare_tx) + + return DeclareResult( + hash=res.transaction_hash, + class_hash=res.class_hash, + compiled_contract=compiled_contract, + declare_transaction=declare_tx, + _account=account, + _client=account.client, + _cairo_version=0, + ) + + @staticmethod + async def declare_v2( account: BaseAccount, compiled_contract: str, *, compiled_contract_casm: Optional[str] = None, - casm_class_hash: Optional[int] = None, + compiled_class_hash: Optional[int] = None, nonce: Optional[int] = None, max_fee: Optional[int] = None, auto_estimate: bool = False, @@ -785,51 +825,84 @@ async def declare( :param account: BaseAccount used to sign and send declare transaction. :param compiled_contract: String containing compiled contract. :param compiled_contract_casm: String containing the content of the starknet-sierra-compile (.casm file). - Used when declaring Cairo1 contracts. - :param casm_class_hash: Hash of the compiled_contract_casm. + :param compiled_class_hash: Hash of the compiled_contract_casm. :param nonce: Nonce of the transaction. :param max_fee: Max amount of Wei to be paid when executing transaction. :param auto_estimate: Use automatic fee estimation (not recommended, as it may lead to high costs). :return: DeclareResult instance. """ - if Contract._get_cairo_version(compiled_contract) == 1: - if casm_class_hash is None and compiled_contract_casm is None: - raise ValueError( - "Cairo 1.0 contract was provided without casm_class_hash or compiled_contract_casm argument." - ) + compiled_class_hash = _extract_compiled_class_hash( + compiled_contract_casm, compiled_class_hash + ) - cairo_version = 1 - if casm_class_hash is None: - assert compiled_contract_casm is not None - casm_class_hash = compute_casm_class_hash( - create_casm_class(compiled_contract_casm) - ) + declare_tx = await account.sign_declare_v2_transaction( + compiled_contract=compiled_contract, + compiled_class_hash=compiled_class_hash, + nonce=nonce, + max_fee=max_fee, + auto_estimate=auto_estimate, + ) - declare_tx = await account.sign_declare_v2_transaction( - compiled_contract=compiled_contract, - compiled_class_hash=casm_class_hash, - nonce=nonce, - max_fee=max_fee, - auto_estimate=auto_estimate, - ) - else: - cairo_version = 0 - declare_tx = await account.sign_declare_v1_transaction( - compiled_contract=compiled_contract, - nonce=nonce, - max_fee=max_fee, - auto_estimate=auto_estimate, - ) res = await account.client.declare(transaction=declare_tx) return DeclareResult( hash=res.transaction_hash, - _client=account.client, class_hash=res.class_hash, + compiled_contract=compiled_contract, + declare_transaction=declare_tx, _account=account, + _client=account.client, + _cairo_version=1, + ) + + @staticmethod + async def declare_v3( + account: BaseAccount, + compiled_contract: str, + *, + compiled_contract_casm: Optional[str] = None, + compiled_class_hash: Optional[int] = None, + nonce: Optional[int] = None, + l1_resource_bounds: Optional[ResourceBounds] = None, + auto_estimate: bool = False, + ) -> DeclareResult: + """ + Declares a contract. + + :param account: BaseAccount used to sign and send declare transaction. + :param compiled_contract: String containing compiled contract. + :param compiled_contract_casm: String containing the content of the starknet-sierra-compile (.casm file). + :param compiled_class_hash: Hash of the compiled_contract_casm. + :param nonce: Nonce of the transaction. + :param l1_resource_bounds: Max amount and max price per unit of L1 gas (in Wei) used when executing + this transaction. + :param auto_estimate: Use automatic fee estimation (not recommended, as it may lead to high costs). + :return: DeclareResult instance. + """ + + compiled_class_hash = _extract_compiled_class_hash( + compiled_contract_casm, compiled_class_hash + ) + + declare_tx = await account.sign_declare_v3_transaction( + compiled_contract=compiled_contract, + compiled_class_hash=compiled_class_hash, + nonce=nonce, + l1_resource_bounds=l1_resource_bounds, + auto_estimate=auto_estimate, + ) + + res = await account.client.declare(transaction=declare_tx) + + return DeclareResult( + hash=res.transaction_hash, + class_hash=res.class_hash, compiled_contract=compiled_contract, - _cairo_version=cairo_version, + declare_transaction=declare_tx, + _account=account, + _client=account.client, + _cairo_version=1, ) @staticmethod diff --git a/starknet_py/contract_utils.py b/starknet_py/contract_utils.py new file mode 100644 index 000000000..f33c06d85 --- /dev/null +++ b/starknet_py/contract_utils.py @@ -0,0 +1,23 @@ +from typing import Optional + +from starknet_py.common import create_casm_class +from starknet_py.hash.casm_class_hash import compute_casm_class_hash + + +def _extract_compiled_class_hash( + compiled_contract_casm: Optional[str] = None, + compiled_class_hash: Optional[int] = None, +) -> int: + if compiled_class_hash is None and compiled_contract_casm is None: + raise ValueError( + "For Cairo 1.0 contracts, either the 'compiled_class_hash' or the 'compiled_contract_casm' " + "argument must be provided." + ) + + if compiled_class_hash is None: + assert compiled_contract_casm is not None + compiled_class_hash = compute_casm_class_hash( + create_casm_class(compiled_contract_casm) + ) + + return compiled_class_hash From 5478589d65ab9203ed2526f67a543266dbdeb8c3 Mon Sep 17 00:00:00 2001 From: Dariusz Doktorski Date: Wed, 24 Jan 2024 15:29:38 +0100 Subject: [PATCH 07/24] Adjust tests to the new `Contract` declare interface --- starknet_py/tests/e2e/block_test.py | 2 +- starknet_py/tests/e2e/cairo1v2_test.py | 2 +- .../fixtures/prepare_net_for_gateway_test.py | 2 +- .../tests/e2e/client/fixtures/transactions.py | 2 +- .../e2e/contract_interaction/declare_test.py | 64 +++++++++++++++---- .../e2e/contract_interaction/deploy_test.py | 6 +- .../e2e/docs/code_examples/test_contract.py | 2 +- .../guide/test_simple_declare_and_deploy.py | 2 +- .../test_simple_declare_and_deploy_cairo1.py | 2 +- .../e2e/docs/quickstart/test_using_account.py | 2 +- starknet_py/tests/e2e/fixtures/contracts.py | 4 +- starknet_py/tests/e2e/fixtures/proxy.py | 2 +- starknet_py/tests/e2e/proxy/proxy_test.py | 2 +- 13 files changed, 70 insertions(+), 24 deletions(-) diff --git a/starknet_py/tests/e2e/block_test.py b/starknet_py/tests/e2e/block_test.py index 3a170d979..a6e24fbb1 100644 --- a/starknet_py/tests/e2e/block_test.py +++ b/starknet_py/tests/e2e/block_test.py @@ -12,7 +12,7 @@ async def declare_contract(account: BaseAccount, compiled_contract: str): - declare_result = await Contract.declare( + declare_result = await Contract.declare_v1( account=account, compiled_contract=compiled_contract, max_fee=MAX_FEE, diff --git a/starknet_py/tests/e2e/cairo1v2_test.py b/starknet_py/tests/e2e/cairo1v2_test.py index 22bbf006d..3b755ef58 100644 --- a/starknet_py/tests/e2e/cairo1v2_test.py +++ b/starknet_py/tests/e2e/cairo1v2_test.py @@ -22,7 +22,7 @@ async def declare_deploy_hello2(account) -> Tuple[DeclareResult, DeployResult]: "hello2_compiled.casm", directory=CONTRACTS_COMPILED_V2_DIR ) - declare_result = await Contract.declare( + declare_result = await Contract.declare_v2( account=account, compiled_contract=compiled_contract, compiled_contract_casm=compiled_contract_casm, diff --git a/starknet_py/tests/e2e/client/fixtures/prepare_net_for_gateway_test.py b/starknet_py/tests/e2e/client/fixtures/prepare_net_for_gateway_test.py index 8031f4efd..2d634d271 100755 --- a/starknet_py/tests/e2e/client/fixtures/prepare_net_for_gateway_test.py +++ b/starknet_py/tests/e2e/client/fixtures/prepare_net_for_gateway_test.py @@ -30,7 +30,7 @@ async def prepare_net_for_tests( deploy_account_details: AccountToBeDeployedDetails, ) -> PreparedNetworkData: # pylint: disable=too-many-locals - declare_result = await Contract.declare( + declare_result = await Contract.declare_v1( account=account, compiled_contract=compiled_contract, max_fee=MAX_FEE ) await declare_result.wait_for_acceptance() diff --git a/starknet_py/tests/e2e/client/fixtures/transactions.py b/starknet_py/tests/e2e/client/fixtures/transactions.py index 637b5d344..3c7171ff8 100644 --- a/starknet_py/tests/e2e/client/fixtures/transactions.py +++ b/starknet_py/tests/e2e/client/fixtures/transactions.py @@ -105,7 +105,7 @@ async def replaced_class(account: Account, map_class_hash: int) -> Tuple[int, in ) declare_result = await ( - await Contract.declare(account, compiled_contract, max_fee=MAX_FEE) + await Contract.declare_v1(account, compiled_contract, max_fee=MAX_FEE) ).wait_for_acceptance() deploy_result = await ( diff --git a/starknet_py/tests/e2e/contract_interaction/declare_test.py b/starknet_py/tests/e2e/contract_interaction/declare_test.py index 504f8e03c..d8febbc8b 100644 --- a/starknet_py/tests/e2e/contract_interaction/declare_test.py +++ b/starknet_py/tests/e2e/contract_interaction/declare_test.py @@ -1,16 +1,26 @@ import pytest from starknet_py.contract import Contract -from starknet_py.tests.e2e.fixtures.constants import MAX_FEE +from starknet_py.net.models import DeclareV2, DeclareV3 +from starknet_py.tests.e2e.fixtures.constants import ( + CONTRACTS_COMPILED_V1_DIR, + CONTRACTS_COMPILED_V2_DIR, + MAX_FEE, + MAX_RESOURCE_BOUNDS_L1, +) from starknet_py.tests.e2e.fixtures.misc import read_contract @pytest.mark.asyncio -async def test_contract_declare(account): - compiled_contract = read_contract("test_contract_compiled.json") - compiled_contract_casm = read_contract("test_contract_compiled.casm") +async def test_contract_declare_v2(account): + compiled_contract = read_contract( + "test_contract_compiled.json", directory=CONTRACTS_COMPILED_V1_DIR + ) + compiled_contract_casm = read_contract( + "test_contract_compiled.casm", directory=CONTRACTS_COMPILED_V1_DIR + ) - declare_result = await Contract.declare( + declare_result = await Contract.declare_v2( account, compiled_contract=compiled_contract, compiled_contract_casm=compiled_contract_casm, @@ -18,21 +28,53 @@ async def test_contract_declare(account): ) await declare_result.wait_for_acceptance() + assert isinstance(declare_result.declare_transaction, DeclareV2) + assert isinstance(declare_result.hash, int) + assert isinstance(declare_result.class_hash, int) + assert declare_result.compiled_contract == compiled_contract + + +@pytest.mark.asyncio +async def test_contract_declare_v3(account): + compiled_contract = read_contract( + "test_contract_compiled.json", directory=CONTRACTS_COMPILED_V2_DIR + ) + compiled_contract_casm = read_contract( + "test_contract_compiled.casm", directory=CONTRACTS_COMPILED_V2_DIR + ) + + declare_result = await Contract.declare_v3( + account, + compiled_contract=compiled_contract, + compiled_contract_casm=compiled_contract_casm, + l1_resource_bounds=MAX_RESOURCE_BOUNDS_L1, + ) + await declare_result.wait_for_acceptance() + + assert isinstance(declare_result.declare_transaction, DeclareV3) assert isinstance(declare_result.hash, int) assert isinstance(declare_result.class_hash, int) assert declare_result.compiled_contract == compiled_contract @pytest.mark.asyncio -async def test_throws_when_cairo1_without_compiled_contract_casm_and_casm_class_hash( +async def test_throws_when_cairo1_without_compiled_contract_casm_and_class_hash( account, ): + error_message = ( + "For Cairo 1.0 contracts, either the 'compiled_class_hash' or the 'compiled_contract_casm' " + "argument must be provided." + ) compiled_contract = read_contract("erc20_compiled.json") - with pytest.raises( - ValueError, - match="Cairo 1.0 contract was provided without casm_class_hash or compiled_contract_casm argument.", - ): - await Contract.declare( + with pytest.raises(ValueError, match=error_message): + await Contract.declare_v2( account, compiled_contract=compiled_contract, max_fee=MAX_FEE ) + + with pytest.raises(ValueError, match=error_message): + await Contract.declare_v3( + account, + compiled_contract=compiled_contract, + l1_resource_bounds=MAX_RESOURCE_BOUNDS_L1, + ) diff --git a/starknet_py/tests/e2e/contract_interaction/deploy_test.py b/starknet_py/tests/e2e/contract_interaction/deploy_test.py index d5d50e740..52b506fd1 100644 --- a/starknet_py/tests/e2e/contract_interaction/deploy_test.py +++ b/starknet_py/tests/e2e/contract_interaction/deploy_test.py @@ -1,11 +1,13 @@ import dataclasses import json import re +from unittest.mock import Mock import pytest from starknet_py.common import create_sierra_compiled_contract from starknet_py.contract import Contract, DeclareResult +from starknet_py.net.models import DeclareV2 from starknet_py.tests.e2e.fixtures.constants import MAX_FEE from starknet_py.tests.e2e.fixtures.misc import read_contract @@ -24,6 +26,7 @@ async def test_declare_deploy( class_hash=cairo1_minimal_contract_class_hash, compiled_contract=compiled_contract, hash=0, + declare_transaction=Mock(spec=DeclareV2), ) deploy_result = await declare_result.deploy(max_fee=MAX_FEE) @@ -45,6 +48,7 @@ async def test_throws_on_wrong_abi(account, cairo1_minimal_contract_class_hash: class_hash=cairo1_minimal_contract_class_hash, compiled_contract=compiled_contract, hash=0, + declare_transaction=Mock(spec=DeclareV2), ) compiled_contract = compiled_contract.replace('"abi": [', '"abi": ') @@ -84,7 +88,7 @@ async def test_deploy_contract_flow(account, cairo1_hello_starknet_class_hash: i @pytest.mark.asyncio async def test_general_simplified_deployment_flow(account, map_compiled_contract): - declare_result = await Contract.declare( + declare_result = await Contract.declare_v1( account=account, compiled_contract=map_compiled_contract, max_fee=MAX_FEE, 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 2154b7878..fc4788736 100644 --- a/starknet_py/tests/e2e/docs/code_examples/test_contract.py +++ b/starknet_py/tests/e2e/docs/code_examples/test_contract.py @@ -53,7 +53,7 @@ async def test_from_address(account, contract_address): async def test_declare(account, custom_proxy): compiled_contract = custom_proxy # docs-start: declare - declare_result = await Contract.declare( + declare_result = await Contract.declare_v1( account=account, compiled_contract=compiled_contract, max_fee=int(1e15) ) # docs-end: declare diff --git a/starknet_py/tests/e2e/docs/guide/test_simple_declare_and_deploy.py b/starknet_py/tests/e2e/docs/guide/test_simple_declare_and_deploy.py index 49c5066cf..e528f3305 100644 --- a/starknet_py/tests/e2e/docs/guide/test_simple_declare_and_deploy.py +++ b/starknet_py/tests/e2e/docs/guide/test_simple_declare_and_deploy.py @@ -13,7 +13,7 @@ async def test_simple_declare_and_deploy(account, map_compiled_contract): # docs: start # To declare through Contract class you have to compile a contract and pass it to the Contract.declare - declare_result = await Contract.declare( + declare_result = await Contract.declare_v1( account=account, compiled_contract=compiled_contract, max_fee=int(1e16) ) # Wait for the transaction diff --git a/starknet_py/tests/e2e/docs/guide/test_simple_declare_and_deploy_cairo1.py b/starknet_py/tests/e2e/docs/guide/test_simple_declare_and_deploy_cairo1.py index 789b6a790..d45c6c606 100644 --- a/starknet_py/tests/e2e/docs/guide/test_simple_declare_and_deploy_cairo1.py +++ b/starknet_py/tests/e2e/docs/guide/test_simple_declare_and_deploy_cairo1.py @@ -15,7 +15,7 @@ async def test_simple_declare_and_deploy(account): constructor_args = {"public_key_": 0x123} # docs: start - declare_result = await Contract.declare( + declare_result = await Contract.declare_v2( account=account, compiled_contract=compiled_contract, compiled_contract_casm=compiled_contract_casm, diff --git a/starknet_py/tests/e2e/docs/quickstart/test_using_account.py b/starknet_py/tests/e2e/docs/quickstart/test_using_account.py index 4322f18a1..d7d6cec98 100644 --- a/starknet_py/tests/e2e/docs/quickstart/test_using_account.py +++ b/starknet_py/tests/e2e/docs/quickstart/test_using_account.py @@ -16,7 +16,7 @@ async def test_using_account(account, map_compiled_contract): # docs: end # docs: start # Declare and deploy an example contract which implements a simple k-v store. - declare_result = await Contract.declare( + declare_result = await Contract.declare_v1( account=account, compiled_contract=map_compiled_contract, max_fee=MAX_FEE ) await declare_result.wait_for_acceptance() diff --git a/starknet_py/tests/e2e/fixtures/contracts.py b/starknet_py/tests/e2e/fixtures/contracts.py index 632456210..f40108b5f 100644 --- a/starknet_py/tests/e2e/fixtures/contracts.py +++ b/starknet_py/tests/e2e/fixtures/contracts.py @@ -165,7 +165,7 @@ async def deployed_balance_contract( """ Declares, deploys a new balance contract and returns its instance. """ - declare_result = await Contract.declare( + declare_result = await Contract.declare_v1( account=account, compiled_contract=balance_contract, max_fee=int(1e16), @@ -196,7 +196,7 @@ async def map_contract_declare_hash( account: BaseAccount, map_compiled_contract: str, ): - declare_result = await Contract.declare( + declare_result = await Contract.declare_v1( account=account, compiled_contract=map_compiled_contract, max_fee=MAX_FEE, diff --git a/starknet_py/tests/e2e/fixtures/proxy.py b/starknet_py/tests/e2e/fixtures/proxy.py index e89eb8a8d..0cbcf7355 100644 --- a/starknet_py/tests/e2e/fixtures/proxy.py +++ b/starknet_py/tests/e2e/fixtures/proxy.py @@ -116,7 +116,7 @@ async def deploy_proxy_to_contract( declare_result = await account.client.declare(declare_tx) await account.client.wait_for_tx(declare_result.transaction_hash) - declare_proxy_result = await Contract.declare( + declare_proxy_result = await Contract.declare_v1( account=account, compiled_contract=compiled_proxy, max_fee=MAX_FEE, diff --git a/starknet_py/tests/e2e/proxy/proxy_test.py b/starknet_py/tests/e2e/proxy/proxy_test.py index 043dbff40..9d3b0003f 100644 --- a/starknet_py/tests/e2e/proxy/proxy_test.py +++ b/starknet_py/tests/e2e/proxy/proxy_test.py @@ -103,7 +103,7 @@ async def implementation_hash( async def test_contract_from_address_with_old_address_proxy( account, old_proxy, map_contract ): - declare_result = await Contract.declare( + declare_result = await Contract.declare_v1( account=account, compiled_contract=old_proxy, max_fee=MAX_FEE ) await declare_result.wait_for_acceptance() From 3bd6c713394f189fd54494edc0c1b91f25cf0ac3 Mon Sep 17 00:00:00 2001 From: Dariusz Doktorski Date: Wed, 24 Jan 2024 15:48:09 +0100 Subject: [PATCH 08/24] Move `_unpack_provider` function to `contract_utils` --- starknet_py/contract.py | 22 ++-------------------- starknet_py/contract_utils.py | 22 +++++++++++++++++++++- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/starknet_py/contract.py b/starknet_py/contract.py index 02a0a1adf..4e8cffe76 100644 --- a/starknet_py/contract.py +++ b/starknet_py/contract.py @@ -6,7 +6,7 @@ from abc import ABC, abstractmethod from dataclasses import dataclass from functools import cached_property -from typing import Dict, List, Optional, Tuple, TypeVar, Union +from typing import Dict, List, Optional, TypeVar, Union from marshmallow import ValidationError @@ -24,7 +24,7 @@ ) from starknet_py.common import create_compiled_contract, create_sierra_compiled_contract from starknet_py.constants import DEFAULT_DEPLOYER_ADDRESS -from starknet_py.contract_utils import _extract_compiled_class_hash +from starknet_py.contract_utils import _extract_compiled_class_hash, _unpack_provider from starknet_py.hash.address import compute_address from starknet_py.hash.class_hash import compute_class_hash from starknet_py.hash.selector import get_selector_from_name @@ -1054,21 +1054,3 @@ def _create_proxy_config(proxy_config) -> ProxyConfig: return ProxyConfig() proxy_arg = ProxyConfig() if proxy_config is True else proxy_config return prepare_proxy_config(proxy_arg) - - -def _unpack_provider( - provider: Union[BaseAccount, Client] -) -> Tuple[Client, Optional[BaseAccount]]: - """ - Get the client and optional account to be used by Contract. - - If provided with Client, returns this Client and None. - If provided with BaseAccount, returns underlying Client and the account. - """ - if isinstance(provider, Client): - return provider, None - - if isinstance(provider, BaseAccount): - return provider.client, provider - - raise ValueError("Argument provider is not of accepted type.") diff --git a/starknet_py/contract_utils.py b/starknet_py/contract_utils.py index f33c06d85..161d03991 100644 --- a/starknet_py/contract_utils.py +++ b/starknet_py/contract_utils.py @@ -1,7 +1,9 @@ -from typing import Optional +from typing import Optional, Tuple, Union from starknet_py.common import create_casm_class from starknet_py.hash.casm_class_hash import compute_casm_class_hash +from starknet_py.net.account.base_account import BaseAccount +from starknet_py.net.client import Client def _extract_compiled_class_hash( @@ -21,3 +23,21 @@ def _extract_compiled_class_hash( ) return compiled_class_hash + + +def _unpack_provider( + provider: Union[BaseAccount, Client] +) -> Tuple[Client, Optional[BaseAccount]]: + """ + Get the client and optional account to be used by Contract. + + If provided with Client, returns this Client and None. + If provided with BaseAccount, returns underlying Client and the account. + """ + if isinstance(provider, Client): + return provider, None + + if isinstance(provider, BaseAccount): + return provider.client, provider + + raise ValueError("Argument provider is not of accepted type.") From 99cbef0c9572bfe2aa2ac53561fc77a61d80b15e Mon Sep 17 00:00:00 2001 From: Dariusz Doktorski Date: Wed, 24 Jan 2024 16:01:55 +0100 Subject: [PATCH 09/24] Remove `_get_cairo_version` function from `Contract` class --- starknet_py/contract.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/starknet_py/contract.py b/starknet_py/contract.py index 4e8cffe76..055c02260 100644 --- a/starknet_py/contract.py +++ b/starknet_py/contract.py @@ -905,10 +905,6 @@ async def declare_v3( _cairo_version=1, ) - @staticmethod - def _get_cairo_version(compiled_contract: str) -> int: - return 1 if "sierra_program" in compiled_contract else 0 - @staticmethod async def deploy_contract( account: BaseAccount, From 0e7e0f2a83668bf6e4e508db41b78f4ac990301a Mon Sep 17 00:00:00 2001 From: Dariusz Doktorski Date: Wed, 24 Jan 2024 18:36:00 +0100 Subject: [PATCH 10/24] Split deploy contract methods in `Contract` class --- starknet_py/contract.py | 178 ++++++++++++++++++++++++++++++++-------- 1 file changed, 143 insertions(+), 35 deletions(-) diff --git a/starknet_py/contract.py b/starknet_py/contract.py index 055c02260..da4d055bf 100644 --- a/starknet_py/contract.py +++ b/starknet_py/contract.py @@ -191,7 +191,7 @@ def __post_init__(self): if self.declare_transaction is None: raise ValueError("Argument declare_transaction can't be None.") - async def deploy( + async def deploy_v1( self, *, deployer_address: AddressRepresentation = DEFAULT_DEPLOYER_ADDRESS, @@ -217,6 +217,66 @@ async def deploy( :return: DeployResult instance. """ # pylint: disable=too-many-arguments, too-many-locals + abi = self._get_abi() + + return await Contract.deploy_contract_v1( + account=self._account, + class_hash=self.class_hash, + abi=abi, + constructor_args=constructor_args, + deployer_address=deployer_address, + cairo_version=self._cairo_version, + nonce=nonce, + max_fee=max_fee, + auto_estimate=auto_estimate, + salt=salt, + unique=unique, + ) + + async def deploy_v3( + self, + *, + deployer_address: AddressRepresentation = DEFAULT_DEPLOYER_ADDRESS, + salt: Optional[int] = None, + unique: bool = True, + constructor_args: Optional[Union[List, Dict]] = None, + nonce: Optional[int] = None, + l1_resource_bounds: Optional[ResourceBounds] = None, + auto_estimate: bool = False, + ) -> "DeployResult": + """ + Deploys a contract. + + :param deployer_address: Address of the UDC. Is set to the address of + the default UDC (same address on mainnet/testnet/devnet) by default. + Must be set when using custom network other than ones listed above. + :param salt: Optional salt. Random value is selected if it is not provided. + :param unique: Determines if the contract should be salted with the account address. + :param constructor_args: a ``list`` or ``dict`` of arguments for the constructor. + :param nonce: Nonce of the transaction with call to deployer. + :param l1_resource_bounds: Max amount and max price per unit of L1 gas (in Wei) used when executing + this transaction. + :param auto_estimate: Use automatic fee estimation (not recommended, as it may lead to high costs). + :return: DeployResult instance. + """ + # pylint: disable=too-many-arguments, too-many-locals + abi = self._get_abi() + + return await Contract.deploy_contract_v3( + account=self._account, + class_hash=self.class_hash, + abi=abi, + constructor_args=constructor_args, + deployer_address=deployer_address, + cairo_version=self._cairo_version, + nonce=nonce, + l1_resource_bounds=l1_resource_bounds, + auto_estimate=auto_estimate, + salt=salt, + unique=unique, + ) + + def _get_abi(self) -> List: if self._cairo_version == 0: abi = create_compiled_contract(compiled_contract=self.compiled_contract).abi else: @@ -230,36 +290,7 @@ async def deploy( "Contract's ABI can't be converted to format List[Dict]. " "Make sure provided compiled_contract is correct." ) from exc - - deployer = Deployer( - deployer_address=deployer_address, - account_address=self._account.address if unique else None, - ) - deploy_call, address = deployer.create_contract_deployment( - class_hash=self.class_hash, - salt=salt, - abi=abi, - calldata=constructor_args, - cairo_version=self._cairo_version, - ) - res = await self._account.execute( - calls=deploy_call, nonce=nonce, max_fee=max_fee, auto_estimate=auto_estimate - ) - - deployed_contract = Contract( - provider=self._account, - address=address, - abi=abi, - cairo_version=self._cairo_version, - ) - - deploy_result = DeployResult( - hash=res.transaction_hash, - _client=self._account.client, - deployed_contract=deployed_contract, - ) - - return deploy_result + return abi @add_sync_methods @@ -906,7 +937,7 @@ async def declare_v3( ) @staticmethod - async def deploy_contract( + async def deploy_contract_v1( account: BaseAccount, class_hash: Hash, abi: List, @@ -917,9 +948,11 @@ async def deploy_contract( nonce: Optional[int] = None, max_fee: Optional[int] = None, auto_estimate: bool = False, + salt: Optional[int] = None, + unique: bool = True, ) -> "DeployResult": """ - Deploys a contract through Universal Deployer Contract + Deploys a contract through Universal Deployer Contract. :param account: BaseAccount used to sign and send deploy transaction. :param class_hash: The class_hash of the contract to be deployed. @@ -929,23 +962,98 @@ async def deploy_contract( the default UDC (same address on mainnet/testnet/devnet) by default. Must be set when using custom network other than ones listed above. :param cairo_version: Version of the Cairo in which contract is written. + By default, it is set to 0. :param nonce: Nonce of the transaction. :param max_fee: Max amount of Wei to be paid when executing transaction. :param auto_estimate: Use automatic fee estimation (not recommended, as it may lead to high costs). + :param salt: Optional salt. Random value is selected if it is not provided. + :param unique: Determines if the contract should be salted with the account address. :return: DeployResult instance. """ # pylint: disable=too-many-arguments deployer = Deployer( - deployer_address=deployer_address, account_address=account.address + deployer_address=deployer_address, + account_address=account.address if unique else None, ) deploy_call, address = deployer.create_contract_deployment( class_hash=class_hash, + salt=salt, abi=abi, calldata=constructor_args, cairo_version=cairo_version, ) + res = await account.execute( - calls=deploy_call, nonce=nonce, max_fee=max_fee, auto_estimate=auto_estimate + calls=deploy_call, + nonce=nonce, + max_fee=max_fee, + auto_estimate=auto_estimate, + ) + + deployed_contract = Contract( + provider=account, address=address, abi=abi, cairo_version=cairo_version + ) + deploy_result = DeployResult( + hash=res.transaction_hash, + _client=account.client, + deployed_contract=deployed_contract, + ) + + return deploy_result + + @staticmethod + async def deploy_contract_v3( + account: BaseAccount, + class_hash: Hash, + abi: List, + constructor_args: Optional[Union[List, Dict]] = None, + *, + deployer_address: AddressRepresentation = DEFAULT_DEPLOYER_ADDRESS, + cairo_version: int = 1, + nonce: Optional[int] = None, + l1_resource_bounds: Optional[ResourceBounds] = None, + auto_estimate: bool = False, + salt: Optional[int] = None, + unique: bool = True, + ) -> "DeployResult": + """ + Deploys a contract through Universal Deployer Contract. + + :param account: BaseAccount used to sign and send deploy transaction. + :param class_hash: The class_hash of the contract to be deployed. + :param abi: An abi of the contract to be deployed. + :param constructor_args: a ``list`` or ``dict`` of arguments for the constructor. + :param deployer_address: Address of the UDC. Is set to the address of + the default UDC (same address on mainnet/testnet/devnet) by default. + Must be set when using custom network other than ones listed above. + :param cairo_version: Version of the Cairo in which contract is written. + By default, it is set to 1. + :param nonce: Nonce of the transaction. + :param l1_resource_bounds: Max amount and max price per unit of L1 gas (in Wei) used when executing + this transaction. + :param auto_estimate: Use automatic fee estimation (not recommended, as it may lead to high costs). + :param salt: Optional salt. Random value is selected if it is not provided. + :param unique: Determines if the contract should be salted with the account address. + :return: DeployResult instance. + """ + # pylint: disable=too-many-arguments + deployer = Deployer( + deployer_address=deployer_address, + account_address=account.address if unique else None, + ) + deploy_call, address = deployer.create_contract_deployment( + class_hash=class_hash, + salt=salt, + abi=abi, + calldata=constructor_args, + cairo_version=cairo_version, + ) + + res = await account.execute_v3( + calls=deploy_call, + nonce=nonce, + l1_resource_bounds=l1_resource_bounds, + auto_estimate=auto_estimate, ) deployed_contract = Contract( From 219a13959d1229a8f25bf19d2505815418cf81d0 Mon Sep 17 00:00:00 2001 From: Dariusz Doktorski Date: Wed, 24 Jan 2024 18:36:54 +0100 Subject: [PATCH 11/24] Adjust tests to the new `Contract` deploy interface --- starknet_py/tests/e2e/cairo1v2_test.py | 2 +- .../fixtures/prepare_net_for_gateway_test.py | 2 +- .../tests/e2e/client/fixtures/transactions.py | 2 +- .../e2e/contract_interaction/deploy_test.py | 77 +++++++++++++++++-- .../e2e/docs/code_examples/test_contract.py | 4 +- .../guide/test_simple_declare_and_deploy.py | 2 +- .../test_simple_declare_and_deploy_cairo1.py | 2 +- .../e2e/docs/guide/test_simple_deploy.py | 4 +- .../docs/guide/test_simple_deploy_cairo1.py | 2 +- .../e2e/docs/quickstart/test_using_account.py | 2 +- starknet_py/tests/e2e/fixtures/contracts.py | 4 +- starknet_py/tests/e2e/fixtures/proxy.py | 2 +- starknet_py/tests/e2e/proxy/proxy_test.py | 2 +- 13 files changed, 85 insertions(+), 22 deletions(-) diff --git a/starknet_py/tests/e2e/cairo1v2_test.py b/starknet_py/tests/e2e/cairo1v2_test.py index 3b755ef58..5cf98bb5f 100644 --- a/starknet_py/tests/e2e/cairo1v2_test.py +++ b/starknet_py/tests/e2e/cairo1v2_test.py @@ -30,7 +30,7 @@ async def declare_deploy_hello2(account) -> Tuple[DeclareResult, DeployResult]: ) await declare_result.wait_for_acceptance() - deploy_result = await declare_result.deploy(auto_estimate=True) + deploy_result = await declare_result.deploy_v1(auto_estimate=True) await deploy_result.wait_for_acceptance() return declare_result, deploy_result diff --git a/starknet_py/tests/e2e/client/fixtures/prepare_net_for_gateway_test.py b/starknet_py/tests/e2e/client/fixtures/prepare_net_for_gateway_test.py index 2d634d271..29113df46 100755 --- a/starknet_py/tests/e2e/client/fixtures/prepare_net_for_gateway_test.py +++ b/starknet_py/tests/e2e/client/fixtures/prepare_net_for_gateway_test.py @@ -34,7 +34,7 @@ async def prepare_net_for_tests( account=account, compiled_contract=compiled_contract, max_fee=MAX_FEE ) await declare_result.wait_for_acceptance() - deploy_result = await declare_result.deploy(max_fee=MAX_FEE) + deploy_result = await declare_result.deploy_v1(max_fee=MAX_FEE) await deploy_result.wait_for_acceptance() declare_receipt = await account.client.get_transaction_receipt(declare_result.hash) diff --git a/starknet_py/tests/e2e/client/fixtures/transactions.py b/starknet_py/tests/e2e/client/fixtures/transactions.py index 3c7171ff8..49ca10a17 100644 --- a/starknet_py/tests/e2e/client/fixtures/transactions.py +++ b/starknet_py/tests/e2e/client/fixtures/transactions.py @@ -109,7 +109,7 @@ async def replaced_class(account: Account, map_class_hash: int) -> Tuple[int, in ).wait_for_acceptance() deploy_result = await ( - await declare_result.deploy(max_fee=MAX_FEE) + await declare_result.deploy_v1(max_fee=MAX_FEE) ).wait_for_acceptance() contract = deploy_result.deployed_contract diff --git a/starknet_py/tests/e2e/contract_interaction/deploy_test.py b/starknet_py/tests/e2e/contract_interaction/deploy_test.py index 52b506fd1..ca3812d98 100644 --- a/starknet_py/tests/e2e/contract_interaction/deploy_test.py +++ b/starknet_py/tests/e2e/contract_interaction/deploy_test.py @@ -7,13 +7,14 @@ from starknet_py.common import create_sierra_compiled_contract from starknet_py.contract import Contract, DeclareResult +from starknet_py.net.client_models import InvokeTransaction, InvokeTransactionV3 from starknet_py.net.models import DeclareV2 -from starknet_py.tests.e2e.fixtures.constants import MAX_FEE +from starknet_py.tests.e2e.fixtures.constants import MAX_FEE, MAX_RESOURCE_BOUNDS_L1 from starknet_py.tests.e2e.fixtures.misc import read_contract @pytest.mark.asyncio -async def test_declare_deploy( +async def test_declare_deploy_v1( account, cairo1_minimal_contract_class_hash: int, ): @@ -29,7 +30,34 @@ async def test_declare_deploy( declare_transaction=Mock(spec=DeclareV2), ) - deploy_result = await declare_result.deploy(max_fee=MAX_FEE) + deploy_result = await declare_result.deploy_v1(max_fee=MAX_FEE) + await deploy_result.wait_for_acceptance() + + assert isinstance(deploy_result.hash, int) + assert deploy_result.hash != 0 + assert deploy_result.deployed_contract.address != 0 + + +@pytest.mark.asyncio +async def test_declare_deploy_v3( + account, + cairo1_minimal_contract_class_hash: int, +): + compiled_contract = read_contract("minimal_contract_compiled.json") + + declare_result = DeclareResult( + _account=account, + _client=account.client, + _cairo_version=1, + class_hash=cairo1_minimal_contract_class_hash, + compiled_contract=compiled_contract, + hash=0, + declare_transaction=Mock(spec=DeclareV2), + ) + + deploy_result = await declare_result.deploy_v3( + l1_resource_bounds=MAX_RESOURCE_BOUNDS_L1 + ) await deploy_result.wait_for_acceptance() assert isinstance(deploy_result.hash, int) @@ -63,15 +91,15 @@ async def test_throws_on_wrong_abi(account, cairo1_minimal_contract_class_hash: "Make sure provided compiled_contract is correct." ), ): - await declare_result.deploy(max_fee=MAX_FEE) + await declare_result.deploy_v1(max_fee=MAX_FEE) @pytest.mark.asyncio -async def test_deploy_contract_flow(account, cairo1_hello_starknet_class_hash: int): +async def test_deploy_contract_v1(account, cairo1_hello_starknet_class_hash: int): compiled_contract = read_contract("hello_starknet_compiled.json") abi = create_sierra_compiled_contract(compiled_contract=compiled_contract).abi - deploy_result = await Contract.deploy_contract( + deploy_result = await Contract.deploy_contract_v1( class_hash=cairo1_hello_starknet_class_hash, account=account, abi=json.loads(abi), @@ -85,6 +113,41 @@ async def test_deploy_contract_flow(account, cairo1_hello_starknet_class_hash: i assert isinstance(contract.address, int) assert len(contract.functions) != 0 + transaction = await account.client.get_transaction(tx_hash=deploy_result.hash) + assert isinstance(transaction, InvokeTransaction) + + class_hash = await account.client.get_class_hash_at( + contract_address=contract.address + ) + assert class_hash == cairo1_hello_starknet_class_hash + + +@pytest.mark.asyncio +async def test_deploy_contract_v3(account, cairo1_hello_starknet_class_hash: int): + compiled_contract = read_contract("hello_starknet_compiled.json") + abi = create_sierra_compiled_contract(compiled_contract=compiled_contract).abi + + deploy_result = await Contract.deploy_contract_v3( + class_hash=cairo1_hello_starknet_class_hash, + account=account, + abi=json.loads(abi), + l1_resource_bounds=MAX_RESOURCE_BOUNDS_L1, + cairo_version=1, + ) + await deploy_result.wait_for_acceptance() + + contract = deploy_result.deployed_contract + assert isinstance(contract.address, int) + assert len(contract.functions) != 0 + + transaction = await account.client.get_transaction(tx_hash=deploy_result.hash) + assert isinstance(transaction, InvokeTransactionV3) + + class_hash = await account.client.get_class_hash_at( + contract_address=contract.address + ) + assert class_hash == cairo1_hello_starknet_class_hash + @pytest.mark.asyncio async def test_general_simplified_deployment_flow(account, map_compiled_contract): @@ -94,7 +157,7 @@ async def test_general_simplified_deployment_flow(account, map_compiled_contract max_fee=MAX_FEE, ) await declare_result.wait_for_acceptance() - deployment = await declare_result.deploy(max_fee=MAX_FEE) + deployment = await declare_result.deploy_v1(max_fee=MAX_FEE) await deployment.wait_for_acceptance() contract = deployment.deployed_contract 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 fc4788736..f797acc05 100644 --- a/starknet_py/tests/e2e/docs/code_examples/test_contract.py +++ b/starknet_py/tests/e2e/docs/code_examples/test_contract.py @@ -62,7 +62,7 @@ async def test_declare(account, custom_proxy): @pytest.mark.asyncio async def test_deploy_contract(account, class_hash): # docs-start: deploy_contract - deploy_result = await Contract.deploy_contract( + deploy_result = await Contract.deploy_contract_v1( account=account, class_hash=class_hash, abi=[ @@ -76,7 +76,7 @@ async def test_deploy_contract(account, class_hash): max_fee=int(1e15), ) # or when contract has a constructor with arguments - deploy_result = await Contract.deploy_contract( + deploy_result = await Contract.deploy_contract_v1( account=account, class_hash=class_hash, abi=[ diff --git a/starknet_py/tests/e2e/docs/guide/test_simple_declare_and_deploy.py b/starknet_py/tests/e2e/docs/guide/test_simple_declare_and_deploy.py index e528f3305..23f5ef850 100644 --- a/starknet_py/tests/e2e/docs/guide/test_simple_declare_and_deploy.py +++ b/starknet_py/tests/e2e/docs/guide/test_simple_declare_and_deploy.py @@ -20,7 +20,7 @@ async def test_simple_declare_and_deploy(account, map_compiled_contract): await declare_result.wait_for_acceptance() # After contract is declared it can be deployed - deploy_result = await declare_result.deploy(max_fee=int(1e16)) + deploy_result = await declare_result.deploy_v1(max_fee=int(1e16)) await deploy_result.wait_for_acceptance() # You can pass more arguments to the `deploy` method. Check `API` section to learn more diff --git a/starknet_py/tests/e2e/docs/guide/test_simple_declare_and_deploy_cairo1.py b/starknet_py/tests/e2e/docs/guide/test_simple_declare_and_deploy_cairo1.py index d45c6c606..f8f72e72c 100644 --- a/starknet_py/tests/e2e/docs/guide/test_simple_declare_and_deploy_cairo1.py +++ b/starknet_py/tests/e2e/docs/guide/test_simple_declare_and_deploy_cairo1.py @@ -23,7 +23,7 @@ async def test_simple_declare_and_deploy(account): ) await declare_result.wait_for_acceptance() - deploy_result = await declare_result.deploy( + deploy_result = await declare_result.deploy_v1( constructor_args=constructor_args, max_fee=int(1e16) ) await deploy_result.wait_for_acceptance() diff --git a/starknet_py/tests/e2e/docs/guide/test_simple_deploy.py b/starknet_py/tests/e2e/docs/guide/test_simple_deploy.py index 3348211b3..2abb18ebd 100644 --- a/starknet_py/tests/e2e/docs/guide/test_simple_deploy.py +++ b/starknet_py/tests/e2e/docs/guide/test_simple_deploy.py @@ -15,7 +15,7 @@ async def test_simple_deploy(account, map_class_hash, map_compiled_contract): abi = create_compiled_contract(compiled_contract=map_compiled_contract).abi # docs: start - # To deploy contract just use `Contract.deploy_contract` method + # To deploy contract just use `Contract.deploy_contract_v1` method # Note that class_hash and abi of the contract must be known # If constructor of the contract requires arguments, pass constructor_args parameter @@ -25,7 +25,7 @@ async def test_simple_deploy(account, map_class_hash, map_compiled_contract): constructor_args = None # docs: start - deploy_result = await Contract.deploy_contract( + deploy_result = await Contract.deploy_contract_v1( account=account, class_hash=class_hash, abi=abi, 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 26ba5d973..7a1de341f 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 @@ -38,7 +38,7 @@ async def test_simple_deploy_cairo1(account, cairo1_erc20_class_hash): "recipient": account.address, } - deploy_result = await Contract.deploy_contract( + deploy_result = await Contract.deploy_contract_v1( account=account, class_hash=class_hash, abi=json.loads(abi), diff --git a/starknet_py/tests/e2e/docs/quickstart/test_using_account.py b/starknet_py/tests/e2e/docs/quickstart/test_using_account.py index d7d6cec98..a6d82006b 100644 --- a/starknet_py/tests/e2e/docs/quickstart/test_using_account.py +++ b/starknet_py/tests/e2e/docs/quickstart/test_using_account.py @@ -20,7 +20,7 @@ async def test_using_account(account, map_compiled_contract): account=account, compiled_contract=map_compiled_contract, max_fee=MAX_FEE ) await declare_result.wait_for_acceptance() - deploy_result = await declare_result.deploy(max_fee=MAX_FEE) + deploy_result = await declare_result.deploy_v1(max_fee=MAX_FEE) # Wait until deployment transaction is accepted await deploy_result.wait_for_acceptance() diff --git a/starknet_py/tests/e2e/fixtures/contracts.py b/starknet_py/tests/e2e/fixtures/contracts.py index f40108b5f..a74a58fb2 100644 --- a/starknet_py/tests/e2e/fixtures/contracts.py +++ b/starknet_py/tests/e2e/fixtures/contracts.py @@ -116,7 +116,7 @@ async def deploy_contract(account: BaseAccount, class_hash: int, abi: List) -> C """ Deploys a contract and returns its instance. """ - deployment_result = await Contract.deploy_contract( + deployment_result = await Contract.deploy_contract_v1( account=account, class_hash=class_hash, abi=abi, max_fee=MAX_FEE ) deployment_result = await deployment_result.wait_for_acceptance() @@ -172,7 +172,7 @@ async def deployed_balance_contract( ) await declare_result.wait_for_acceptance() - deploy_result = await declare_result.deploy(max_fee=int(1e16)) + deploy_result = await declare_result.deploy_v1(max_fee=int(1e16)) await deploy_result.wait_for_acceptance() return deploy_result.deployed_contract diff --git a/starknet_py/tests/e2e/fixtures/proxy.py b/starknet_py/tests/e2e/fixtures/proxy.py index 0cbcf7355..98232bbaf 100644 --- a/starknet_py/tests/e2e/fixtures/proxy.py +++ b/starknet_py/tests/e2e/fixtures/proxy.py @@ -122,7 +122,7 @@ async def deploy_proxy_to_contract( max_fee=MAX_FEE, ) await declare_proxy_result.wait_for_acceptance() - deploy_result = await declare_proxy_result.deploy( + deploy_result = await declare_proxy_result.deploy_v1( constructor_args=[ declare_result.class_hash, get_selector_from_name("put"), diff --git a/starknet_py/tests/e2e/proxy/proxy_test.py b/starknet_py/tests/e2e/proxy/proxy_test.py index 9d3b0003f..770a41444 100644 --- a/starknet_py/tests/e2e/proxy/proxy_test.py +++ b/starknet_py/tests/e2e/proxy/proxy_test.py @@ -107,7 +107,7 @@ async def test_contract_from_address_with_old_address_proxy( account=account, compiled_contract=old_proxy, max_fee=MAX_FEE ) await declare_result.wait_for_acceptance() - deploy_result = await declare_result.deploy( + deploy_result = await declare_result.deploy_v1( constructor_args={"implementation_address": map_contract.address}, max_fee=MAX_FEE, ) From 9d0e0f78ba97128766a21b6cc34cb85a40e44f96 Mon Sep 17 00:00:00 2001 From: Dariusz Doktorski Date: Wed, 24 Jan 2024 19:21:02 +0100 Subject: [PATCH 12/24] Break down `contract.py` file --- starknet_py/contract.py | 529 +----------------- starknet_py/contract_function.py | 481 ++++++++++++++++ .../net/account/account_deployment_result.py | 2 +- starknet_py/sent_transaction.py | 57 ++ .../contract_interaction/interaction_test.py | 4 +- .../code_examples/test_contract_function.py | 3 +- 6 files changed, 549 insertions(+), 527 deletions(-) create mode 100644 starknet_py/contract_function.py create mode 100644 starknet_py/sent_transaction.py diff --git a/starknet_py/contract.py b/starknet_py/contract.py index da4d055bf..ac35b0d16 100644 --- a/starknet_py/contract.py +++ b/starknet_py/contract.py @@ -1,21 +1,11 @@ from __future__ import annotations -import dataclasses import json -import warnings -from abc import ABC, abstractmethod from dataclasses import dataclass -from functools import cached_property -from typing import Dict, List, Optional, TypeVar, Union +from typing import Dict, List, Optional, Union from marshmallow import ValidationError -from starknet_py.abi.model import Abi -from starknet_py.abi.parser import AbiParser -from starknet_py.abi.v1.model import Abi as AbiV1 -from starknet_py.abi.v1.parser import AbiParser as AbiV1Parser -from starknet_py.abi.v2.model import Abi as AbiV2 -from starknet_py.abi.v2.parser import AbiParser as AbiV2Parser from starknet_py.abi.v2.shape import ( FUNCTION_ENTRY, IMPL_ENTRY, @@ -24,140 +14,25 @@ ) from starknet_py.common import create_compiled_contract, create_sierra_compiled_contract from starknet_py.constants import DEFAULT_DEPLOYER_ADDRESS +from starknet_py.contract_function import ContractData, ContractFunction from starknet_py.contract_utils import _extract_compiled_class_hash, _unpack_provider from starknet_py.hash.address import compute_address from starknet_py.hash.class_hash import compute_class_hash -from starknet_py.hash.selector import get_selector_from_name 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, ResourceBounds, Tag +from starknet_py.net.client_models import Hash, ResourceBounds from starknet_py.net.models import AddressRepresentation, parse_address -from starknet_py.net.models.transaction import Declare, Invoke +from starknet_py.net.models.transaction import Declare from starknet_py.net.udc_deployer.deployer import Deployer from starknet_py.proxy.contract_abi_resolver import ( ContractAbiResolver, ProxyConfig, prepare_proxy_config, ) -from starknet_py.serialization import TupleDataclass, serializer_for_function -from starknet_py.serialization.factory import serializer_for_function_v1 -from starknet_py.serialization.function_serialization_adapter import ( - FunctionSerializationAdapter, -) -from starknet_py.utils.constructor_args_translator import ( - _is_abi_v2, - translate_constructor_args, -) +from starknet_py.sent_transaction import SentTransaction +from starknet_py.utils.constructor_args_translator import translate_constructor_args from starknet_py.utils.sync import add_sync_methods -ABI = list -ABIEntry = dict -TypeSentTransaction = TypeVar("TypeSentTransaction", bound="SentTransaction") - - -@dataclass(frozen=True) -class ContractData: - """ - Basic data of a deployed contract. - """ - - address: int - abi: ABI - cairo_version: int - - @cached_property - def parsed_abi(self) -> Union[Abi, AbiV1, AbiV2]: - """ - Abi parsed into proper dataclass. - - :return: Abi - """ - if self.cairo_version == 1: - if _is_abi_v2(self.abi): - return AbiV2Parser(self.abi).parse() - return AbiV1Parser(self.abi).parse() - return AbiParser(self.abi).parse() - - @staticmethod - def from_abi(address: int, abi: ABI, cairo_version: int = 0) -> ContractData: - """ - Create ContractData from ABI. - - :param address: Address of the deployed contract. - :param abi: Abi of the contract. - :param cairo_version: Version of the Cairo in which contract is written. - :return: ContractData instance. - """ - return ContractData( - address=address, - abi=abi, - cairo_version=cairo_version, - ) - - -@add_sync_methods -@dataclass(frozen=True) -class SentTransaction: - """ - Dataclass exposing the interface of transaction related to a performed action. - """ - - hash: int - """Hash of the transaction.""" - - _client: Client - status: Optional[str] = None - """Status of the transaction.""" - - block_number: Optional[int] = None - """Number of the block in which transaction was included.""" - - async def wait_for_acceptance( - self: TypeSentTransaction, - wait_for_accept: Optional[bool] = None, - check_interval: float = 2, - retries: int = 500, - ) -> TypeSentTransaction: - """ - Waits for transaction to be accepted on chain till ``ACCEPTED`` status. - Returns a new SentTransaction instance, **does not mutate original instance**. - """ - if wait_for_accept is not None: - warnings.warn( - "Parameter `wait_for_accept` has been deprecated - since Starknet 0.12.0, transactions in a PENDING" - " block have status ACCEPTED_ON_L2." - ) - - tx_receipt = await self._client.wait_for_tx( - self.hash, - check_interval=check_interval, - retries=retries, - ) - return dataclasses.replace( - self, - status=tx_receipt.finality_status, - block_number=tx_receipt.block_number, - ) - - -@add_sync_methods -@dataclass(frozen=True) -class InvokeResult(SentTransaction): - """ - Result of the Invoke transaction. - """ - - # We ensure these are not None in __post_init__ - contract: ContractData = None # pyright: ignore - """Additional information about the Contract that made the transaction.""" - - invoke_transaction: Invoke = None # pyright: ignore - """A InvokeTransaction instance used.""" - - def __post_init__(self): - assert self.contract is not None - assert self.invoke_transaction is not None - @add_sync_methods @dataclass(frozen=True) @@ -309,398 +184,6 @@ def __post_init__(self): raise ValueError("Argument deployed_contract can't be None.") -@dataclass -class CallToSend(Call): - _client: Client - _payload_transformer: FunctionSerializationAdapter - - -@add_sync_methods -@dataclass -class PreparedFunctionCall(CallToSend): - async def call_raw( - self, - block_hash: Optional[str] = None, - block_number: Optional[Union[int, Tag]] = None, - ) -> List[int]: - """ - Calls a method without translating the result into python values. - - :param block_hash: Optional block hash. - :param block_number: Optional block number. - :return: list of ints. - """ - return await self._client.call_contract( - call=self, block_hash=block_hash, block_number=block_number - ) - - async def call( - self, - block_hash: Optional[str] = None, - block_number: Optional[Union[int, Tag]] = None, - ) -> TupleDataclass: - """ - Calls a method. - - :param block_hash: Optional block hash. - :param block_number: Optional block number. - :return: TupleDataclass representing call result. - """ - result = await self.call_raw(block_hash=block_hash, block_number=block_number) - return self._payload_transformer.deserialize(result) - - -@add_sync_methods -@dataclass -class PreparedFunctionInvoke(ABC, CallToSend): - _contract_data: ContractData - _account: Optional[BaseAccount] - - def __post_init__(self): - if self._account is None: - raise ValueError( - "Contract instance was created without providing an Account. " - "It is not possible to prepare and send an invoke transaction." - ) - - @property - def get_account(self): - if self._account is not None: - return self._account - - raise ValueError( - "The account is not defined. It is not possible to send an invoke transaction." - ) - - @abstractmethod - async def estimate_fee( - self, - block_hash: Optional[Union[Hash, Tag]] = None, - block_number: Optional[Union[int, Tag]] = None, - *, - nonce: Optional[int] = None, - ) -> EstimatedFee: - """ - Estimate fee for prepared function call. - - :param block_hash: Estimate fee at specific block hash. - :param block_number: Estimate fee at given block number - (or "latest" / "pending" for the latest / pending block), default is "pending". - :param nonce: Nonce of the transaction. - :return: Estimated amount of the transaction cost, either in Wei or Fri associated with executing the - specified transaction. - """ - - async def _invoke(self, transaction: Invoke) -> InvokeResult: - response = await self._client.send_transaction(transaction) - - invoke_result = InvokeResult( - hash=response.transaction_hash, # noinspection PyTypeChecker - _client=self._client, - contract=self._contract_data, - invoke_transaction=transaction, - ) - - return invoke_result - - -@add_sync_methods -@dataclass -class PreparedFunctionInvokeV1(PreparedFunctionInvoke): - max_fee: Optional[int] - - async def invoke( - self, - max_fee: Optional[int] = None, - auto_estimate: bool = False, - *, - nonce: Optional[int] = None, - ) -> InvokeResult: - """ - Send an Invoke transaction version 1 for a prepared data. - - :param max_fee: Max amount of Wei to be paid when executing transaction. - :param auto_estimate: Use automatic fee estimation, not recommend as it may lead to high costs. - :param nonce: Nonce of the transaction. - :return: InvokeResult. - """ - - transaction = await self.get_account.sign_invoke_v1_transaction( - calls=self, - nonce=nonce, - max_fee=max_fee or self.max_fee, - auto_estimate=auto_estimate, - ) - - return await self._invoke(transaction) - - async def estimate_fee( - self, - block_hash: Optional[Union[Hash, Tag]] = None, - block_number: Optional[Union[int, Tag]] = None, - *, - nonce: Optional[int] = None, - ) -> EstimatedFee: - tx = await self.get_account.sign_invoke_v1_transaction( - calls=self, nonce=nonce, max_fee=0 - ) - - estimated_fee = await self._client.estimate_fee( - tx=tx, - block_hash=block_hash, - block_number=block_number, - ) - - assert isinstance(estimated_fee, EstimatedFee) - return estimated_fee - - -@add_sync_methods -@dataclass -class PreparedFunctionInvokeV3(PreparedFunctionInvoke): - l1_resource_bounds: Optional[ResourceBounds] - - async def invoke( - self, - l1_resource_bounds: Optional[ResourceBounds] = None, - auto_estimate: bool = False, - *, - nonce: Optional[int] = None, - ) -> InvokeResult: - """ - Send an Invoke transaction version 3 for a prepared data. - - :param l1_resource_bounds: Max amount and max price per unit of L1 gas (in Wei) used when executing - this transaction. - :param auto_estimate: Use automatic fee estimation, not recommend as it may lead to high costs. - :param nonce: Nonce of the transaction. - :return: InvokeResult. - """ - - transaction = await self.get_account.sign_invoke_v3_transaction( - calls=self, - nonce=nonce, - l1_resource_bounds=l1_resource_bounds or self.l1_resource_bounds, - auto_estimate=auto_estimate, - ) - - return await self._invoke(transaction) - - async def estimate_fee( - self, - block_hash: Optional[Union[Hash, Tag]] = None, - block_number: Optional[Union[int, Tag]] = None, - *, - nonce: Optional[int] = None, - ) -> EstimatedFee: - tx = await self.get_account.sign_invoke_v3_transaction( - calls=self, nonce=nonce, l1_resource_bounds=ResourceBounds.init_with_zeros() - ) - - estimated_fee = await self._client.estimate_fee( - tx=tx, - block_hash=block_hash, - block_number=block_number, - ) - - assert isinstance(estimated_fee, EstimatedFee) - return estimated_fee - - -@add_sync_methods -class ContractFunction: - def __init__( - self, - name: str, - abi: ABIEntry, - contract_data: ContractData, - client: Client, - account: Optional[BaseAccount], - cairo_version: int = 0, - *, - interface_name: Optional[str] = None, - ): - # pylint: disable=too-many-arguments - self.name = name - self.abi = abi - self.inputs = abi["inputs"] - self.contract_data = contract_data - self.client = client - self.account = account - - if abi["type"] == L1_HANDLER_ENTRY: - assert not isinstance(contract_data.parsed_abi, AbiV1) - function = contract_data.parsed_abi.l1_handler - elif interface_name is None: - function = contract_data.parsed_abi.functions.get(name) - else: - assert isinstance(contract_data.parsed_abi, AbiV2) - interface = contract_data.parsed_abi.interfaces[interface_name] - function = interface.items[name] - - assert function is not None - - if cairo_version == 1: - assert not isinstance(function, Abi.Function) and function is not None - self._payload_transformer = serializer_for_function_v1(function) - - else: - assert isinstance(function, Abi.Function) and function is not None - self._payload_transformer = serializer_for_function(function) - - def prepare_call( - self, - *args, - **kwargs, - ) -> PreparedFunctionCall: - """ - ``*args`` and ``**kwargs`` are translated into Cairo calldata. - Creates a ``PreparedFunctionCall`` instance which exposes calldata for every argument - and adds more arguments when calling methods. - - :return: PreparedFunctionCall. - """ - - calldata = self._payload_transformer.serialize(*args, **kwargs) - return PreparedFunctionCall( - to_addr=self.contract_data.address, - calldata=calldata, - selector=self.get_selector(self.name), - _client=self.client, - _payload_transformer=self._payload_transformer, - ) - - async def call( - self, - *args, - block_hash: Optional[str] = None, - block_number: Optional[Union[int, Tag]] = None, - **kwargs, - ) -> TupleDataclass: - """ - Call contract's function. ``*args`` and ``**kwargs`` are translated into Cairo calldata. - The result is translated from Cairo data to python values. - Equivalent of ``.prepare_call(*args, **kwargs).call()``. - - :param block_hash: Block hash to perform the call to the contract at specific point of time. - :param block_number: Block number to perform the call to the contract at specific point of time. - :return: TupleDataclass representing call result. - """ - return await self.prepare_call(*args, **kwargs).call( - block_hash=block_hash, block_number=block_number - ) - - def prepare_invoke_v1( - self, - *args, - max_fee: Optional[int] = None, - **kwargs, - ) -> PreparedFunctionInvokeV1: - """ - ``*args`` and ``**kwargs`` are translated into Cairo calldata. - Creates a ``PreparedFunctionInvokeV1`` instance which exposes calldata for every argument - and adds more arguments when calling methods. - - :param max_fee: Max amount of Wei to be paid when executing transaction. - :return: PreparedFunctionCall. - """ - - calldata = self._payload_transformer.serialize(*args, **kwargs) - return PreparedFunctionInvokeV1( - to_addr=self.contract_data.address, - calldata=calldata, - selector=self.get_selector(self.name), - max_fee=max_fee, - _contract_data=self.contract_data, - _client=self.client, - _account=self.account, - _payload_transformer=self._payload_transformer, - ) - - async def invoke_v1( - self, - *args, - max_fee: Optional[int] = None, - auto_estimate: bool = False, - nonce: Optional[int] = None, - **kwargs, - ) -> InvokeResult: - """ - Invoke contract's function. ``*args`` and ``**kwargs`` are translated into Cairo calldata. - Equivalent of ``.prepare_invoke_v1(*args, **kwargs).invoke()``. - - :param max_fee: Max amount of Wei to be paid when executing transaction. - :param auto_estimate: Use automatic fee estimation, not recommend as it may lead to high costs. - :param nonce: Nonce of the transaction. - :return: InvokeResult. - """ - prepared_invoke = self.prepare_invoke_v1(*args, **kwargs) - return await prepared_invoke.invoke( - max_fee=max_fee, nonce=nonce, auto_estimate=auto_estimate - ) - - def prepare_invoke_v3( - self, - *args, - l1_resource_bounds: Optional[ResourceBounds] = None, - **kwargs, - ) -> PreparedFunctionInvokeV3: - """ - ``*args`` and ``**kwargs`` are translated into Cairo calldata. - Creates a ``PreparedFunctionInvokeV3`` instance which exposes calldata for every argument - and adds more arguments when calling methods. - - :param l1_resource_bounds: Max amount and max price per unit of L1 gas (in Wei) used when executing - this transaction. - :return: PreparedFunctionInvokeV3. - """ - - calldata = self._payload_transformer.serialize(*args, **kwargs) - return PreparedFunctionInvokeV3( - to_addr=self.contract_data.address, - calldata=calldata, - selector=self.get_selector(self.name), - l1_resource_bounds=l1_resource_bounds, - _contract_data=self.contract_data, - _client=self.client, - _account=self.account, - _payload_transformer=self._payload_transformer, - ) - - async def invoke_v3( - self, - *args, - l1_resource_bounds: Optional[ResourceBounds] = None, - auto_estimate: bool = False, - nonce: Optional[int] = None, - **kwargs, - ) -> InvokeResult: - """ - Invoke contract's function. ``*args`` and ``**kwargs`` are translated into Cairo calldata. - Equivalent of ``.prepare_invoke_v3(*args, **kwargs).invoke()``. - - :param l1_resource_bounds: Max amount and max price per unit of L1 gas (in Wei) used when executing - this transaction. - :param auto_estimate: Use automatic fee estimation, not recommend as it may lead to high costs. - :param nonce: Nonce of the transaction. - :return: InvokeResult. - """ - prepared_invoke = self.prepare_invoke_v3(*args, **kwargs) - return await prepared_invoke.invoke( - l1_resource_bounds=l1_resource_bounds, - nonce=nonce, - auto_estimate=auto_estimate, - ) - - @staticmethod - def get_selector(function_name: str): - """ - :param function_name: Contract function's name. - :return: A Starknet integer selector for this function inside the contract. - """ - return get_selector_from_name(function_name) - - FunctionsRepository = Dict[str, ContractFunction] diff --git a/starknet_py/contract_function.py b/starknet_py/contract_function.py new file mode 100644 index 000000000..c10a1328e --- /dev/null +++ b/starknet_py/contract_function.py @@ -0,0 +1,481 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod +from dataclasses import dataclass +from functools import cached_property +from typing import List, Optional, Union + +from starknet_py.abi import Abi, AbiParser +from starknet_py.abi.v1.model import Abi as AbiV1 +from starknet_py.abi.v1.parser import AbiParser as AbiV1Parser +from starknet_py.abi.v2.model import Abi as AbiV2 +from starknet_py.abi.v2.parser import AbiParser as AbiV2Parser +from starknet_py.abi.v2.shape import L1_HANDLER_ENTRY +from starknet_py.hash.selector import get_selector_from_name +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, ResourceBounds, Tag +from starknet_py.net.models.transaction import Invoke +from starknet_py.sent_transaction import SentTransaction +from starknet_py.serialization import ( + FunctionSerializationAdapter, + TupleDataclass, + serializer_for_function, +) +from starknet_py.serialization.factory import serializer_for_function_v1 +from starknet_py.utils.constructor_args_translator import _is_abi_v2 +from starknet_py.utils.sync import add_sync_methods + +ABI = list +ABIEntry = dict + + +@dataclass(frozen=True) +class ContractData: + """ + Basic data of a deployed contract. + """ + + address: int + abi: ABI + cairo_version: int + + @cached_property + def parsed_abi(self) -> Union[Abi, AbiV1, AbiV2]: + """ + Abi parsed into proper dataclass. + + :return: Abi + """ + if self.cairo_version == 1: + if _is_abi_v2(self.abi): + return AbiV2Parser(self.abi).parse() + return AbiV1Parser(self.abi).parse() + return AbiParser(self.abi).parse() + + @staticmethod + def from_abi(address: int, abi: ABI, cairo_version: int = 0) -> ContractData: + """ + Create ContractData from ABI. + + :param address: Address of the deployed contract. + :param abi: Abi of the contract. + :param cairo_version: Version of the Cairo in which contract is written. + :return: ContractData instance. + """ + return ContractData( + address=address, + abi=abi, + cairo_version=cairo_version, + ) + + +@add_sync_methods +@dataclass(frozen=True) +class InvokeResult(SentTransaction): + """ + Result of the Invoke transaction. + """ + + # We ensure these are not None in __post_init__ + contract: ContractData = None # pyright: ignore + """Additional information about the Contract that made the transaction.""" + + invoke_transaction: Invoke = None # pyright: ignore + """A InvokeTransaction instance used.""" + + def __post_init__(self): + assert self.contract is not None + assert self.invoke_transaction is not None + + +@dataclass +class CallToSend(Call): + _client: Client + _payload_transformer: FunctionSerializationAdapter + + +@add_sync_methods +@dataclass +class PreparedFunctionCall(CallToSend): + async def call_raw( + self, + block_hash: Optional[str] = None, + block_number: Optional[Union[int, Tag]] = None, + ) -> List[int]: + """ + Calls a method without translating the result into python values. + + :param block_hash: Optional block hash. + :param block_number: Optional block number. + :return: list of ints. + """ + return await self._client.call_contract( + call=self, block_hash=block_hash, block_number=block_number + ) + + async def call( + self, + block_hash: Optional[str] = None, + block_number: Optional[Union[int, Tag]] = None, + ) -> TupleDataclass: + """ + Calls a method. + + :param block_hash: Optional block hash. + :param block_number: Optional block number. + :return: TupleDataclass representing call result. + """ + result = await self.call_raw(block_hash=block_hash, block_number=block_number) + return self._payload_transformer.deserialize(result) + + +@add_sync_methods +@dataclass +class PreparedFunctionInvoke(ABC, CallToSend): + _contract_data: ContractData + _account: Optional[BaseAccount] + + def __post_init__(self): + if self._account is None: + raise ValueError( + "Contract instance was created without providing an Account. " + "It is not possible to prepare and send an invoke transaction." + ) + + @property + def get_account(self): + if self._account is not None: + return self._account + + raise ValueError( + "The account is not defined. It is not possible to send an invoke transaction." + ) + + @abstractmethod + async def estimate_fee( + self, + block_hash: Optional[Union[Hash, Tag]] = None, + block_number: Optional[Union[int, Tag]] = None, + *, + nonce: Optional[int] = None, + ) -> EstimatedFee: + """ + Estimate fee for prepared function call. + + :param block_hash: Estimate fee at specific block hash. + :param block_number: Estimate fee at given block number + (or "latest" / "pending" for the latest / pending block), default is "pending". + :param nonce: Nonce of the transaction. + :return: Estimated amount of the transaction cost, either in Wei or Fri associated with executing the + specified transaction. + """ + + async def _invoke(self, transaction: Invoke) -> InvokeResult: + response = await self._client.send_transaction(transaction) + + invoke_result = InvokeResult( + hash=response.transaction_hash, # noinspection PyTypeChecker + _client=self._client, + contract=self._contract_data, + invoke_transaction=transaction, + ) + + return invoke_result + + +@add_sync_methods +@dataclass +class PreparedFunctionInvokeV1(PreparedFunctionInvoke): + max_fee: Optional[int] + + async def invoke( + self, + max_fee: Optional[int] = None, + auto_estimate: bool = False, + *, + nonce: Optional[int] = None, + ) -> InvokeResult: + """ + Send an Invoke transaction version 1 for a prepared data. + + :param max_fee: Max amount of Wei to be paid when executing transaction. + :param auto_estimate: Use automatic fee estimation, not recommend as it may lead to high costs. + :param nonce: Nonce of the transaction. + :return: InvokeResult. + """ + + transaction = await self.get_account.sign_invoke_v1_transaction( + calls=self, + nonce=nonce, + max_fee=max_fee or self.max_fee, + auto_estimate=auto_estimate, + ) + + return await self._invoke(transaction) + + async def estimate_fee( + self, + block_hash: Optional[Union[Hash, Tag]] = None, + block_number: Optional[Union[int, Tag]] = None, + *, + nonce: Optional[int] = None, + ) -> EstimatedFee: + tx = await self.get_account.sign_invoke_v1_transaction( + calls=self, nonce=nonce, max_fee=0 + ) + + estimated_fee = await self._client.estimate_fee( + tx=tx, + block_hash=block_hash, + block_number=block_number, + ) + + assert isinstance(estimated_fee, EstimatedFee) + return estimated_fee + + +@add_sync_methods +@dataclass +class PreparedFunctionInvokeV3(PreparedFunctionInvoke): + l1_resource_bounds: Optional[ResourceBounds] + + async def invoke( + self, + l1_resource_bounds: Optional[ResourceBounds] = None, + auto_estimate: bool = False, + *, + nonce: Optional[int] = None, + ) -> InvokeResult: + """ + Send an Invoke transaction version 3 for a prepared data. + + :param l1_resource_bounds: Max amount and max price per unit of L1 gas (in Wei) used when executing + this transaction. + :param auto_estimate: Use automatic fee estimation, not recommend as it may lead to high costs. + :param nonce: Nonce of the transaction. + :return: InvokeResult. + """ + + transaction = await self.get_account.sign_invoke_v3_transaction( + calls=self, + nonce=nonce, + l1_resource_bounds=l1_resource_bounds or self.l1_resource_bounds, + auto_estimate=auto_estimate, + ) + + return await self._invoke(transaction) + + async def estimate_fee( + self, + block_hash: Optional[Union[Hash, Tag]] = None, + block_number: Optional[Union[int, Tag]] = None, + *, + nonce: Optional[int] = None, + ) -> EstimatedFee: + tx = await self.get_account.sign_invoke_v3_transaction( + calls=self, nonce=nonce, l1_resource_bounds=ResourceBounds.init_with_zeros() + ) + + estimated_fee = await self._client.estimate_fee( + tx=tx, + block_hash=block_hash, + block_number=block_number, + ) + + assert isinstance(estimated_fee, EstimatedFee) + return estimated_fee + + +@add_sync_methods +class ContractFunction: + def __init__( + self, + name: str, + abi: ABIEntry, + contract_data: ContractData, + client: Client, + account: Optional[BaseAccount], + cairo_version: int = 0, + *, + interface_name: Optional[str] = None, + ): + # pylint: disable=too-many-arguments + self.name = name + self.abi = abi + self.inputs = abi["inputs"] + self.contract_data = contract_data + self.client = client + self.account = account + + if abi["type"] == L1_HANDLER_ENTRY: + assert not isinstance(contract_data.parsed_abi, AbiV1) + function = contract_data.parsed_abi.l1_handler + elif interface_name is None: + function = contract_data.parsed_abi.functions.get(name) + else: + assert isinstance(contract_data.parsed_abi, AbiV2) + interface = contract_data.parsed_abi.interfaces[interface_name] + function = interface.items[name] + + assert function is not None + + if cairo_version == 1: + assert not isinstance(function, Abi.Function) and function is not None + self._payload_transformer = serializer_for_function_v1(function) + + else: + assert isinstance(function, Abi.Function) and function is not None + self._payload_transformer = serializer_for_function(function) + + def prepare_call( + self, + *args, + **kwargs, + ) -> PreparedFunctionCall: + """ + ``*args`` and ``**kwargs`` are translated into Cairo calldata. + Creates a ``PreparedFunctionCall`` instance which exposes calldata for every argument + and adds more arguments when calling methods. + + :return: PreparedFunctionCall. + """ + + calldata = self._payload_transformer.serialize(*args, **kwargs) + return PreparedFunctionCall( + to_addr=self.contract_data.address, + calldata=calldata, + selector=self.get_selector(self.name), + _client=self.client, + _payload_transformer=self._payload_transformer, + ) + + async def call( + self, + *args, + block_hash: Optional[str] = None, + block_number: Optional[Union[int, Tag]] = None, + **kwargs, + ) -> TupleDataclass: + """ + Call contract's function. ``*args`` and ``**kwargs`` are translated into Cairo calldata. + The result is translated from Cairo data to python values. + Equivalent of ``.prepare_call(*args, **kwargs).call()``. + + :param block_hash: Block hash to perform the call to the contract at specific point of time. + :param block_number: Block number to perform the call to the contract at specific point of time. + :return: TupleDataclass representing call result. + """ + return await self.prepare_call(*args, **kwargs).call( + block_hash=block_hash, block_number=block_number + ) + + def prepare_invoke_v1( + self, + *args, + max_fee: Optional[int] = None, + **kwargs, + ) -> PreparedFunctionInvokeV1: + """ + ``*args`` and ``**kwargs`` are translated into Cairo calldata. + Creates a ``PreparedFunctionInvokeV1`` instance which exposes calldata for every argument + and adds more arguments when calling methods. + + :param max_fee: Max amount of Wei to be paid when executing transaction. + :return: PreparedFunctionCall. + """ + + calldata = self._payload_transformer.serialize(*args, **kwargs) + return PreparedFunctionInvokeV1( + to_addr=self.contract_data.address, + calldata=calldata, + selector=self.get_selector(self.name), + max_fee=max_fee, + _contract_data=self.contract_data, + _client=self.client, + _account=self.account, + _payload_transformer=self._payload_transformer, + ) + + async def invoke_v1( + self, + *args, + max_fee: Optional[int] = None, + auto_estimate: bool = False, + nonce: Optional[int] = None, + **kwargs, + ) -> InvokeResult: + """ + Invoke contract's function. ``*args`` and ``**kwargs`` are translated into Cairo calldata. + Equivalent of ``.prepare_invoke_v1(*args, **kwargs).invoke()``. + + :param max_fee: Max amount of Wei to be paid when executing transaction. + :param auto_estimate: Use automatic fee estimation, not recommend as it may lead to high costs. + :param nonce: Nonce of the transaction. + :return: InvokeResult. + """ + prepared_invoke = self.prepare_invoke_v1(*args, **kwargs) + return await prepared_invoke.invoke( + max_fee=max_fee, nonce=nonce, auto_estimate=auto_estimate + ) + + def prepare_invoke_v3( + self, + *args, + l1_resource_bounds: Optional[ResourceBounds] = None, + **kwargs, + ) -> PreparedFunctionInvokeV3: + """ + ``*args`` and ``**kwargs`` are translated into Cairo calldata. + Creates a ``PreparedFunctionInvokeV3`` instance which exposes calldata for every argument + and adds more arguments when calling methods. + + :param l1_resource_bounds: Max amount and max price per unit of L1 gas (in Wei) used when executing + this transaction. + :return: PreparedFunctionInvokeV3. + """ + + calldata = self._payload_transformer.serialize(*args, **kwargs) + return PreparedFunctionInvokeV3( + to_addr=self.contract_data.address, + calldata=calldata, + selector=self.get_selector(self.name), + l1_resource_bounds=l1_resource_bounds, + _contract_data=self.contract_data, + _client=self.client, + _account=self.account, + _payload_transformer=self._payload_transformer, + ) + + async def invoke_v3( + self, + *args, + l1_resource_bounds: Optional[ResourceBounds] = None, + auto_estimate: bool = False, + nonce: Optional[int] = None, + **kwargs, + ) -> InvokeResult: + """ + Invoke contract's function. ``*args`` and ``**kwargs`` are translated into Cairo calldata. + Equivalent of ``.prepare_invoke_v3(*args, **kwargs).invoke()``. + + :param l1_resource_bounds: Max amount and max price per unit of L1 gas (in Wei) used when executing + this transaction. + :param auto_estimate: Use automatic fee estimation, not recommend as it may lead to high costs. + :param nonce: Nonce of the transaction. + :return: InvokeResult. + """ + prepared_invoke = self.prepare_invoke_v3(*args, **kwargs) + return await prepared_invoke.invoke( + l1_resource_bounds=l1_resource_bounds, + nonce=nonce, + auto_estimate=auto_estimate, + ) + + @staticmethod + def get_selector(function_name: str): + """ + :param function_name: Contract function's name. + :return: A Starknet integer selector for this function inside the contract. + """ + return get_selector_from_name(function_name) diff --git a/starknet_py/net/account/account_deployment_result.py b/starknet_py/net/account/account_deployment_result.py index 44912ce95..f7a029a7e 100644 --- a/starknet_py/net/account/account_deployment_result.py +++ b/starknet_py/net/account/account_deployment_result.py @@ -1,6 +1,6 @@ from dataclasses import dataclass -from starknet_py.contract import SentTransaction +from starknet_py.sent_transaction import SentTransaction from starknet_py.utils.sync import add_sync_methods diff --git a/starknet_py/sent_transaction.py b/starknet_py/sent_transaction.py new file mode 100644 index 000000000..aec864ebd --- /dev/null +++ b/starknet_py/sent_transaction.py @@ -0,0 +1,57 @@ +from __future__ import annotations + +import dataclasses +import warnings +from dataclasses import dataclass +from typing import Optional, TypeVar + +from starknet_py.net.client import Client +from starknet_py.utils.sync import add_sync_methods + + +@add_sync_methods +@dataclass(frozen=True) +class SentTransaction: + """ + Dataclass exposing the interface of transaction related to a performed action. + """ + + hash: int + """Hash of the transaction.""" + + _client: Client + status: Optional[str] = None + """Status of the transaction.""" + + block_number: Optional[int] = None + """Number of the block in which transaction was included.""" + + async def wait_for_acceptance( + self: TypeSentTransaction, + wait_for_accept: Optional[bool] = None, + check_interval: float = 2, + retries: int = 500, + ) -> TypeSentTransaction: + """ + Waits for transaction to be accepted on chain till ``ACCEPTED`` status. + Returns a new SentTransaction instance, **does not mutate original instance**. + """ + if wait_for_accept is not None: + warnings.warn( + "Parameter `wait_for_accept` has been deprecated - since Starknet 0.12.0, transactions in a PENDING" + " block have status ACCEPTED_ON_L2." + ) + + tx_receipt = await self._client.wait_for_tx( + self.hash, + check_interval=check_interval, + retries=retries, + ) + return dataclasses.replace( + self, + status=tx_receipt.finality_status, + block_number=tx_receipt.block_number, + ) + + +TypeSentTransaction = TypeVar("TypeSentTransaction", bound="SentTransaction") diff --git a/starknet_py/tests/e2e/contract_interaction/interaction_test.py b/starknet_py/tests/e2e/contract_interaction/interaction_test.py index 1ffdd29c6..e538650bd 100644 --- a/starknet_py/tests/e2e/contract_interaction/interaction_test.py +++ b/starknet_py/tests/e2e/contract_interaction/interaction_test.py @@ -1,7 +1,7 @@ import pytest -from starknet_py.contract import ( - Contract, +from starknet_py.contract import Contract +from starknet_py.contract_function import ( PreparedFunctionInvokeV1, PreparedFunctionInvokeV3, ) diff --git a/starknet_py/tests/e2e/docs/code_examples/test_contract_function.py b/starknet_py/tests/e2e/docs/code_examples/test_contract_function.py index 84d49e81f..b1064a7d1 100644 --- a/starknet_py/tests/e2e/docs/code_examples/test_contract_function.py +++ b/starknet_py/tests/e2e/docs/code_examples/test_contract_function.py @@ -1,7 +1,8 @@ # pylint: disable=unused-variable import pytest -from starknet_py.contract import Contract, ContractFunction +from starknet_py.contract import Contract +from starknet_py.contract_function import ContractFunction def test_prepare(map_contract: Contract): From 01b55fb2b35b5a17355f6962dbd866b087e3300a Mon Sep 17 00:00:00 2001 From: Dariusz Doktorski Date: Wed, 24 Jan 2024 19:49:16 +0100 Subject: [PATCH 13/24] Move `Contract` related classes to separate directory --- README.md | 4 ++-- starknet_py/net/account/account_deployment_result.py | 2 +- starknet_py/net/contract/__init__.py | 0 starknet_py/{ => net/contract}/contract.py | 9 ++++++--- starknet_py/{ => net/contract}/contract_function.py | 2 +- starknet_py/{ => net/contract}/contract_test.py | 2 +- starknet_py/{ => net/contract}/contract_utils.py | 0 starknet_py/{ => net/contract}/sent_transaction.py | 0 starknet_py/tests/e2e/account/account_test.py | 2 +- starknet_py/tests/e2e/block_test.py | 2 +- starknet_py/tests/e2e/cairo1v2_test.py | 2 +- .../client/fixtures/prepare_net_for_gateway_test.py | 2 +- starknet_py/tests/e2e/client/fixtures/transactions.py | 2 +- starknet_py/tests/e2e/client/full_node_test.py | 2 +- .../tests/e2e/contract_interaction/declare_test.py | 2 +- .../tests/e2e/contract_interaction/deploy_test.py | 2 +- .../tests/e2e/contract_interaction/interaction_test.py | 10 +++++----- .../e2e/contract_interaction/v1_interaction_test.py | 2 +- starknet_py/tests/e2e/deploy/deployer_test.py | 2 +- .../account_creation/test_deploy_prefunded_account.py | 2 +- .../tests/e2e/docs/code_examples/test_contract.py | 2 +- .../e2e/docs/code_examples/test_contract_function.py | 4 ++-- .../e2e/docs/code_examples/test_full_node_client.py | 2 +- .../docs/code_examples/test_prepared_function_call.py | 2 +- .../e2e/docs/guide/test_deploying_in_multicall.py | 2 +- .../e2e/docs/guide/test_handling_client_errors.py | 2 +- .../tests/e2e/docs/guide/test_resolving_proxies.py | 2 +- .../e2e/docs/guide/test_simple_declare_and_deploy.py | 2 +- .../guide/test_simple_declare_and_deploy_cairo1.py | 2 +- starknet_py/tests/e2e/docs/guide/test_simple_deploy.py | 2 +- .../tests/e2e/docs/guide/test_simple_deploy_cairo1.py | 2 +- .../e2e/docs/guide/test_using_existing_contracts.py | 2 +- .../tests/e2e/docs/quickstart/test_synchronous_api.py | 2 +- .../tests/e2e/docs/quickstart/test_using_account.py | 2 +- .../tests/e2e/docs/quickstart/test_using_contract.py | 2 +- starknet_py/tests/e2e/fixtures/accounts.py | 2 +- starknet_py/tests/e2e/fixtures/contracts.py | 2 +- starknet_py/tests/e2e/fixtures/proxy.py | 2 +- starknet_py/tests/e2e/proxy/proxy_test.py | 2 +- .../tests/e2e/tests_on_networks/account_test.py | 2 +- starknet_py/tests/e2e/utils.py | 2 +- 41 files changed, 49 insertions(+), 46 deletions(-) create mode 100644 starknet_py/net/contract/__init__.py rename starknet_py/{ => net/contract}/contract.py (98%) rename starknet_py/{ => net/contract}/contract_function.py (99%) rename starknet_py/{ => net/contract}/contract_test.py (96%) rename starknet_py/{ => net/contract}/contract_utils.py (100%) rename starknet_py/{ => net/contract}/sent_transaction.py (100%) diff --git a/README.md b/README.md index 62b7e0ad3..c285d2c30 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ Installation varies between operating systems. This is the recommended way of using the SDK. ```python -from starknet_py.contract import Contract +from starknet_py.net.contract.contract import Contract from starknet_py.net.full_node_client import FullNodeClient contract = await Contract.from_address( @@ -51,7 +51,7 @@ contract = await Contract.from_address( You can access synchronous world with `_sync` postfix. ```python -from starknet_py.contract import Contract +from starknet_py.net.contract.contract import Contract from starknet_py.net.full_node_client import FullNodeClient contract = Contract.from_address_sync( diff --git a/starknet_py/net/account/account_deployment_result.py b/starknet_py/net/account/account_deployment_result.py index f7a029a7e..b5205fc1b 100644 --- a/starknet_py/net/account/account_deployment_result.py +++ b/starknet_py/net/account/account_deployment_result.py @@ -1,6 +1,6 @@ from dataclasses import dataclass -from starknet_py.sent_transaction import SentTransaction +from starknet_py.net.contract.sent_transaction import SentTransaction from starknet_py.utils.sync import add_sync_methods diff --git a/starknet_py/net/contract/__init__.py b/starknet_py/net/contract/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/starknet_py/contract.py b/starknet_py/net/contract/contract.py similarity index 98% rename from starknet_py/contract.py rename to starknet_py/net/contract/contract.py index ac35b0d16..42119498a 100644 --- a/starknet_py/contract.py +++ b/starknet_py/net/contract/contract.py @@ -14,13 +14,17 @@ ) from starknet_py.common import create_compiled_contract, create_sierra_compiled_contract from starknet_py.constants import DEFAULT_DEPLOYER_ADDRESS -from starknet_py.contract_function import ContractData, ContractFunction -from starknet_py.contract_utils import _extract_compiled_class_hash, _unpack_provider from starknet_py.hash.address import compute_address from starknet_py.hash.class_hash import compute_class_hash from starknet_py.net.account.base_account import BaseAccount from starknet_py.net.client import Client from starknet_py.net.client_models import Hash, ResourceBounds +from starknet_py.net.contract.contract_function import ContractData, ContractFunction +from starknet_py.net.contract.contract_utils import ( + _extract_compiled_class_hash, + _unpack_provider, +) +from starknet_py.net.contract.sent_transaction import SentTransaction from starknet_py.net.models import AddressRepresentation, parse_address from starknet_py.net.models.transaction import Declare from starknet_py.net.udc_deployer.deployer import Deployer @@ -29,7 +33,6 @@ ProxyConfig, prepare_proxy_config, ) -from starknet_py.sent_transaction import SentTransaction from starknet_py.utils.constructor_args_translator import translate_constructor_args from starknet_py.utils.sync import add_sync_methods diff --git a/starknet_py/contract_function.py b/starknet_py/net/contract/contract_function.py similarity index 99% rename from starknet_py/contract_function.py rename to starknet_py/net/contract/contract_function.py index c10a1328e..8f7fb1094 100644 --- a/starknet_py/contract_function.py +++ b/starknet_py/net/contract/contract_function.py @@ -15,8 +15,8 @@ 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, ResourceBounds, Tag +from starknet_py.net.contract.sent_transaction import SentTransaction from starknet_py.net.models.transaction import Invoke -from starknet_py.sent_transaction import SentTransaction from starknet_py.serialization import ( FunctionSerializationAdapter, TupleDataclass, diff --git a/starknet_py/contract_test.py b/starknet_py/net/contract/contract_test.py similarity index 96% rename from starknet_py/contract_test.py rename to starknet_py/net/contract/contract_test.py index d137d14b7..f447ec47c 100644 --- a/starknet_py/contract_test.py +++ b/starknet_py/net/contract/contract_test.py @@ -1,7 +1,7 @@ import pytest -from starknet_py.contract import Contract, DeclareResult, DeployResult from starknet_py.net.account.base_account import BaseAccount +from starknet_py.net.contract.contract import Contract, DeclareResult, DeployResult def test_compute_hash(balance_contract): diff --git a/starknet_py/contract_utils.py b/starknet_py/net/contract/contract_utils.py similarity index 100% rename from starknet_py/contract_utils.py rename to starknet_py/net/contract/contract_utils.py diff --git a/starknet_py/sent_transaction.py b/starknet_py/net/contract/sent_transaction.py similarity index 100% rename from starknet_py/sent_transaction.py rename to starknet_py/net/contract/sent_transaction.py diff --git a/starknet_py/tests/e2e/account/account_test.py b/starknet_py/tests/e2e/account/account_test.py index 01119172d..1bcb3617e 100644 --- a/starknet_py/tests/e2e/account/account_test.py +++ b/starknet_py/tests/e2e/account/account_test.py @@ -3,7 +3,6 @@ import pytest -from starknet_py.contract import Contract from starknet_py.hash.address import compute_address from starknet_py.hash.selector import get_selector_from_name from starknet_py.net.account.account import Account @@ -21,6 +20,7 @@ TransactionExecutionStatus, TransactionFinalityStatus, ) +from starknet_py.net.contract.contract import Contract from starknet_py.net.full_node_client import FullNodeClient from starknet_py.net.models import StarknetChainId from starknet_py.net.models.transaction import ( diff --git a/starknet_py/tests/e2e/block_test.py b/starknet_py/tests/e2e/block_test.py index a6e24fbb1..7f6d0373e 100644 --- a/starknet_py/tests/e2e/block_test.py +++ b/starknet_py/tests/e2e/block_test.py @@ -1,6 +1,5 @@ import pytest -from starknet_py.contract import Contract from starknet_py.net.account.base_account import BaseAccount from starknet_py.net.client_models import ( PendingStarknetBlock, @@ -8,6 +7,7 @@ StarknetBlock, StarknetBlockWithTxHashes, ) +from starknet_py.net.contract.contract import Contract from starknet_py.tests.e2e.fixtures.constants import MAX_FEE diff --git a/starknet_py/tests/e2e/cairo1v2_test.py b/starknet_py/tests/e2e/cairo1v2_test.py index 5cf98bb5f..d1645abd8 100644 --- a/starknet_py/tests/e2e/cairo1v2_test.py +++ b/starknet_py/tests/e2e/cairo1v2_test.py @@ -4,8 +4,8 @@ import pytest_asyncio from starknet_py.cairo.felt import decode_shortstring -from starknet_py.contract import Contract, DeclareResult, DeployResult from starknet_py.hash.storage import get_storage_var_address +from starknet_py.net.contract.contract import Contract, DeclareResult, DeployResult from starknet_py.tests.e2e.fixtures.constants import CONTRACTS_COMPILED_V2_DIR from starknet_py.tests.e2e.fixtures.misc import read_contract diff --git a/starknet_py/tests/e2e/client/fixtures/prepare_net_for_gateway_test.py b/starknet_py/tests/e2e/client/fixtures/prepare_net_for_gateway_test.py index 29113df46..fb96a8c9e 100755 --- a/starknet_py/tests/e2e/client/fixtures/prepare_net_for_gateway_test.py +++ b/starknet_py/tests/e2e/client/fixtures/prepare_net_for_gateway_test.py @@ -1,7 +1,7 @@ from dataclasses import dataclass -from starknet_py.contract import Contract from starknet_py.net.account.account import Account +from starknet_py.net.contract.contract import Contract from starknet_py.tests.e2e.fixtures.constants import MAX_FEE from starknet_py.tests.e2e.utils import ( AccountToBeDeployedDetails, diff --git a/starknet_py/tests/e2e/client/fixtures/transactions.py b/starknet_py/tests/e2e/client/fixtures/transactions.py index 49ca10a17..c4070f20a 100644 --- a/starknet_py/tests/e2e/client/fixtures/transactions.py +++ b/starknet_py/tests/e2e/client/fixtures/transactions.py @@ -4,8 +4,8 @@ import pytest import pytest_asyncio -from starknet_py.contract import Contract from starknet_py.net.account.account import Account +from starknet_py.net.contract.contract import Contract from starknet_py.net.full_node_client import FullNodeClient from starknet_py.net.models.transaction import DeployAccountV1 from starknet_py.net.udc_deployer.deployer import Deployer diff --git a/starknet_py/tests/e2e/client/full_node_test.py b/starknet_py/tests/e2e/client/full_node_test.py index 1d3e943da..5b633fa71 100644 --- a/starknet_py/tests/e2e/client/full_node_test.py +++ b/starknet_py/tests/e2e/client/full_node_test.py @@ -4,7 +4,6 @@ import pytest from starknet_py.common import create_casm_class -from starknet_py.contract import Contract from starknet_py.hash.address import compute_address from starknet_py.hash.casm_class_hash import compute_casm_class_hash from starknet_py.hash.selector import get_selector_from_name @@ -24,6 +23,7 @@ SyncStatus, TransactionType, ) +from starknet_py.net.contract.contract import Contract from starknet_py.net.full_node_client import _to_rpc_felt from starknet_py.net.models import StarknetChainId from starknet_py.tests.e2e.fixtures.constants import ( diff --git a/starknet_py/tests/e2e/contract_interaction/declare_test.py b/starknet_py/tests/e2e/contract_interaction/declare_test.py index d8febbc8b..3ad43dbde 100644 --- a/starknet_py/tests/e2e/contract_interaction/declare_test.py +++ b/starknet_py/tests/e2e/contract_interaction/declare_test.py @@ -1,6 +1,6 @@ import pytest -from starknet_py.contract import Contract +from starknet_py.net.contract.contract import Contract from starknet_py.net.models import DeclareV2, DeclareV3 from starknet_py.tests.e2e.fixtures.constants import ( CONTRACTS_COMPILED_V1_DIR, diff --git a/starknet_py/tests/e2e/contract_interaction/deploy_test.py b/starknet_py/tests/e2e/contract_interaction/deploy_test.py index ca3812d98..4021700c5 100644 --- a/starknet_py/tests/e2e/contract_interaction/deploy_test.py +++ b/starknet_py/tests/e2e/contract_interaction/deploy_test.py @@ -6,8 +6,8 @@ import pytest from starknet_py.common import create_sierra_compiled_contract -from starknet_py.contract import Contract, DeclareResult from starknet_py.net.client_models import InvokeTransaction, InvokeTransactionV3 +from starknet_py.net.contract.contract import Contract, DeclareResult from starknet_py.net.models import DeclareV2 from starknet_py.tests.e2e.fixtures.constants import MAX_FEE, MAX_RESOURCE_BOUNDS_L1 from starknet_py.tests.e2e.fixtures.misc import read_contract diff --git a/starknet_py/tests/e2e/contract_interaction/interaction_test.py b/starknet_py/tests/e2e/contract_interaction/interaction_test.py index e538650bd..4f910a641 100644 --- a/starknet_py/tests/e2e/contract_interaction/interaction_test.py +++ b/starknet_py/tests/e2e/contract_interaction/interaction_test.py @@ -1,13 +1,13 @@ import pytest -from starknet_py.contract import Contract -from starknet_py.contract_function import ( - PreparedFunctionInvokeV1, - PreparedFunctionInvokeV3, -) 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, ResourceBounds +from starknet_py.net.contract.contract import Contract +from starknet_py.net.contract.contract_function import ( + PreparedFunctionInvokeV1, + PreparedFunctionInvokeV3, +) from starknet_py.net.models import InvokeV1, InvokeV3 from starknet_py.tests.e2e.fixtures.constants import ( MAX_FEE, 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 67ecc92b1..c2b456650 100644 --- a/starknet_py/tests/e2e/contract_interaction/v1_interaction_test.py +++ b/starknet_py/tests/e2e/contract_interaction/v1_interaction_test.py @@ -3,7 +3,7 @@ import pytest from starknet_py.cairo.felt import decode_shortstring, encode_shortstring -from starknet_py.contract import Contract +from starknet_py.net.contract.contract import Contract from starknet_py.tests.e2e.fixtures.constants import MAX_FEE from starknet_py.tests.e2e.fixtures.contracts import deploy_v1_contract diff --git a/starknet_py/tests/e2e/deploy/deployer_test.py b/starknet_py/tests/e2e/deploy/deployer_test.py index 51db149d4..510cea5a9 100644 --- a/starknet_py/tests/e2e/deploy/deployer_test.py +++ b/starknet_py/tests/e2e/deploy/deployer_test.py @@ -1,7 +1,7 @@ import pytest -from starknet_py.contract import Contract from starknet_py.hash.address import compute_address +from starknet_py.net.contract.contract import Contract 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 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 ec3474357..af6f65294 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 @@ -1,7 +1,7 @@ import pytest -from starknet_py.contract import Contract from starknet_py.net.client import Client +from starknet_py.net.contract.contract import Contract from starknet_py.net.models import chain_from_network from starknet_py.tests.e2e.fixtures.constants import MAX_FEE from starknet_py.tests.e2e.utils import _get_random_private_key_unsafe 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 f797acc05..dc1880781 100644 --- a/starknet_py/tests/e2e/docs/code_examples/test_contract.py +++ b/starknet_py/tests/e2e/docs/code_examples/test_contract.py @@ -1,8 +1,8 @@ # pylint: disable=unused-variable import pytest -from starknet_py.contract import Contract from starknet_py.net.account.account import Account +from starknet_py.net.contract.contract import Contract 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 diff --git a/starknet_py/tests/e2e/docs/code_examples/test_contract_function.py b/starknet_py/tests/e2e/docs/code_examples/test_contract_function.py index b1064a7d1..ef5af44e8 100644 --- a/starknet_py/tests/e2e/docs/code_examples/test_contract_function.py +++ b/starknet_py/tests/e2e/docs/code_examples/test_contract_function.py @@ -1,8 +1,8 @@ # pylint: disable=unused-variable import pytest -from starknet_py.contract import Contract -from starknet_py.contract_function import ContractFunction +from starknet_py.net.contract.contract import Contract +from starknet_py.net.contract.contract_function import ContractFunction def test_prepare(map_contract: Contract): 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 2a6dd53db..e33956bf6 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 @@ -1,10 +1,10 @@ # pylint: disable=unused-variable import pytest -from starknet_py.contract import Contract 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.contract.contract import Contract from starknet_py.net.full_node_client import FullNodeClient diff --git a/starknet_py/tests/e2e/docs/code_examples/test_prepared_function_call.py b/starknet_py/tests/e2e/docs/code_examples/test_prepared_function_call.py index c91f7a722..17a386393 100644 --- a/starknet_py/tests/e2e/docs/code_examples/test_prepared_function_call.py +++ b/starknet_py/tests/e2e/docs/code_examples/test_prepared_function_call.py @@ -1,7 +1,7 @@ # pylint: disable=unused-variable import pytest -from starknet_py.contract import Contract +from starknet_py.net.contract.contract import Contract @pytest.mark.asyncio 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 533f0392a..0b8c9b2d6 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 @@ -7,7 +7,7 @@ async def test_deploying_in_multicall(account, map_class_hash, map_compiled_contract): # pylint: disable=import-outside-toplevel, # docs: start - from starknet_py.contract import Contract + from starknet_py.net.contract.contract import Contract from starknet_py.net.udc_deployer.deployer import Deployer # First, create Deployer instance. For more details see previous paragraph diff --git a/starknet_py/tests/e2e/docs/guide/test_handling_client_errors.py b/starknet_py/tests/e2e/docs/guide/test_handling_client_errors.py index ba66feeaf..db7704678 100644 --- a/starknet_py/tests/e2e/docs/guide/test_handling_client_errors.py +++ b/starknet_py/tests/e2e/docs/guide/test_handling_client_errors.py @@ -5,8 +5,8 @@ async def test_handling_client_errors(account): # pylint: disable=import-outside-toplevel # docs: start - from starknet_py.contract import Contract from starknet_py.net.client_errors import ClientError + from starknet_py.net.contract.contract import Contract try: contract_address = "1" # Doesn't exist diff --git a/starknet_py/tests/e2e/docs/guide/test_resolving_proxies.py b/starknet_py/tests/e2e/docs/guide/test_resolving_proxies.py index 30cb78601..e943e8283 100644 --- a/starknet_py/tests/e2e/docs/guide/test_resolving_proxies.py +++ b/starknet_py/tests/e2e/docs/guide/test_resolving_proxies.py @@ -23,7 +23,7 @@ async def test_resolving_proxies( ): # pylint: disable=import-outside-toplevel # docs-1: start - from starknet_py.contract import Contract + from starknet_py.net.contract.contract import Contract # docs-1: end address = map_contract.address diff --git a/starknet_py/tests/e2e/docs/guide/test_simple_declare_and_deploy.py b/starknet_py/tests/e2e/docs/guide/test_simple_declare_and_deploy.py index 23f5ef850..c55fe8fc0 100644 --- a/starknet_py/tests/e2e/docs/guide/test_simple_declare_and_deploy.py +++ b/starknet_py/tests/e2e/docs/guide/test_simple_declare_and_deploy.py @@ -5,7 +5,7 @@ async def test_simple_declare_and_deploy(account, map_compiled_contract): # pylint: disable=import-outside-toplevel # docs: start - from starknet_py.contract import Contract + from starknet_py.net.contract.contract import Contract # docs: end diff --git a/starknet_py/tests/e2e/docs/guide/test_simple_declare_and_deploy_cairo1.py b/starknet_py/tests/e2e/docs/guide/test_simple_declare_and_deploy_cairo1.py index f8f72e72c..0d31358ff 100644 --- a/starknet_py/tests/e2e/docs/guide/test_simple_declare_and_deploy_cairo1.py +++ b/starknet_py/tests/e2e/docs/guide/test_simple_declare_and_deploy_cairo1.py @@ -7,7 +7,7 @@ async def test_simple_declare_and_deploy(account): # pylint: disable=import-outside-toplevel # docs: start - from starknet_py.contract import Contract + from starknet_py.net.contract.contract import Contract # docs: end compiled_contract = read_contract("account_compiled.json") diff --git a/starknet_py/tests/e2e/docs/guide/test_simple_deploy.py b/starknet_py/tests/e2e/docs/guide/test_simple_deploy.py index 2abb18ebd..2bc40a66c 100644 --- a/starknet_py/tests/e2e/docs/guide/test_simple_deploy.py +++ b/starknet_py/tests/e2e/docs/guide/test_simple_deploy.py @@ -7,7 +7,7 @@ async def test_simple_deploy(account, map_class_hash, map_compiled_contract): # pylint: disable=import-outside-toplevel # docs: start - from starknet_py.contract import Contract + from starknet_py.net.contract.contract import Contract # docs: end 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 7a1de341f..cb150d5e2 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 @@ -18,7 +18,7 @@ async def test_simple_deploy_cairo1(account, cairo1_erc20_class_hash): # docs: start from starknet_py.cairo.felt import encode_shortstring from starknet_py.common import create_sierra_compiled_contract - from starknet_py.contract import Contract + from starknet_py.net.contract.contract import Contract # docs: end diff --git a/starknet_py/tests/e2e/docs/guide/test_using_existing_contracts.py b/starknet_py/tests/e2e/docs/guide/test_using_existing_contracts.py index 6180360e6..a3219e95e 100644 --- a/starknet_py/tests/e2e/docs/guide/test_using_existing_contracts.py +++ b/starknet_py/tests/e2e/docs/guide/test_using_existing_contracts.py @@ -27,7 +27,7 @@ async def test_using_existing_contracts(account, erc20_contract): # pylint: disable=import-outside-toplevel,too-many-locals,unused-variable # docs: start - from starknet_py.contract import Contract + from starknet_py.net.contract.contract import Contract address = "0x00178130dd6286a9a0e031e4c73b2bd04ffa92804264a25c1c08c1612559f458" diff --git a/starknet_py/tests/e2e/docs/quickstart/test_synchronous_api.py b/starknet_py/tests/e2e/docs/quickstart/test_synchronous_api.py index ab3a9a322..7ffcc6699 100644 --- a/starknet_py/tests/e2e/docs/quickstart/test_synchronous_api.py +++ b/starknet_py/tests/e2e/docs/quickstart/test_synchronous_api.py @@ -3,7 +3,7 @@ def test_synchronous_api(account, map_contract): # docs: start - from starknet_py.contract import Contract + from starknet_py.net.contract.contract import Contract contract_address = ( "0x01336fa7c870a7403aced14dda865b75f29113230ed84e3a661f7af70fe83e7b" diff --git a/starknet_py/tests/e2e/docs/quickstart/test_using_account.py b/starknet_py/tests/e2e/docs/quickstart/test_using_account.py index a6d82006b..686c8a11a 100644 --- a/starknet_py/tests/e2e/docs/quickstart/test_using_account.py +++ b/starknet_py/tests/e2e/docs/quickstart/test_using_account.py @@ -11,7 +11,7 @@ async def test_using_account(account, map_compiled_contract): # pylint: disable=import-outside-toplevel, duplicate-code, too-many-locals # docs: start - from starknet_py.contract import Contract + from starknet_py.net.contract.contract import Contract # docs: end # docs: start diff --git a/starknet_py/tests/e2e/docs/quickstart/test_using_contract.py b/starknet_py/tests/e2e/docs/quickstart/test_using_contract.py index 1203e1298..999799839 100644 --- a/starknet_py/tests/e2e/docs/quickstart/test_using_contract.py +++ b/starknet_py/tests/e2e/docs/quickstart/test_using_contract.py @@ -10,7 +10,7 @@ async def test_using_contract(account, map_contract): # pylint: disable=unused-variable,too-many-locals # docs: start - from starknet_py.contract import Contract + from starknet_py.net.contract.contract import Contract contract_address = ( "0x01336fa7c870a7403aced14dda865b75f29113230ed84e3a661f7af70fe83e7b" diff --git a/starknet_py/tests/e2e/fixtures/accounts.py b/starknet_py/tests/e2e/fixtures/accounts.py index d8f03782b..9fe2f4cb5 100644 --- a/starknet_py/tests/e2e/fixtures/accounts.py +++ b/starknet_py/tests/e2e/fixtures/accounts.py @@ -6,11 +6,11 @@ import pytest import pytest_asyncio -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.base_account import BaseAccount from starknet_py.net.client_models import PriceUnit +from starknet_py.net.contract.contract import Contract 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 diff --git a/starknet_py/tests/e2e/fixtures/contracts.py b/starknet_py/tests/e2e/fixtures/contracts.py index a74a58fb2..f11a2fe68 100644 --- a/starknet_py/tests/e2e/fixtures/contracts.py +++ b/starknet_py/tests/e2e/fixtures/contracts.py @@ -11,9 +11,9 @@ create_sierra_compiled_contract, ) from starknet_py.constants import FEE_CONTRACT_ADDRESS -from starknet_py.contract import Contract from starknet_py.hash.casm_class_hash import compute_casm_class_hash from starknet_py.net.account.base_account import BaseAccount +from starknet_py.net.contract.contract import Contract from starknet_py.net.udc_deployer.deployer import Deployer from starknet_py.tests.e2e.fixtures.constants import ( CONTRACTS_COMPILED_V0_DIR, diff --git a/starknet_py/tests/e2e/fixtures/proxy.py b/starknet_py/tests/e2e/fixtures/proxy.py index 98232bbaf..eb41f36a0 100644 --- a/starknet_py/tests/e2e/fixtures/proxy.py +++ b/starknet_py/tests/e2e/fixtures/proxy.py @@ -3,9 +3,9 @@ import pytest import pytest_asyncio -from starknet_py.contract import Contract, DeployResult from starknet_py.hash.selector import get_selector_from_name from starknet_py.net.account.account import Account +from starknet_py.net.contract.contract import Contract, DeployResult from starknet_py.tests.e2e.fixtures.constants import ( CONTRACTS_COMPILED_V0_DIR, CONTRACTS_PRECOMPILED_DIR, diff --git a/starknet_py/tests/e2e/proxy/proxy_test.py b/starknet_py/tests/e2e/proxy/proxy_test.py index 770a41444..ee63e36af 100644 --- a/starknet_py/tests/e2e/proxy/proxy_test.py +++ b/starknet_py/tests/e2e/proxy/proxy_test.py @@ -2,10 +2,10 @@ import pytest -from starknet_py.contract import Contract from starknet_py.hash.storage import get_storage_var_address from starknet_py.net.client import Client from starknet_py.net.client_errors import ContractNotFoundError +from starknet_py.net.contract.contract import Contract from starknet_py.net.models import Address from starknet_py.proxy.contract_abi_resolver import ProxyResolutionError from starknet_py.proxy.proxy_check import ProxyCheck 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 90053ecff..933067be1 100644 --- a/starknet_py/tests/e2e/tests_on_networks/account_test.py +++ b/starknet_py/tests/e2e/tests_on_networks/account_test.py @@ -1,7 +1,7 @@ import pytest -from starknet_py.contract import Contract from starknet_py.net.client_errors import ClientError +from starknet_py.net.contract.contract import Contract from starknet_py.tests.e2e.fixtures.constants import ( MAP_CONTRACT_ADDRESS_GOERLI_INTEGRATION, MAX_FEE, diff --git a/starknet_py/tests/e2e/utils.py b/starknet_py/tests/e2e/utils.py index cdda24975..12a4d6781 100644 --- a/starknet_py/tests/e2e/utils.py +++ b/starknet_py/tests/e2e/utils.py @@ -2,10 +2,10 @@ 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.contract.contract import Contract from starknet_py.net.http_client import HttpClient, HttpMethod from starknet_py.net.models import StarknetChainId from starknet_py.net.models.transaction import DeployAccountV1 From 040824854e9961d9b76dee1e2ebae71170104e73 Mon Sep 17 00:00:00 2001 From: Dariusz Doktorski Date: Wed, 24 Jan 2024 21:32:07 +0100 Subject: [PATCH 14/24] Ignore pylint locals for `deploy_contract` methods to fix lint --- starknet_py/net/contract/contract.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/starknet_py/net/contract/contract.py b/starknet_py/net/contract/contract.py index 42119498a..aeae0961d 100644 --- a/starknet_py/net/contract/contract.py +++ b/starknet_py/net/contract/contract.py @@ -456,7 +456,7 @@ async def deploy_contract_v1( :param unique: Determines if the contract should be salted with the account address. :return: DeployResult instance. """ - # pylint: disable=too-many-arguments + # pylint: disable=too-many-arguments, too-many-locals deployer = Deployer( deployer_address=deployer_address, account_address=account.address if unique else None, @@ -522,7 +522,7 @@ async def deploy_contract_v3( :param unique: Determines if the contract should be salted with the account address. :return: DeployResult instance. """ - # pylint: disable=too-many-arguments + # pylint: disable=too-many-arguments, too-many-locals deployer = Deployer( deployer_address=deployer_address, account_address=account.address if unique else None, From 5cce06ceca77b82033bafd27186f8a823c946284 Mon Sep 17 00:00:00 2001 From: Dariusz Doktorski Date: Thu, 25 Jan 2024 08:40:44 +0100 Subject: [PATCH 15/24] Add `_declare_contract` function --- starknet_py/net/contract/contract.py | 54 +++++++++++++--------------- 1 file changed, 25 insertions(+), 29 deletions(-) diff --git a/starknet_py/net/contract/contract.py b/starknet_py/net/contract/contract.py index aeae0961d..32dd75fa8 100644 --- a/starknet_py/net/contract/contract.py +++ b/starknet_py/net/contract/contract.py @@ -313,16 +313,9 @@ async def declare_v1( max_fee=max_fee, auto_estimate=auto_estimate, ) - res = await account.client.declare(transaction=declare_tx) - return DeclareResult( - hash=res.transaction_hash, - class_hash=res.class_hash, - compiled_contract=compiled_contract, - declare_transaction=declare_tx, - _account=account, - _client=account.client, - _cairo_version=0, + return await _declare_contract( + declare_tx, account, compiled_contract, cairo_version=0 ) @staticmethod @@ -361,16 +354,8 @@ async def declare_v2( auto_estimate=auto_estimate, ) - res = await account.client.declare(transaction=declare_tx) - - return DeclareResult( - hash=res.transaction_hash, - class_hash=res.class_hash, - compiled_contract=compiled_contract, - declare_transaction=declare_tx, - _account=account, - _client=account.client, - _cairo_version=1, + return await _declare_contract( + declare_tx, account, compiled_contract, cairo_version=1 ) @staticmethod @@ -410,16 +395,8 @@ async def declare_v3( auto_estimate=auto_estimate, ) - res = await account.client.declare(transaction=declare_tx) - - return DeclareResult( - hash=res.transaction_hash, - class_hash=res.class_hash, - compiled_contract=compiled_contract, - declare_transaction=declare_tx, - _account=account, - _client=account.client, - _cairo_version=1, + return await _declare_contract( + declare_tx, account, compiled_contract, cairo_version=1 ) @staticmethod @@ -644,3 +621,22 @@ def _create_proxy_config(proxy_config) -> ProxyConfig: return ProxyConfig() proxy_arg = ProxyConfig() if proxy_config is True else proxy_config return prepare_proxy_config(proxy_arg) + + +async def _declare_contract( + transaction: Declare, + account: BaseAccount, + compiled_contract: str, + cairo_version: int, +) -> DeclareResult: + res = await account.client.declare(transaction=transaction) + + return DeclareResult( + hash=res.transaction_hash, + class_hash=res.class_hash, + compiled_contract=compiled_contract, + declare_transaction=transaction, + _account=account, + _client=account.client, + _cairo_version=cairo_version, + ) From 08c0a1b072fdd9cd407f070105d98289d1fad612 Mon Sep 17 00:00:00 2001 From: Dariusz Doktorski Date: Tue, 30 Jan 2024 15:49:33 +0100 Subject: [PATCH 16/24] Rename `CallToSend` to `PreparedCallBase` --- starknet_py/net/contract/contract_function.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/starknet_py/net/contract/contract_function.py b/starknet_py/net/contract/contract_function.py index 8f7fb1094..767bbdce7 100644 --- a/starknet_py/net/contract/contract_function.py +++ b/starknet_py/net/contract/contract_function.py @@ -90,14 +90,14 @@ def __post_init__(self): @dataclass -class CallToSend(Call): +class PreparedCallBase(Call): _client: Client _payload_transformer: FunctionSerializationAdapter @add_sync_methods @dataclass -class PreparedFunctionCall(CallToSend): +class PreparedFunctionCall(PreparedCallBase): async def call_raw( self, block_hash: Optional[str] = None, @@ -132,7 +132,7 @@ async def call( @add_sync_methods @dataclass -class PreparedFunctionInvoke(ABC, CallToSend): +class PreparedFunctionInvoke(ABC, PreparedCallBase): _contract_data: ContractData _account: Optional[BaseAccount] From 67494845ecf49701256c95a1aa499e3a3e78bdd8 Mon Sep 17 00:00:00 2001 From: Dariusz Doktorski Date: Tue, 30 Jan 2024 16:14:24 +0100 Subject: [PATCH 17/24] Replace Wei with Fri in docstrings --- starknet_py/net/contract/contract.py | 6 +++--- starknet_py/net/contract/contract_function.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/starknet_py/net/contract/contract.py b/starknet_py/net/contract/contract.py index 32dd75fa8..74ffecea1 100644 --- a/starknet_py/net/contract/contract.py +++ b/starknet_py/net/contract/contract.py @@ -132,7 +132,7 @@ async def deploy_v3( :param unique: Determines if the contract should be salted with the account address. :param constructor_args: a ``list`` or ``dict`` of arguments for the constructor. :param nonce: Nonce of the transaction with call to deployer. - :param l1_resource_bounds: Max amount and max price per unit of L1 gas (in Wei) used when executing + :param l1_resource_bounds: Max amount and max price per unit of L1 gas (in Fri) used when executing this transaction. :param auto_estimate: Use automatic fee estimation (not recommended, as it may lead to high costs). :return: DeployResult instance. @@ -377,7 +377,7 @@ async def declare_v3( :param compiled_contract_casm: String containing the content of the starknet-sierra-compile (.casm file). :param compiled_class_hash: Hash of the compiled_contract_casm. :param nonce: Nonce of the transaction. - :param l1_resource_bounds: Max amount and max price per unit of L1 gas (in Wei) used when executing + :param l1_resource_bounds: Max amount and max price per unit of L1 gas (in Fri) used when executing this transaction. :param auto_estimate: Use automatic fee estimation (not recommended, as it may lead to high costs). :return: DeclareResult instance. @@ -492,7 +492,7 @@ async def deploy_contract_v3( :param cairo_version: Version of the Cairo in which contract is written. By default, it is set to 1. :param nonce: Nonce of the transaction. - :param l1_resource_bounds: Max amount and max price per unit of L1 gas (in Wei) used when executing + :param l1_resource_bounds: Max amount and max price per unit of L1 gas (in Fri) used when executing this transaction. :param auto_estimate: Use automatic fee estimation (not recommended, as it may lead to high costs). :param salt: Optional salt. Random value is selected if it is not provided. diff --git a/starknet_py/net/contract/contract_function.py b/starknet_py/net/contract/contract_function.py index 767bbdce7..f6fbda390 100644 --- a/starknet_py/net/contract/contract_function.py +++ b/starknet_py/net/contract/contract_function.py @@ -250,7 +250,7 @@ async def invoke( """ Send an Invoke transaction version 3 for a prepared data. - :param l1_resource_bounds: Max amount and max price per unit of L1 gas (in Wei) used when executing + :param l1_resource_bounds: Max amount and max price per unit of L1 gas (in Fri) used when executing this transaction. :param auto_estimate: Use automatic fee estimation, not recommend as it may lead to high costs. :param nonce: Nonce of the transaction. @@ -430,7 +430,7 @@ def prepare_invoke_v3( Creates a ``PreparedFunctionInvokeV3`` instance which exposes calldata for every argument and adds more arguments when calling methods. - :param l1_resource_bounds: Max amount and max price per unit of L1 gas (in Wei) used when executing + :param l1_resource_bounds: Max amount and max price per unit of L1 gas (in Fri) used when executing this transaction. :return: PreparedFunctionInvokeV3. """ @@ -459,7 +459,7 @@ async def invoke_v3( Invoke contract's function. ``*args`` and ``**kwargs`` are translated into Cairo calldata. Equivalent of ``.prepare_invoke_v3(*args, **kwargs).invoke()``. - :param l1_resource_bounds: Max amount and max price per unit of L1 gas (in Wei) used when executing + :param l1_resource_bounds: Max amount and max price per unit of L1 gas (in Fri) used when executing this transaction. :param auto_estimate: Use automatic fee estimation, not recommend as it may lead to high costs. :param nonce: Nonce of the transaction. From aa5f517d69d52de99d9b0dcf05d5eb5dc66f9124 Mon Sep 17 00:00:00 2001 From: Dariusz Doktorski Date: Tue, 30 Jan 2024 16:31:52 +0100 Subject: [PATCH 18/24] Add test to get balance in STRK --- starknet_py/net/account/account_test.py | 27 +++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/starknet_py/net/account/account_test.py b/starknet_py/net/account/account_test.py index a8132dcfa..bcb22b1cf 100644 --- a/starknet_py/net/account/account_test.py +++ b/starknet_py/net/account/account_test.py @@ -8,7 +8,11 @@ from starknet_py.net.models import StarknetChainId, parse_address from starknet_py.net.networks import GOERLI, MAINNET from starknet_py.net.signer.stark_curve_signer import KeyPair, StarkCurveSigner -from starknet_py.tests.e2e.fixtures.constants import MAX_FEE +from starknet_py.tests.e2e.fixtures.constants import ( + MAX_FEE, + MAX_RESOURCE_BOUNDS_L1, + STRK_FEE_CONTRACT_ADDRESS, +) @pytest.mark.asyncio @@ -38,7 +42,7 @@ async def test_get_balance_default_token_address(net): @pytest.mark.asyncio -async def test_account_get_balance(account, map_contract): +async def test_account_get_balance_eth(account, map_contract): balance = await account.get_balance() block = await account.client.get_block(block_number="latest") @@ -52,6 +56,25 @@ async def test_account_get_balance(account, map_contract): assert old_balance == balance +@pytest.mark.asyncio +async def test_account_get_balance_strk(account, map_contract): + balance = await account.get_balance(token_address=STRK_FEE_CONTRACT_ADDRESS) + block = await account.client.get_block(block_number="latest") + + await map_contract.functions["put"].invoke_v3( + key=10, value=10, l1_resource_bounds=MAX_RESOURCE_BOUNDS_L1 + ) + + new_balance = await account.get_balance(token_address=STRK_FEE_CONTRACT_ADDRESS) + old_balance = await account.get_balance( + token_address=STRK_FEE_CONTRACT_ADDRESS, block_number=block.block_number + ) + + assert balance > 0 + assert new_balance < balance + assert old_balance == balance + + def test_create_account(): key_pair = KeyPair.from_private_key(0x111) account = Account( From 00077a39407f18ec669a9723616fc9a7b1c36b81 Mon Sep 17 00:00:00 2001 From: Dariusz Doktorski Date: Tue, 30 Jan 2024 16:37:16 +0100 Subject: [PATCH 19/24] Update docstrings for deploy methods about sepolia network --- starknet_py/net/contract/contract.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/starknet_py/net/contract/contract.py b/starknet_py/net/contract/contract.py index 74ffecea1..4f2d62454 100644 --- a/starknet_py/net/contract/contract.py +++ b/starknet_py/net/contract/contract.py @@ -84,7 +84,7 @@ async def deploy_v1( Deploys a contract. :param deployer_address: Address of the UDC. Is set to the address of - the default UDC (same address on mainnet/testnet/devnet) by default. + the default UDC (same address on mainnet/goerli/sepolia) by default. Must be set when using custom network other than ones listed above. :param salt: Optional salt. Random value is selected if it is not provided. :param unique: Determines if the contract should be salted with the account address. @@ -126,7 +126,7 @@ async def deploy_v3( Deploys a contract. :param deployer_address: Address of the UDC. Is set to the address of - the default UDC (same address on mainnet/testnet/devnet) by default. + the default UDC (same address on mainnet/goerli/sepolia) by default. Must be set when using custom network other than ones listed above. :param salt: Optional salt. Random value is selected if it is not provided. :param unique: Determines if the contract should be salted with the account address. @@ -422,7 +422,7 @@ async def deploy_contract_v1( :param abi: An abi of the contract to be deployed. :param constructor_args: a ``list`` or ``dict`` of arguments for the constructor. :param deployer_address: Address of the UDC. Is set to the address of - the default UDC (same address on mainnet/testnet/devnet) by default. + the default UDC (same address on mainnet/goerli/sepolia) by default. Must be set when using custom network other than ones listed above. :param cairo_version: Version of the Cairo in which contract is written. By default, it is set to 0. @@ -487,7 +487,7 @@ async def deploy_contract_v3( :param abi: An abi of the contract to be deployed. :param constructor_args: a ``list`` or ``dict`` of arguments for the constructor. :param deployer_address: Address of the UDC. Is set to the address of - the default UDC (same address on mainnet/testnet/devnet) by default. + the default UDC (same address on mainnet/goerli/sepolia) by default. Must be set when using custom network other than ones listed above. :param cairo_version: Version of the Cairo in which contract is written. By default, it is set to 1. From d53c474abef3e757af3f6c1488c343c2efb3ef52 Mon Sep 17 00:00:00 2001 From: Dariusz Doktorski Date: Tue, 30 Jan 2024 16:54:25 +0100 Subject: [PATCH 20/24] Add version postfix for `Abi` and `AbiParser` imports --- starknet_py/net/contract/contract_function.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/starknet_py/net/contract/contract_function.py b/starknet_py/net/contract/contract_function.py index f6fbda390..d9957d65e 100644 --- a/starknet_py/net/contract/contract_function.py +++ b/starknet_py/net/contract/contract_function.py @@ -5,7 +5,8 @@ from functools import cached_property from typing import List, Optional, Union -from starknet_py.abi import Abi, AbiParser +from starknet_py.abi import Abi as AbiV0 +from starknet_py.abi import AbiParser as AbiV0Parser from starknet_py.abi.v1.model import Abi as AbiV1 from starknet_py.abi.v1.parser import AbiParser as AbiV1Parser from starknet_py.abi.v2.model import Abi as AbiV2 @@ -41,7 +42,7 @@ class ContractData: cairo_version: int @cached_property - def parsed_abi(self) -> Union[Abi, AbiV1, AbiV2]: + def parsed_abi(self) -> Union[AbiV0, AbiV1, AbiV2]: """ Abi parsed into proper dataclass. @@ -51,7 +52,7 @@ def parsed_abi(self) -> Union[Abi, AbiV1, AbiV2]: if _is_abi_v2(self.abi): return AbiV2Parser(self.abi).parse() return AbiV1Parser(self.abi).parse() - return AbiParser(self.abi).parse() + return AbiV0Parser(self.abi).parse() @staticmethod def from_abi(address: int, abi: ABI, cairo_version: int = 0) -> ContractData: @@ -321,11 +322,11 @@ def __init__( assert function is not None if cairo_version == 1: - assert not isinstance(function, Abi.Function) and function is not None + assert not isinstance(function, AbiV0.Function) and function is not None self._payload_transformer = serializer_for_function_v1(function) else: - assert isinstance(function, Abi.Function) and function is not None + assert isinstance(function, AbiV0.Function) and function is not None self._payload_transformer = serializer_for_function(function) def prepare_call( From fbf41288cda7d9916df52b69dfa6b5dc5815c575 Mon Sep 17 00:00:00 2001 From: Dariusz Doktorski Date: Tue, 30 Jan 2024 17:04:20 +0100 Subject: [PATCH 21/24] Use query version for fee estimation --- starknet_py/net/contract/contract_function.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/starknet_py/net/contract/contract_function.py b/starknet_py/net/contract/contract_function.py index d9957d65e..e7448980a 100644 --- a/starknet_py/net/contract/contract_function.py +++ b/starknet_py/net/contract/contract_function.py @@ -225,9 +225,10 @@ async def estimate_fee( tx = await self.get_account.sign_invoke_v1_transaction( calls=self, nonce=nonce, max_fee=0 ) + estimate_tx = await self.get_account.sign_for_fee_estimate(transaction=tx) estimated_fee = await self._client.estimate_fee( - tx=tx, + tx=estimate_tx, block_hash=block_hash, block_number=block_number, ) @@ -277,9 +278,10 @@ async def estimate_fee( tx = await self.get_account.sign_invoke_v3_transaction( calls=self, nonce=nonce, l1_resource_bounds=ResourceBounds.init_with_zeros() ) + estimate_tx = await self.get_account.sign_for_fee_estimate(transaction=tx) estimated_fee = await self._client.estimate_fee( - tx=tx, + tx=estimate_tx, block_hash=block_hash, block_number=block_number, ) From d79f55107cc3795adbb716eb45915ef36074ca4d Mon Sep 17 00:00:00 2001 From: Dariusz Doktorski Date: Tue, 30 Jan 2024 19:43:27 +0100 Subject: [PATCH 22/24] Undo contract refactoring --- README.md | 4 +- starknet_py/{net/contract => }/contract.py | 539 +++++++++++++++++- .../{net/contract => }/contract_test.py | 2 +- .../{net/contract => }/contract_utils.py | 0 .../net/account/account_deployment_result.py | 2 +- starknet_py/net/contract/__init__.py | 0 starknet_py/net/contract/contract_function.py | 484 ---------------- starknet_py/net/contract/sent_transaction.py | 57 -- starknet_py/tests/e2e/account/account_test.py | 2 +- starknet_py/tests/e2e/block_test.py | 2 +- starknet_py/tests/e2e/cairo1v2_test.py | 2 +- .../fixtures/prepare_net_for_gateway_test.py | 2 +- .../tests/e2e/client/fixtures/transactions.py | 2 +- .../tests/e2e/client/full_node_test.py | 2 +- .../e2e/contract_interaction/declare_test.py | 2 +- .../e2e/contract_interaction/deploy_test.py | 2 +- .../contract_interaction/interaction_test.py | 10 +- .../v1_interaction_test.py | 2 +- starknet_py/tests/e2e/deploy/deployer_test.py | 2 +- .../test_deploy_prefunded_account.py | 2 +- .../e2e/docs/code_examples/test_contract.py | 2 +- .../code_examples/test_contract_function.py | 3 +- .../code_examples/test_full_node_client.py | 2 +- .../test_prepared_function_call.py | 2 +- .../docs/guide/test_deploying_in_multicall.py | 2 +- .../docs/guide/test_handling_client_errors.py | 2 +- .../e2e/docs/guide/test_resolving_proxies.py | 2 +- .../guide/test_simple_declare_and_deploy.py | 2 +- .../test_simple_declare_and_deploy_cairo1.py | 2 +- .../e2e/docs/guide/test_simple_deploy.py | 2 +- .../docs/guide/test_simple_deploy_cairo1.py | 2 +- .../guide/test_using_existing_contracts.py | 2 +- .../docs/quickstart/test_synchronous_api.py | 2 +- .../e2e/docs/quickstart/test_using_account.py | 2 +- .../docs/quickstart/test_using_contract.py | 2 +- starknet_py/tests/e2e/fixtures/accounts.py | 2 +- starknet_py/tests/e2e/fixtures/contracts.py | 2 +- starknet_py/tests/e2e/fixtures/proxy.py | 2 +- starknet_py/tests/e2e/proxy/proxy_test.py | 2 +- .../e2e/tests_on_networks/account_test.py | 2 +- starknet_py/tests/e2e/utils.py | 2 +- 41 files changed, 570 insertions(+), 593 deletions(-) rename starknet_py/{net/contract => }/contract.py (57%) rename starknet_py/{net/contract => }/contract_test.py (96%) rename starknet_py/{net/contract => }/contract_utils.py (100%) delete mode 100644 starknet_py/net/contract/__init__.py delete mode 100644 starknet_py/net/contract/contract_function.py delete mode 100644 starknet_py/net/contract/sent_transaction.py diff --git a/README.md b/README.md index c285d2c30..62b7e0ad3 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ Installation varies between operating systems. This is the recommended way of using the SDK. ```python -from starknet_py.net.contract.contract import Contract +from starknet_py.contract import Contract from starknet_py.net.full_node_client import FullNodeClient contract = await Contract.from_address( @@ -51,7 +51,7 @@ contract = await Contract.from_address( You can access synchronous world with `_sync` postfix. ```python -from starknet_py.net.contract.contract import Contract +from starknet_py.contract import Contract from starknet_py.net.full_node_client import FullNodeClient contract = Contract.from_address_sync( diff --git a/starknet_py/net/contract/contract.py b/starknet_py/contract.py similarity index 57% rename from starknet_py/net/contract/contract.py rename to starknet_py/contract.py index 4f2d62454..0131ff489 100644 --- a/starknet_py/net/contract/contract.py +++ b/starknet_py/contract.py @@ -1,11 +1,21 @@ from __future__ import annotations +import dataclasses import json +import warnings +from abc import ABC, abstractmethod from dataclasses import dataclass -from typing import Dict, List, Optional, Union +from functools import cached_property +from typing import Dict, List, Optional, TypeVar, Union from marshmallow import ValidationError +from starknet_py.abi import Abi as AbiV0 +from starknet_py.abi import AbiParser as AbiV0Parser +from starknet_py.abi.v1.model import Abi as AbiV1 +from starknet_py.abi.v1.parser import AbiParser as AbiV1Parser +from starknet_py.abi.v2.model import Abi as AbiV2 +from starknet_py.abi.v2.parser import AbiParser as AbiV2Parser from starknet_py.abi.v2.shape import ( FUNCTION_ENTRY, IMPL_ENTRY, @@ -14,28 +24,143 @@ ) from starknet_py.common import create_compiled_contract, create_sierra_compiled_contract from starknet_py.constants import DEFAULT_DEPLOYER_ADDRESS +from starknet_py.contract_utils import _extract_compiled_class_hash, _unpack_provider from starknet_py.hash.address import compute_address from starknet_py.hash.class_hash import compute_class_hash +from starknet_py.hash.selector import get_selector_from_name from starknet_py.net.account.base_account import BaseAccount from starknet_py.net.client import Client -from starknet_py.net.client_models import Hash, ResourceBounds -from starknet_py.net.contract.contract_function import ContractData, ContractFunction -from starknet_py.net.contract.contract_utils import ( - _extract_compiled_class_hash, - _unpack_provider, -) -from starknet_py.net.contract.sent_transaction import SentTransaction +from starknet_py.net.client_models import Call, EstimatedFee, Hash, ResourceBounds, Tag from starknet_py.net.models import AddressRepresentation, parse_address -from starknet_py.net.models.transaction import Declare +from starknet_py.net.models.transaction import Declare, Invoke from starknet_py.net.udc_deployer.deployer import Deployer from starknet_py.proxy.contract_abi_resolver import ( ContractAbiResolver, ProxyConfig, prepare_proxy_config, ) -from starknet_py.utils.constructor_args_translator import translate_constructor_args +from starknet_py.serialization import ( + FunctionSerializationAdapter, + TupleDataclass, + serializer_for_function, +) +from starknet_py.serialization.factory import serializer_for_function_v1 +from starknet_py.utils.constructor_args_translator import ( + _is_abi_v2, + translate_constructor_args, +) from starknet_py.utils.sync import add_sync_methods +# pylint: disable=too-many-lines + +ABI = list +ABIEntry = dict +TypeSentTransaction = TypeVar("TypeSentTransaction", bound="SentTransaction") + + +@dataclass(frozen=True) +class ContractData: + """ + Basic data of a deployed contract. + """ + + address: int + abi: ABI + cairo_version: int + + @cached_property + def parsed_abi(self) -> Union[AbiV0, AbiV1, AbiV2]: + """ + Abi parsed into proper dataclass. + + :return: Abi + """ + if self.cairo_version == 1: + if _is_abi_v2(self.abi): + return AbiV2Parser(self.abi).parse() + return AbiV1Parser(self.abi).parse() + return AbiV0Parser(self.abi).parse() + + @staticmethod + def from_abi(address: int, abi: ABI, cairo_version: int = 0) -> ContractData: + """ + Create ContractData from ABI. + + :param address: Address of the deployed contract. + :param abi: Abi of the contract. + :param cairo_version: Version of the Cairo in which contract is written. + :return: ContractData instance. + """ + return ContractData( + address=address, + abi=abi, + cairo_version=cairo_version, + ) + + +@add_sync_methods +@dataclass(frozen=True) +class SentTransaction: + """ + Dataclass exposing the interface of transaction related to a performed action. + """ + + hash: int + """Hash of the transaction.""" + + _client: Client + status: Optional[str] = None + """Status of the transaction.""" + + block_number: Optional[int] = None + """Number of the block in which transaction was included.""" + + async def wait_for_acceptance( + self: TypeSentTransaction, + wait_for_accept: Optional[bool] = None, + check_interval: float = 2, + retries: int = 500, + ) -> TypeSentTransaction: + """ + Waits for transaction to be accepted on chain till ``ACCEPTED`` status. + Returns a new SentTransaction instance, **does not mutate original instance**. + """ + if wait_for_accept is not None: + warnings.warn( + "Parameter `wait_for_accept` has been deprecated - since Starknet 0.12.0, transactions in a PENDING" + " block have status ACCEPTED_ON_L2." + ) + + tx_receipt = await self._client.wait_for_tx( + self.hash, + check_interval=check_interval, + retries=retries, + ) + return dataclasses.replace( + self, + status=tx_receipt.finality_status, + block_number=tx_receipt.block_number, + ) + + +@add_sync_methods +@dataclass(frozen=True) +class InvokeResult(SentTransaction): + """ + Result of the Invoke transaction. + """ + + # We ensure these are not None in __post_init__ + contract: ContractData = None # pyright: ignore + """Additional information about the Contract that made the transaction.""" + + invoke_transaction: Invoke = None # pyright: ignore + """A InvokeTransaction instance used.""" + + def __post_init__(self): + assert self.contract is not None + assert self.invoke_transaction is not None + @add_sync_methods @dataclass(frozen=True) @@ -187,6 +312,400 @@ def __post_init__(self): raise ValueError("Argument deployed_contract can't be None.") +@dataclass +class PreparedCallBase(Call): + _client: Client + _payload_transformer: FunctionSerializationAdapter + + +@add_sync_methods +@dataclass +class PreparedFunctionCall(PreparedCallBase): + async def call_raw( + self, + block_hash: Optional[str] = None, + block_number: Optional[Union[int, Tag]] = None, + ) -> List[int]: + """ + Calls a method without translating the result into python values. + + :param block_hash: Optional block hash. + :param block_number: Optional block number. + :return: list of ints. + """ + return await self._client.call_contract( + call=self, block_hash=block_hash, block_number=block_number + ) + + async def call( + self, + block_hash: Optional[str] = None, + block_number: Optional[Union[int, Tag]] = None, + ) -> TupleDataclass: + """ + Calls a method. + + :param block_hash: Optional block hash. + :param block_number: Optional block number. + :return: TupleDataclass representing call result. + """ + result = await self.call_raw(block_hash=block_hash, block_number=block_number) + return self._payload_transformer.deserialize(result) + + +@add_sync_methods +@dataclass +class PreparedFunctionInvoke(ABC, PreparedCallBase): + _contract_data: ContractData + _account: Optional[BaseAccount] + + def __post_init__(self): + if self._account is None: + raise ValueError( + "Contract instance was created without providing an Account. " + "It is not possible to prepare and send an invoke transaction." + ) + + @property + def get_account(self): + if self._account is not None: + return self._account + + raise ValueError( + "The account is not defined. It is not possible to send an invoke transaction." + ) + + @abstractmethod + async def estimate_fee( + self, + block_hash: Optional[Union[Hash, Tag]] = None, + block_number: Optional[Union[int, Tag]] = None, + *, + nonce: Optional[int] = None, + ) -> EstimatedFee: + """ + Estimate fee for prepared function call. + + :param block_hash: Estimate fee at specific block hash. + :param block_number: Estimate fee at given block number + (or "latest" / "pending" for the latest / pending block), default is "pending". + :param nonce: Nonce of the transaction. + :return: Estimated amount of the transaction cost, either in Wei or Fri associated with executing the + specified transaction. + """ + + async def _invoke(self, transaction: Invoke) -> InvokeResult: + response = await self._client.send_transaction(transaction) + + invoke_result = InvokeResult( + hash=response.transaction_hash, # noinspection PyTypeChecker + _client=self._client, + contract=self._contract_data, + invoke_transaction=transaction, + ) + + return invoke_result + + +@add_sync_methods +@dataclass +class PreparedFunctionInvokeV1(PreparedFunctionInvoke): + max_fee: Optional[int] + + async def invoke( + self, + max_fee: Optional[int] = None, + auto_estimate: bool = False, + *, + nonce: Optional[int] = None, + ) -> InvokeResult: + """ + Send an Invoke transaction version 1 for a prepared data. + + :param max_fee: Max amount of Wei to be paid when executing transaction. + :param auto_estimate: Use automatic fee estimation, not recommend as it may lead to high costs. + :param nonce: Nonce of the transaction. + :return: InvokeResult. + """ + + transaction = await self.get_account.sign_invoke_v1_transaction( + calls=self, + nonce=nonce, + max_fee=max_fee or self.max_fee, + auto_estimate=auto_estimate, + ) + + return await self._invoke(transaction) + + async def estimate_fee( + self, + block_hash: Optional[Union[Hash, Tag]] = None, + block_number: Optional[Union[int, Tag]] = None, + *, + nonce: Optional[int] = None, + ) -> EstimatedFee: + tx = await self.get_account.sign_invoke_v1_transaction( + calls=self, nonce=nonce, max_fee=0 + ) + estimate_tx = await self.get_account.sign_for_fee_estimate(transaction=tx) + + estimated_fee = await self._client.estimate_fee( + tx=estimate_tx, + block_hash=block_hash, + block_number=block_number, + ) + + assert isinstance(estimated_fee, EstimatedFee) + return estimated_fee + + +@add_sync_methods +@dataclass +class PreparedFunctionInvokeV3(PreparedFunctionInvoke): + l1_resource_bounds: Optional[ResourceBounds] + + async def invoke( + self, + l1_resource_bounds: Optional[ResourceBounds] = None, + auto_estimate: bool = False, + *, + nonce: Optional[int] = None, + ) -> InvokeResult: + """ + Send an Invoke transaction version 3 for a prepared data. + + :param l1_resource_bounds: Max amount and max price per unit of L1 gas (in Fri) used when executing + this transaction. + :param auto_estimate: Use automatic fee estimation, not recommend as it may lead to high costs. + :param nonce: Nonce of the transaction. + :return: InvokeResult. + """ + + transaction = await self.get_account.sign_invoke_v3_transaction( + calls=self, + nonce=nonce, + l1_resource_bounds=l1_resource_bounds or self.l1_resource_bounds, + auto_estimate=auto_estimate, + ) + + return await self._invoke(transaction) + + async def estimate_fee( + self, + block_hash: Optional[Union[Hash, Tag]] = None, + block_number: Optional[Union[int, Tag]] = None, + *, + nonce: Optional[int] = None, + ) -> EstimatedFee: + tx = await self.get_account.sign_invoke_v3_transaction( + calls=self, nonce=nonce, l1_resource_bounds=ResourceBounds.init_with_zeros() + ) + estimate_tx = await self.get_account.sign_for_fee_estimate(transaction=tx) + + estimated_fee = await self._client.estimate_fee( + tx=estimate_tx, + block_hash=block_hash, + block_number=block_number, + ) + + assert isinstance(estimated_fee, EstimatedFee) + return estimated_fee + + +@add_sync_methods +class ContractFunction: + def __init__( + self, + name: str, + abi: ABIEntry, + contract_data: ContractData, + client: Client, + account: Optional[BaseAccount], + cairo_version: int = 0, + *, + interface_name: Optional[str] = None, + ): + # pylint: disable=too-many-arguments + self.name = name + self.abi = abi + self.inputs = abi["inputs"] + self.contract_data = contract_data + self.client = client + self.account = account + + if abi["type"] == L1_HANDLER_ENTRY: + assert not isinstance(contract_data.parsed_abi, AbiV1) + function = contract_data.parsed_abi.l1_handler + elif interface_name is None: + function = contract_data.parsed_abi.functions.get(name) + else: + assert isinstance(contract_data.parsed_abi, AbiV2) + interface = contract_data.parsed_abi.interfaces[interface_name] + function = interface.items[name] + + assert function is not None + + if cairo_version == 1: + assert not isinstance(function, AbiV0.Function) and function is not None + self._payload_transformer = serializer_for_function_v1(function) + + else: + assert isinstance(function, AbiV0.Function) and function is not None + self._payload_transformer = serializer_for_function(function) + + def prepare_call( + self, + *args, + **kwargs, + ) -> PreparedFunctionCall: + """ + ``*args`` and ``**kwargs`` are translated into Cairo calldata. + Creates a ``PreparedFunctionCall`` instance which exposes calldata for every argument + and adds more arguments when calling methods. + + :return: PreparedFunctionCall. + """ + + calldata = self._payload_transformer.serialize(*args, **kwargs) + return PreparedFunctionCall( + to_addr=self.contract_data.address, + calldata=calldata, + selector=self.get_selector(self.name), + _client=self.client, + _payload_transformer=self._payload_transformer, + ) + + async def call( + self, + *args, + block_hash: Optional[str] = None, + block_number: Optional[Union[int, Tag]] = None, + **kwargs, + ) -> TupleDataclass: + """ + Call contract's function. ``*args`` and ``**kwargs`` are translated into Cairo calldata. + The result is translated from Cairo data to python values. + Equivalent of ``.prepare_call(*args, **kwargs).call()``. + + :param block_hash: Block hash to perform the call to the contract at specific point of time. + :param block_number: Block number to perform the call to the contract at specific point of time. + :return: TupleDataclass representing call result. + """ + return await self.prepare_call(*args, **kwargs).call( + block_hash=block_hash, block_number=block_number + ) + + def prepare_invoke_v1( + self, + *args, + max_fee: Optional[int] = None, + **kwargs, + ) -> PreparedFunctionInvokeV1: + """ + ``*args`` and ``**kwargs`` are translated into Cairo calldata. + Creates a ``PreparedFunctionInvokeV1`` instance which exposes calldata for every argument + and adds more arguments when calling methods. + + :param max_fee: Max amount of Wei to be paid when executing transaction. + :return: PreparedFunctionCall. + """ + + calldata = self._payload_transformer.serialize(*args, **kwargs) + return PreparedFunctionInvokeV1( + to_addr=self.contract_data.address, + calldata=calldata, + selector=self.get_selector(self.name), + max_fee=max_fee, + _contract_data=self.contract_data, + _client=self.client, + _account=self.account, + _payload_transformer=self._payload_transformer, + ) + + async def invoke_v1( + self, + *args, + max_fee: Optional[int] = None, + auto_estimate: bool = False, + nonce: Optional[int] = None, + **kwargs, + ) -> InvokeResult: + """ + Invoke contract's function. ``*args`` and ``**kwargs`` are translated into Cairo calldata. + Equivalent of ``.prepare_invoke_v1(*args, **kwargs).invoke()``. + + :param max_fee: Max amount of Wei to be paid when executing transaction. + :param auto_estimate: Use automatic fee estimation, not recommend as it may lead to high costs. + :param nonce: Nonce of the transaction. + :return: InvokeResult. + """ + prepared_invoke = self.prepare_invoke_v1(*args, **kwargs) + return await prepared_invoke.invoke( + max_fee=max_fee, nonce=nonce, auto_estimate=auto_estimate + ) + + def prepare_invoke_v3( + self, + *args, + l1_resource_bounds: Optional[ResourceBounds] = None, + **kwargs, + ) -> PreparedFunctionInvokeV3: + """ + ``*args`` and ``**kwargs`` are translated into Cairo calldata. + Creates a ``PreparedFunctionInvokeV3`` instance which exposes calldata for every argument + and adds more arguments when calling methods. + + :param l1_resource_bounds: Max amount and max price per unit of L1 gas (in Fri) used when executing + this transaction. + :return: PreparedFunctionInvokeV3. + """ + + calldata = self._payload_transformer.serialize(*args, **kwargs) + return PreparedFunctionInvokeV3( + to_addr=self.contract_data.address, + calldata=calldata, + selector=self.get_selector(self.name), + l1_resource_bounds=l1_resource_bounds, + _contract_data=self.contract_data, + _client=self.client, + _account=self.account, + _payload_transformer=self._payload_transformer, + ) + + async def invoke_v3( + self, + *args, + l1_resource_bounds: Optional[ResourceBounds] = None, + auto_estimate: bool = False, + nonce: Optional[int] = None, + **kwargs, + ) -> InvokeResult: + """ + Invoke contract's function. ``*args`` and ``**kwargs`` are translated into Cairo calldata. + Equivalent of ``.prepare_invoke_v3(*args, **kwargs).invoke()``. + + :param l1_resource_bounds: Max amount and max price per unit of L1 gas (in Fri) used when executing + this transaction. + :param auto_estimate: Use automatic fee estimation, not recommend as it may lead to high costs. + :param nonce: Nonce of the transaction. + :return: InvokeResult. + """ + prepared_invoke = self.prepare_invoke_v3(*args, **kwargs) + return await prepared_invoke.invoke( + l1_resource_bounds=l1_resource_bounds, + nonce=nonce, + auto_estimate=auto_estimate, + ) + + @staticmethod + def get_selector(function_name: str): + """ + :param function_name: Contract function's name. + :return: A Starknet integer selector for this function inside the contract. + """ + return get_selector_from_name(function_name) + + FunctionsRepository = Dict[str, ContractFunction] diff --git a/starknet_py/net/contract/contract_test.py b/starknet_py/contract_test.py similarity index 96% rename from starknet_py/net/contract/contract_test.py rename to starknet_py/contract_test.py index f447ec47c..d137d14b7 100644 --- a/starknet_py/net/contract/contract_test.py +++ b/starknet_py/contract_test.py @@ -1,7 +1,7 @@ import pytest +from starknet_py.contract import Contract, DeclareResult, DeployResult from starknet_py.net.account.base_account import BaseAccount -from starknet_py.net.contract.contract import Contract, DeclareResult, DeployResult def test_compute_hash(balance_contract): diff --git a/starknet_py/net/contract/contract_utils.py b/starknet_py/contract_utils.py similarity index 100% rename from starknet_py/net/contract/contract_utils.py rename to starknet_py/contract_utils.py diff --git a/starknet_py/net/account/account_deployment_result.py b/starknet_py/net/account/account_deployment_result.py index b5205fc1b..44912ce95 100644 --- a/starknet_py/net/account/account_deployment_result.py +++ b/starknet_py/net/account/account_deployment_result.py @@ -1,6 +1,6 @@ from dataclasses import dataclass -from starknet_py.net.contract.sent_transaction import SentTransaction +from starknet_py.contract import SentTransaction from starknet_py.utils.sync import add_sync_methods diff --git a/starknet_py/net/contract/__init__.py b/starknet_py/net/contract/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/starknet_py/net/contract/contract_function.py b/starknet_py/net/contract/contract_function.py deleted file mode 100644 index e7448980a..000000000 --- a/starknet_py/net/contract/contract_function.py +++ /dev/null @@ -1,484 +0,0 @@ -from __future__ import annotations - -from abc import ABC, abstractmethod -from dataclasses import dataclass -from functools import cached_property -from typing import List, Optional, Union - -from starknet_py.abi import Abi as AbiV0 -from starknet_py.abi import AbiParser as AbiV0Parser -from starknet_py.abi.v1.model import Abi as AbiV1 -from starknet_py.abi.v1.parser import AbiParser as AbiV1Parser -from starknet_py.abi.v2.model import Abi as AbiV2 -from starknet_py.abi.v2.parser import AbiParser as AbiV2Parser -from starknet_py.abi.v2.shape import L1_HANDLER_ENTRY -from starknet_py.hash.selector import get_selector_from_name -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, ResourceBounds, Tag -from starknet_py.net.contract.sent_transaction import SentTransaction -from starknet_py.net.models.transaction import Invoke -from starknet_py.serialization import ( - FunctionSerializationAdapter, - TupleDataclass, - serializer_for_function, -) -from starknet_py.serialization.factory import serializer_for_function_v1 -from starknet_py.utils.constructor_args_translator import _is_abi_v2 -from starknet_py.utils.sync import add_sync_methods - -ABI = list -ABIEntry = dict - - -@dataclass(frozen=True) -class ContractData: - """ - Basic data of a deployed contract. - """ - - address: int - abi: ABI - cairo_version: int - - @cached_property - def parsed_abi(self) -> Union[AbiV0, AbiV1, AbiV2]: - """ - Abi parsed into proper dataclass. - - :return: Abi - """ - if self.cairo_version == 1: - if _is_abi_v2(self.abi): - return AbiV2Parser(self.abi).parse() - return AbiV1Parser(self.abi).parse() - return AbiV0Parser(self.abi).parse() - - @staticmethod - def from_abi(address: int, abi: ABI, cairo_version: int = 0) -> ContractData: - """ - Create ContractData from ABI. - - :param address: Address of the deployed contract. - :param abi: Abi of the contract. - :param cairo_version: Version of the Cairo in which contract is written. - :return: ContractData instance. - """ - return ContractData( - address=address, - abi=abi, - cairo_version=cairo_version, - ) - - -@add_sync_methods -@dataclass(frozen=True) -class InvokeResult(SentTransaction): - """ - Result of the Invoke transaction. - """ - - # We ensure these are not None in __post_init__ - contract: ContractData = None # pyright: ignore - """Additional information about the Contract that made the transaction.""" - - invoke_transaction: Invoke = None # pyright: ignore - """A InvokeTransaction instance used.""" - - def __post_init__(self): - assert self.contract is not None - assert self.invoke_transaction is not None - - -@dataclass -class PreparedCallBase(Call): - _client: Client - _payload_transformer: FunctionSerializationAdapter - - -@add_sync_methods -@dataclass -class PreparedFunctionCall(PreparedCallBase): - async def call_raw( - self, - block_hash: Optional[str] = None, - block_number: Optional[Union[int, Tag]] = None, - ) -> List[int]: - """ - Calls a method without translating the result into python values. - - :param block_hash: Optional block hash. - :param block_number: Optional block number. - :return: list of ints. - """ - return await self._client.call_contract( - call=self, block_hash=block_hash, block_number=block_number - ) - - async def call( - self, - block_hash: Optional[str] = None, - block_number: Optional[Union[int, Tag]] = None, - ) -> TupleDataclass: - """ - Calls a method. - - :param block_hash: Optional block hash. - :param block_number: Optional block number. - :return: TupleDataclass representing call result. - """ - result = await self.call_raw(block_hash=block_hash, block_number=block_number) - return self._payload_transformer.deserialize(result) - - -@add_sync_methods -@dataclass -class PreparedFunctionInvoke(ABC, PreparedCallBase): - _contract_data: ContractData - _account: Optional[BaseAccount] - - def __post_init__(self): - if self._account is None: - raise ValueError( - "Contract instance was created without providing an Account. " - "It is not possible to prepare and send an invoke transaction." - ) - - @property - def get_account(self): - if self._account is not None: - return self._account - - raise ValueError( - "The account is not defined. It is not possible to send an invoke transaction." - ) - - @abstractmethod - async def estimate_fee( - self, - block_hash: Optional[Union[Hash, Tag]] = None, - block_number: Optional[Union[int, Tag]] = None, - *, - nonce: Optional[int] = None, - ) -> EstimatedFee: - """ - Estimate fee for prepared function call. - - :param block_hash: Estimate fee at specific block hash. - :param block_number: Estimate fee at given block number - (or "latest" / "pending" for the latest / pending block), default is "pending". - :param nonce: Nonce of the transaction. - :return: Estimated amount of the transaction cost, either in Wei or Fri associated with executing the - specified transaction. - """ - - async def _invoke(self, transaction: Invoke) -> InvokeResult: - response = await self._client.send_transaction(transaction) - - invoke_result = InvokeResult( - hash=response.transaction_hash, # noinspection PyTypeChecker - _client=self._client, - contract=self._contract_data, - invoke_transaction=transaction, - ) - - return invoke_result - - -@add_sync_methods -@dataclass -class PreparedFunctionInvokeV1(PreparedFunctionInvoke): - max_fee: Optional[int] - - async def invoke( - self, - max_fee: Optional[int] = None, - auto_estimate: bool = False, - *, - nonce: Optional[int] = None, - ) -> InvokeResult: - """ - Send an Invoke transaction version 1 for a prepared data. - - :param max_fee: Max amount of Wei to be paid when executing transaction. - :param auto_estimate: Use automatic fee estimation, not recommend as it may lead to high costs. - :param nonce: Nonce of the transaction. - :return: InvokeResult. - """ - - transaction = await self.get_account.sign_invoke_v1_transaction( - calls=self, - nonce=nonce, - max_fee=max_fee or self.max_fee, - auto_estimate=auto_estimate, - ) - - return await self._invoke(transaction) - - async def estimate_fee( - self, - block_hash: Optional[Union[Hash, Tag]] = None, - block_number: Optional[Union[int, Tag]] = None, - *, - nonce: Optional[int] = None, - ) -> EstimatedFee: - tx = await self.get_account.sign_invoke_v1_transaction( - calls=self, nonce=nonce, max_fee=0 - ) - estimate_tx = await self.get_account.sign_for_fee_estimate(transaction=tx) - - estimated_fee = await self._client.estimate_fee( - tx=estimate_tx, - block_hash=block_hash, - block_number=block_number, - ) - - assert isinstance(estimated_fee, EstimatedFee) - return estimated_fee - - -@add_sync_methods -@dataclass -class PreparedFunctionInvokeV3(PreparedFunctionInvoke): - l1_resource_bounds: Optional[ResourceBounds] - - async def invoke( - self, - l1_resource_bounds: Optional[ResourceBounds] = None, - auto_estimate: bool = False, - *, - nonce: Optional[int] = None, - ) -> InvokeResult: - """ - Send an Invoke transaction version 3 for a prepared data. - - :param l1_resource_bounds: Max amount and max price per unit of L1 gas (in Fri) used when executing - this transaction. - :param auto_estimate: Use automatic fee estimation, not recommend as it may lead to high costs. - :param nonce: Nonce of the transaction. - :return: InvokeResult. - """ - - transaction = await self.get_account.sign_invoke_v3_transaction( - calls=self, - nonce=nonce, - l1_resource_bounds=l1_resource_bounds or self.l1_resource_bounds, - auto_estimate=auto_estimate, - ) - - return await self._invoke(transaction) - - async def estimate_fee( - self, - block_hash: Optional[Union[Hash, Tag]] = None, - block_number: Optional[Union[int, Tag]] = None, - *, - nonce: Optional[int] = None, - ) -> EstimatedFee: - tx = await self.get_account.sign_invoke_v3_transaction( - calls=self, nonce=nonce, l1_resource_bounds=ResourceBounds.init_with_zeros() - ) - estimate_tx = await self.get_account.sign_for_fee_estimate(transaction=tx) - - estimated_fee = await self._client.estimate_fee( - tx=estimate_tx, - block_hash=block_hash, - block_number=block_number, - ) - - assert isinstance(estimated_fee, EstimatedFee) - return estimated_fee - - -@add_sync_methods -class ContractFunction: - def __init__( - self, - name: str, - abi: ABIEntry, - contract_data: ContractData, - client: Client, - account: Optional[BaseAccount], - cairo_version: int = 0, - *, - interface_name: Optional[str] = None, - ): - # pylint: disable=too-many-arguments - self.name = name - self.abi = abi - self.inputs = abi["inputs"] - self.contract_data = contract_data - self.client = client - self.account = account - - if abi["type"] == L1_HANDLER_ENTRY: - assert not isinstance(contract_data.parsed_abi, AbiV1) - function = contract_data.parsed_abi.l1_handler - elif interface_name is None: - function = contract_data.parsed_abi.functions.get(name) - else: - assert isinstance(contract_data.parsed_abi, AbiV2) - interface = contract_data.parsed_abi.interfaces[interface_name] - function = interface.items[name] - - assert function is not None - - if cairo_version == 1: - assert not isinstance(function, AbiV0.Function) and function is not None - self._payload_transformer = serializer_for_function_v1(function) - - else: - assert isinstance(function, AbiV0.Function) and function is not None - self._payload_transformer = serializer_for_function(function) - - def prepare_call( - self, - *args, - **kwargs, - ) -> PreparedFunctionCall: - """ - ``*args`` and ``**kwargs`` are translated into Cairo calldata. - Creates a ``PreparedFunctionCall`` instance which exposes calldata for every argument - and adds more arguments when calling methods. - - :return: PreparedFunctionCall. - """ - - calldata = self._payload_transformer.serialize(*args, **kwargs) - return PreparedFunctionCall( - to_addr=self.contract_data.address, - calldata=calldata, - selector=self.get_selector(self.name), - _client=self.client, - _payload_transformer=self._payload_transformer, - ) - - async def call( - self, - *args, - block_hash: Optional[str] = None, - block_number: Optional[Union[int, Tag]] = None, - **kwargs, - ) -> TupleDataclass: - """ - Call contract's function. ``*args`` and ``**kwargs`` are translated into Cairo calldata. - The result is translated from Cairo data to python values. - Equivalent of ``.prepare_call(*args, **kwargs).call()``. - - :param block_hash: Block hash to perform the call to the contract at specific point of time. - :param block_number: Block number to perform the call to the contract at specific point of time. - :return: TupleDataclass representing call result. - """ - return await self.prepare_call(*args, **kwargs).call( - block_hash=block_hash, block_number=block_number - ) - - def prepare_invoke_v1( - self, - *args, - max_fee: Optional[int] = None, - **kwargs, - ) -> PreparedFunctionInvokeV1: - """ - ``*args`` and ``**kwargs`` are translated into Cairo calldata. - Creates a ``PreparedFunctionInvokeV1`` instance which exposes calldata for every argument - and adds more arguments when calling methods. - - :param max_fee: Max amount of Wei to be paid when executing transaction. - :return: PreparedFunctionCall. - """ - - calldata = self._payload_transformer.serialize(*args, **kwargs) - return PreparedFunctionInvokeV1( - to_addr=self.contract_data.address, - calldata=calldata, - selector=self.get_selector(self.name), - max_fee=max_fee, - _contract_data=self.contract_data, - _client=self.client, - _account=self.account, - _payload_transformer=self._payload_transformer, - ) - - async def invoke_v1( - self, - *args, - max_fee: Optional[int] = None, - auto_estimate: bool = False, - nonce: Optional[int] = None, - **kwargs, - ) -> InvokeResult: - """ - Invoke contract's function. ``*args`` and ``**kwargs`` are translated into Cairo calldata. - Equivalent of ``.prepare_invoke_v1(*args, **kwargs).invoke()``. - - :param max_fee: Max amount of Wei to be paid when executing transaction. - :param auto_estimate: Use automatic fee estimation, not recommend as it may lead to high costs. - :param nonce: Nonce of the transaction. - :return: InvokeResult. - """ - prepared_invoke = self.prepare_invoke_v1(*args, **kwargs) - return await prepared_invoke.invoke( - max_fee=max_fee, nonce=nonce, auto_estimate=auto_estimate - ) - - def prepare_invoke_v3( - self, - *args, - l1_resource_bounds: Optional[ResourceBounds] = None, - **kwargs, - ) -> PreparedFunctionInvokeV3: - """ - ``*args`` and ``**kwargs`` are translated into Cairo calldata. - Creates a ``PreparedFunctionInvokeV3`` instance which exposes calldata for every argument - and adds more arguments when calling methods. - - :param l1_resource_bounds: Max amount and max price per unit of L1 gas (in Fri) used when executing - this transaction. - :return: PreparedFunctionInvokeV3. - """ - - calldata = self._payload_transformer.serialize(*args, **kwargs) - return PreparedFunctionInvokeV3( - to_addr=self.contract_data.address, - calldata=calldata, - selector=self.get_selector(self.name), - l1_resource_bounds=l1_resource_bounds, - _contract_data=self.contract_data, - _client=self.client, - _account=self.account, - _payload_transformer=self._payload_transformer, - ) - - async def invoke_v3( - self, - *args, - l1_resource_bounds: Optional[ResourceBounds] = None, - auto_estimate: bool = False, - nonce: Optional[int] = None, - **kwargs, - ) -> InvokeResult: - """ - Invoke contract's function. ``*args`` and ``**kwargs`` are translated into Cairo calldata. - Equivalent of ``.prepare_invoke_v3(*args, **kwargs).invoke()``. - - :param l1_resource_bounds: Max amount and max price per unit of L1 gas (in Fri) used when executing - this transaction. - :param auto_estimate: Use automatic fee estimation, not recommend as it may lead to high costs. - :param nonce: Nonce of the transaction. - :return: InvokeResult. - """ - prepared_invoke = self.prepare_invoke_v3(*args, **kwargs) - return await prepared_invoke.invoke( - l1_resource_bounds=l1_resource_bounds, - nonce=nonce, - auto_estimate=auto_estimate, - ) - - @staticmethod - def get_selector(function_name: str): - """ - :param function_name: Contract function's name. - :return: A Starknet integer selector for this function inside the contract. - """ - return get_selector_from_name(function_name) diff --git a/starknet_py/net/contract/sent_transaction.py b/starknet_py/net/contract/sent_transaction.py deleted file mode 100644 index aec864ebd..000000000 --- a/starknet_py/net/contract/sent_transaction.py +++ /dev/null @@ -1,57 +0,0 @@ -from __future__ import annotations - -import dataclasses -import warnings -from dataclasses import dataclass -from typing import Optional, TypeVar - -from starknet_py.net.client import Client -from starknet_py.utils.sync import add_sync_methods - - -@add_sync_methods -@dataclass(frozen=True) -class SentTransaction: - """ - Dataclass exposing the interface of transaction related to a performed action. - """ - - hash: int - """Hash of the transaction.""" - - _client: Client - status: Optional[str] = None - """Status of the transaction.""" - - block_number: Optional[int] = None - """Number of the block in which transaction was included.""" - - async def wait_for_acceptance( - self: TypeSentTransaction, - wait_for_accept: Optional[bool] = None, - check_interval: float = 2, - retries: int = 500, - ) -> TypeSentTransaction: - """ - Waits for transaction to be accepted on chain till ``ACCEPTED`` status. - Returns a new SentTransaction instance, **does not mutate original instance**. - """ - if wait_for_accept is not None: - warnings.warn( - "Parameter `wait_for_accept` has been deprecated - since Starknet 0.12.0, transactions in a PENDING" - " block have status ACCEPTED_ON_L2." - ) - - tx_receipt = await self._client.wait_for_tx( - self.hash, - check_interval=check_interval, - retries=retries, - ) - return dataclasses.replace( - self, - status=tx_receipt.finality_status, - block_number=tx_receipt.block_number, - ) - - -TypeSentTransaction = TypeVar("TypeSentTransaction", bound="SentTransaction") diff --git a/starknet_py/tests/e2e/account/account_test.py b/starknet_py/tests/e2e/account/account_test.py index 1bcb3617e..01119172d 100644 --- a/starknet_py/tests/e2e/account/account_test.py +++ b/starknet_py/tests/e2e/account/account_test.py @@ -3,6 +3,7 @@ import pytest +from starknet_py.contract import Contract from starknet_py.hash.address import compute_address from starknet_py.hash.selector import get_selector_from_name from starknet_py.net.account.account import Account @@ -20,7 +21,6 @@ TransactionExecutionStatus, TransactionFinalityStatus, ) -from starknet_py.net.contract.contract import Contract from starknet_py.net.full_node_client import FullNodeClient from starknet_py.net.models import StarknetChainId from starknet_py.net.models.transaction import ( diff --git a/starknet_py/tests/e2e/block_test.py b/starknet_py/tests/e2e/block_test.py index 7f6d0373e..a6e24fbb1 100644 --- a/starknet_py/tests/e2e/block_test.py +++ b/starknet_py/tests/e2e/block_test.py @@ -1,5 +1,6 @@ import pytest +from starknet_py.contract import Contract from starknet_py.net.account.base_account import BaseAccount from starknet_py.net.client_models import ( PendingStarknetBlock, @@ -7,7 +8,6 @@ StarknetBlock, StarknetBlockWithTxHashes, ) -from starknet_py.net.contract.contract import Contract from starknet_py.tests.e2e.fixtures.constants import MAX_FEE diff --git a/starknet_py/tests/e2e/cairo1v2_test.py b/starknet_py/tests/e2e/cairo1v2_test.py index d1645abd8..5cf98bb5f 100644 --- a/starknet_py/tests/e2e/cairo1v2_test.py +++ b/starknet_py/tests/e2e/cairo1v2_test.py @@ -4,8 +4,8 @@ import pytest_asyncio from starknet_py.cairo.felt import decode_shortstring +from starknet_py.contract import Contract, DeclareResult, DeployResult from starknet_py.hash.storage import get_storage_var_address -from starknet_py.net.contract.contract import Contract, DeclareResult, DeployResult from starknet_py.tests.e2e.fixtures.constants import CONTRACTS_COMPILED_V2_DIR from starknet_py.tests.e2e.fixtures.misc import read_contract diff --git a/starknet_py/tests/e2e/client/fixtures/prepare_net_for_gateway_test.py b/starknet_py/tests/e2e/client/fixtures/prepare_net_for_gateway_test.py index fb96a8c9e..29113df46 100755 --- a/starknet_py/tests/e2e/client/fixtures/prepare_net_for_gateway_test.py +++ b/starknet_py/tests/e2e/client/fixtures/prepare_net_for_gateway_test.py @@ -1,7 +1,7 @@ from dataclasses import dataclass +from starknet_py.contract import Contract from starknet_py.net.account.account import Account -from starknet_py.net.contract.contract import Contract from starknet_py.tests.e2e.fixtures.constants import MAX_FEE from starknet_py.tests.e2e.utils import ( AccountToBeDeployedDetails, diff --git a/starknet_py/tests/e2e/client/fixtures/transactions.py b/starknet_py/tests/e2e/client/fixtures/transactions.py index c4070f20a..49ca10a17 100644 --- a/starknet_py/tests/e2e/client/fixtures/transactions.py +++ b/starknet_py/tests/e2e/client/fixtures/transactions.py @@ -4,8 +4,8 @@ import pytest import pytest_asyncio +from starknet_py.contract import Contract from starknet_py.net.account.account import Account -from starknet_py.net.contract.contract import Contract from starknet_py.net.full_node_client import FullNodeClient from starknet_py.net.models.transaction import DeployAccountV1 from starknet_py.net.udc_deployer.deployer import Deployer diff --git a/starknet_py/tests/e2e/client/full_node_test.py b/starknet_py/tests/e2e/client/full_node_test.py index 5b633fa71..1d3e943da 100644 --- a/starknet_py/tests/e2e/client/full_node_test.py +++ b/starknet_py/tests/e2e/client/full_node_test.py @@ -4,6 +4,7 @@ import pytest from starknet_py.common import create_casm_class +from starknet_py.contract import Contract from starknet_py.hash.address import compute_address from starknet_py.hash.casm_class_hash import compute_casm_class_hash from starknet_py.hash.selector import get_selector_from_name @@ -23,7 +24,6 @@ SyncStatus, TransactionType, ) -from starknet_py.net.contract.contract import Contract from starknet_py.net.full_node_client import _to_rpc_felt from starknet_py.net.models import StarknetChainId from starknet_py.tests.e2e.fixtures.constants import ( diff --git a/starknet_py/tests/e2e/contract_interaction/declare_test.py b/starknet_py/tests/e2e/contract_interaction/declare_test.py index 3ad43dbde..d8febbc8b 100644 --- a/starknet_py/tests/e2e/contract_interaction/declare_test.py +++ b/starknet_py/tests/e2e/contract_interaction/declare_test.py @@ -1,6 +1,6 @@ import pytest -from starknet_py.net.contract.contract import Contract +from starknet_py.contract import Contract from starknet_py.net.models import DeclareV2, DeclareV3 from starknet_py.tests.e2e.fixtures.constants import ( CONTRACTS_COMPILED_V1_DIR, diff --git a/starknet_py/tests/e2e/contract_interaction/deploy_test.py b/starknet_py/tests/e2e/contract_interaction/deploy_test.py index 4021700c5..ca3812d98 100644 --- a/starknet_py/tests/e2e/contract_interaction/deploy_test.py +++ b/starknet_py/tests/e2e/contract_interaction/deploy_test.py @@ -6,8 +6,8 @@ import pytest from starknet_py.common import create_sierra_compiled_contract +from starknet_py.contract import Contract, DeclareResult from starknet_py.net.client_models import InvokeTransaction, InvokeTransactionV3 -from starknet_py.net.contract.contract import Contract, DeclareResult from starknet_py.net.models import DeclareV2 from starknet_py.tests.e2e.fixtures.constants import MAX_FEE, MAX_RESOURCE_BOUNDS_L1 from starknet_py.tests.e2e.fixtures.misc import read_contract diff --git a/starknet_py/tests/e2e/contract_interaction/interaction_test.py b/starknet_py/tests/e2e/contract_interaction/interaction_test.py index 4f910a641..1ffdd29c6 100644 --- a/starknet_py/tests/e2e/contract_interaction/interaction_test.py +++ b/starknet_py/tests/e2e/contract_interaction/interaction_test.py @@ -1,13 +1,13 @@ import pytest -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, ResourceBounds -from starknet_py.net.contract.contract import Contract -from starknet_py.net.contract.contract_function import ( +from starknet_py.contract import ( + Contract, PreparedFunctionInvokeV1, PreparedFunctionInvokeV3, ) +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, ResourceBounds from starknet_py.net.models import InvokeV1, InvokeV3 from starknet_py.tests.e2e.fixtures.constants import ( MAX_FEE, 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 c2b456650..67ecc92b1 100644 --- a/starknet_py/tests/e2e/contract_interaction/v1_interaction_test.py +++ b/starknet_py/tests/e2e/contract_interaction/v1_interaction_test.py @@ -3,7 +3,7 @@ import pytest from starknet_py.cairo.felt import decode_shortstring, encode_shortstring -from starknet_py.net.contract.contract import Contract +from starknet_py.contract import Contract from starknet_py.tests.e2e.fixtures.constants import MAX_FEE from starknet_py.tests.e2e.fixtures.contracts import deploy_v1_contract diff --git a/starknet_py/tests/e2e/deploy/deployer_test.py b/starknet_py/tests/e2e/deploy/deployer_test.py index 510cea5a9..51db149d4 100644 --- a/starknet_py/tests/e2e/deploy/deployer_test.py +++ b/starknet_py/tests/e2e/deploy/deployer_test.py @@ -1,7 +1,7 @@ import pytest +from starknet_py.contract import Contract from starknet_py.hash.address import compute_address -from starknet_py.net.contract.contract import Contract 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 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 af6f65294..ec3474357 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 @@ -1,7 +1,7 @@ import pytest +from starknet_py.contract import Contract from starknet_py.net.client import Client -from starknet_py.net.contract.contract import Contract from starknet_py.net.models import chain_from_network from starknet_py.tests.e2e.fixtures.constants import MAX_FEE from starknet_py.tests.e2e.utils import _get_random_private_key_unsafe 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 dc1880781..f797acc05 100644 --- a/starknet_py/tests/e2e/docs/code_examples/test_contract.py +++ b/starknet_py/tests/e2e/docs/code_examples/test_contract.py @@ -1,8 +1,8 @@ # pylint: disable=unused-variable import pytest +from starknet_py.contract import Contract from starknet_py.net.account.account import Account -from starknet_py.net.contract.contract import Contract 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 diff --git a/starknet_py/tests/e2e/docs/code_examples/test_contract_function.py b/starknet_py/tests/e2e/docs/code_examples/test_contract_function.py index ef5af44e8..84d49e81f 100644 --- a/starknet_py/tests/e2e/docs/code_examples/test_contract_function.py +++ b/starknet_py/tests/e2e/docs/code_examples/test_contract_function.py @@ -1,8 +1,7 @@ # pylint: disable=unused-variable import pytest -from starknet_py.net.contract.contract import Contract -from starknet_py.net.contract.contract_function import ContractFunction +from starknet_py.contract import Contract, ContractFunction def test_prepare(map_contract: Contract): 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 e33956bf6..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 @@ -1,10 +1,10 @@ # pylint: disable=unused-variable import pytest +from starknet_py.contract import Contract 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.contract.contract import Contract from starknet_py.net.full_node_client import FullNodeClient diff --git a/starknet_py/tests/e2e/docs/code_examples/test_prepared_function_call.py b/starknet_py/tests/e2e/docs/code_examples/test_prepared_function_call.py index 17a386393..c91f7a722 100644 --- a/starknet_py/tests/e2e/docs/code_examples/test_prepared_function_call.py +++ b/starknet_py/tests/e2e/docs/code_examples/test_prepared_function_call.py @@ -1,7 +1,7 @@ # pylint: disable=unused-variable import pytest -from starknet_py.net.contract.contract import Contract +from starknet_py.contract import Contract @pytest.mark.asyncio 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 0b8c9b2d6..533f0392a 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 @@ -7,7 +7,7 @@ async def test_deploying_in_multicall(account, map_class_hash, map_compiled_contract): # pylint: disable=import-outside-toplevel, # docs: start - from starknet_py.net.contract.contract import Contract + from starknet_py.contract import Contract from starknet_py.net.udc_deployer.deployer import Deployer # First, create Deployer instance. For more details see previous paragraph diff --git a/starknet_py/tests/e2e/docs/guide/test_handling_client_errors.py b/starknet_py/tests/e2e/docs/guide/test_handling_client_errors.py index db7704678..ba66feeaf 100644 --- a/starknet_py/tests/e2e/docs/guide/test_handling_client_errors.py +++ b/starknet_py/tests/e2e/docs/guide/test_handling_client_errors.py @@ -5,8 +5,8 @@ async def test_handling_client_errors(account): # pylint: disable=import-outside-toplevel # docs: start + from starknet_py.contract import Contract from starknet_py.net.client_errors import ClientError - from starknet_py.net.contract.contract import Contract try: contract_address = "1" # Doesn't exist diff --git a/starknet_py/tests/e2e/docs/guide/test_resolving_proxies.py b/starknet_py/tests/e2e/docs/guide/test_resolving_proxies.py index e943e8283..30cb78601 100644 --- a/starknet_py/tests/e2e/docs/guide/test_resolving_proxies.py +++ b/starknet_py/tests/e2e/docs/guide/test_resolving_proxies.py @@ -23,7 +23,7 @@ async def test_resolving_proxies( ): # pylint: disable=import-outside-toplevel # docs-1: start - from starknet_py.net.contract.contract import Contract + from starknet_py.contract import Contract # docs-1: end address = map_contract.address diff --git a/starknet_py/tests/e2e/docs/guide/test_simple_declare_and_deploy.py b/starknet_py/tests/e2e/docs/guide/test_simple_declare_and_deploy.py index c55fe8fc0..23f5ef850 100644 --- a/starknet_py/tests/e2e/docs/guide/test_simple_declare_and_deploy.py +++ b/starknet_py/tests/e2e/docs/guide/test_simple_declare_and_deploy.py @@ -5,7 +5,7 @@ async def test_simple_declare_and_deploy(account, map_compiled_contract): # pylint: disable=import-outside-toplevel # docs: start - from starknet_py.net.contract.contract import Contract + from starknet_py.contract import Contract # docs: end diff --git a/starknet_py/tests/e2e/docs/guide/test_simple_declare_and_deploy_cairo1.py b/starknet_py/tests/e2e/docs/guide/test_simple_declare_and_deploy_cairo1.py index 0d31358ff..f8f72e72c 100644 --- a/starknet_py/tests/e2e/docs/guide/test_simple_declare_and_deploy_cairo1.py +++ b/starknet_py/tests/e2e/docs/guide/test_simple_declare_and_deploy_cairo1.py @@ -7,7 +7,7 @@ async def test_simple_declare_and_deploy(account): # pylint: disable=import-outside-toplevel # docs: start - from starknet_py.net.contract.contract import Contract + from starknet_py.contract import Contract # docs: end compiled_contract = read_contract("account_compiled.json") diff --git a/starknet_py/tests/e2e/docs/guide/test_simple_deploy.py b/starknet_py/tests/e2e/docs/guide/test_simple_deploy.py index 2bc40a66c..2abb18ebd 100644 --- a/starknet_py/tests/e2e/docs/guide/test_simple_deploy.py +++ b/starknet_py/tests/e2e/docs/guide/test_simple_deploy.py @@ -7,7 +7,7 @@ async def test_simple_deploy(account, map_class_hash, map_compiled_contract): # pylint: disable=import-outside-toplevel # docs: start - from starknet_py.net.contract.contract import Contract + from starknet_py.contract import Contract # docs: end 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 cb150d5e2..7a1de341f 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 @@ -18,7 +18,7 @@ async def test_simple_deploy_cairo1(account, cairo1_erc20_class_hash): # docs: start from starknet_py.cairo.felt import encode_shortstring from starknet_py.common import create_sierra_compiled_contract - from starknet_py.net.contract.contract import Contract + from starknet_py.contract import Contract # docs: end diff --git a/starknet_py/tests/e2e/docs/guide/test_using_existing_contracts.py b/starknet_py/tests/e2e/docs/guide/test_using_existing_contracts.py index a3219e95e..6180360e6 100644 --- a/starknet_py/tests/e2e/docs/guide/test_using_existing_contracts.py +++ b/starknet_py/tests/e2e/docs/guide/test_using_existing_contracts.py @@ -27,7 +27,7 @@ async def test_using_existing_contracts(account, erc20_contract): # pylint: disable=import-outside-toplevel,too-many-locals,unused-variable # docs: start - from starknet_py.net.contract.contract import Contract + from starknet_py.contract import Contract address = "0x00178130dd6286a9a0e031e4c73b2bd04ffa92804264a25c1c08c1612559f458" diff --git a/starknet_py/tests/e2e/docs/quickstart/test_synchronous_api.py b/starknet_py/tests/e2e/docs/quickstart/test_synchronous_api.py index 7ffcc6699..ab3a9a322 100644 --- a/starknet_py/tests/e2e/docs/quickstart/test_synchronous_api.py +++ b/starknet_py/tests/e2e/docs/quickstart/test_synchronous_api.py @@ -3,7 +3,7 @@ def test_synchronous_api(account, map_contract): # docs: start - from starknet_py.net.contract.contract import Contract + from starknet_py.contract import Contract contract_address = ( "0x01336fa7c870a7403aced14dda865b75f29113230ed84e3a661f7af70fe83e7b" diff --git a/starknet_py/tests/e2e/docs/quickstart/test_using_account.py b/starknet_py/tests/e2e/docs/quickstart/test_using_account.py index 686c8a11a..a6d82006b 100644 --- a/starknet_py/tests/e2e/docs/quickstart/test_using_account.py +++ b/starknet_py/tests/e2e/docs/quickstart/test_using_account.py @@ -11,7 +11,7 @@ async def test_using_account(account, map_compiled_contract): # pylint: disable=import-outside-toplevel, duplicate-code, too-many-locals # docs: start - from starknet_py.net.contract.contract import Contract + from starknet_py.contract import Contract # docs: end # docs: start diff --git a/starknet_py/tests/e2e/docs/quickstart/test_using_contract.py b/starknet_py/tests/e2e/docs/quickstart/test_using_contract.py index 999799839..1203e1298 100644 --- a/starknet_py/tests/e2e/docs/quickstart/test_using_contract.py +++ b/starknet_py/tests/e2e/docs/quickstart/test_using_contract.py @@ -10,7 +10,7 @@ async def test_using_contract(account, map_contract): # pylint: disable=unused-variable,too-many-locals # docs: start - from starknet_py.net.contract.contract import Contract + from starknet_py.contract import Contract contract_address = ( "0x01336fa7c870a7403aced14dda865b75f29113230ed84e3a661f7af70fe83e7b" diff --git a/starknet_py/tests/e2e/fixtures/accounts.py b/starknet_py/tests/e2e/fixtures/accounts.py index 9fe2f4cb5..d8f03782b 100644 --- a/starknet_py/tests/e2e/fixtures/accounts.py +++ b/starknet_py/tests/e2e/fixtures/accounts.py @@ -6,11 +6,11 @@ import pytest import pytest_asyncio +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.base_account import BaseAccount from starknet_py.net.client_models import PriceUnit -from starknet_py.net.contract.contract import Contract 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 diff --git a/starknet_py/tests/e2e/fixtures/contracts.py b/starknet_py/tests/e2e/fixtures/contracts.py index f11a2fe68..a74a58fb2 100644 --- a/starknet_py/tests/e2e/fixtures/contracts.py +++ b/starknet_py/tests/e2e/fixtures/contracts.py @@ -11,9 +11,9 @@ create_sierra_compiled_contract, ) from starknet_py.constants import FEE_CONTRACT_ADDRESS +from starknet_py.contract import Contract from starknet_py.hash.casm_class_hash import compute_casm_class_hash from starknet_py.net.account.base_account import BaseAccount -from starknet_py.net.contract.contract import Contract from starknet_py.net.udc_deployer.deployer import Deployer from starknet_py.tests.e2e.fixtures.constants import ( CONTRACTS_COMPILED_V0_DIR, diff --git a/starknet_py/tests/e2e/fixtures/proxy.py b/starknet_py/tests/e2e/fixtures/proxy.py index eb41f36a0..98232bbaf 100644 --- a/starknet_py/tests/e2e/fixtures/proxy.py +++ b/starknet_py/tests/e2e/fixtures/proxy.py @@ -3,9 +3,9 @@ import pytest import pytest_asyncio +from starknet_py.contract import Contract, DeployResult from starknet_py.hash.selector import get_selector_from_name from starknet_py.net.account.account import Account -from starknet_py.net.contract.contract import Contract, DeployResult from starknet_py.tests.e2e.fixtures.constants import ( CONTRACTS_COMPILED_V0_DIR, CONTRACTS_PRECOMPILED_DIR, diff --git a/starknet_py/tests/e2e/proxy/proxy_test.py b/starknet_py/tests/e2e/proxy/proxy_test.py index ee63e36af..770a41444 100644 --- a/starknet_py/tests/e2e/proxy/proxy_test.py +++ b/starknet_py/tests/e2e/proxy/proxy_test.py @@ -2,10 +2,10 @@ import pytest +from starknet_py.contract import Contract from starknet_py.hash.storage import get_storage_var_address from starknet_py.net.client import Client from starknet_py.net.client_errors import ContractNotFoundError -from starknet_py.net.contract.contract import Contract from starknet_py.net.models import Address from starknet_py.proxy.contract_abi_resolver import ProxyResolutionError from starknet_py.proxy.proxy_check import ProxyCheck 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 933067be1..90053ecff 100644 --- a/starknet_py/tests/e2e/tests_on_networks/account_test.py +++ b/starknet_py/tests/e2e/tests_on_networks/account_test.py @@ -1,7 +1,7 @@ import pytest +from starknet_py.contract import Contract from starknet_py.net.client_errors import ClientError -from starknet_py.net.contract.contract import Contract from starknet_py.tests.e2e.fixtures.constants import ( MAP_CONTRACT_ADDRESS_GOERLI_INTEGRATION, MAX_FEE, diff --git a/starknet_py/tests/e2e/utils.py b/starknet_py/tests/e2e/utils.py index 12a4d6781..cdda24975 100644 --- a/starknet_py/tests/e2e/utils.py +++ b/starknet_py/tests/e2e/utils.py @@ -2,10 +2,10 @@ 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.contract.contract import Contract from starknet_py.net.http_client import HttpClient, HttpMethod from starknet_py.net.models import StarknetChainId from starknet_py.net.models.transaction import DeployAccountV1 From f6b99a14d5c66573a54c5a9cb9f0f74745fc6da1 Mon Sep 17 00:00:00 2001 From: Dariusz Doktorski Date: Tue, 30 Jan 2024 20:36:26 +0100 Subject: [PATCH 23/24] Fix docs build and add code examples --- docs/api/contract.rst | 18 ++++++++++ starknet_py/contract.py | 12 +++++++ .../code_examples/test_contract_function.py | 33 +++++++++++++++---- .../test_prepared_function_call.py | 20 ----------- .../test_prepared_function_invoke_v1.py | 24 ++++++++++++++ .../test_prepared_function_invoke_v3.py | 28 ++++++++++++++++ 6 files changed, 109 insertions(+), 26 deletions(-) create mode 100644 starknet_py/tests/e2e/docs/code_examples/test_prepared_function_invoke_v1.py create mode 100644 starknet_py/tests/e2e/docs/code_examples/test_prepared_function_invoke_v3.py diff --git a/docs/api/contract.rst b/docs/api/contract.rst index 4e3000068..e33a55329 100644 --- a/docs/api/contract.rst +++ b/docs/api/contract.rst @@ -34,6 +34,24 @@ PreparedFunctionCall :members: :member-order: groupwise +------------------------ +PreparedFunctionInvokeV1 +------------------------ + +.. autoclass-with-examples:: PreparedFunctionInvokeV1 + :exclude-members: __init__, __new__ + :members: + :member-order: groupwise + +------------------------ +PreparedFunctionInvokeV3 +------------------------ + +.. autoclass-with-examples:: PreparedFunctionInvokeV3 + :exclude-members: __init__, __new__ + :members: + :member-order: groupwise + ------------ InvokeResult ------------ diff --git a/starknet_py/contract.py b/starknet_py/contract.py index 0131ff489..942236796 100644 --- a/starknet_py/contract.py +++ b/starknet_py/contract.py @@ -321,6 +321,10 @@ class PreparedCallBase(Call): @add_sync_methods @dataclass class PreparedFunctionCall(PreparedCallBase): + """ + Prepared date to call a contract function. + """ + async def call_raw( self, block_hash: Optional[str] = None, @@ -410,6 +414,10 @@ async def _invoke(self, transaction: Invoke) -> InvokeResult: @add_sync_methods @dataclass class PreparedFunctionInvokeV1(PreparedFunctionInvoke): + """ + Prepared date to send an InvokeV1 transaction. + """ + max_fee: Optional[int] async def invoke( @@ -462,6 +470,10 @@ async def estimate_fee( @add_sync_methods @dataclass class PreparedFunctionInvokeV3(PreparedFunctionInvoke): + """ + Prepared date to send an InvokeV3 transaction. + """ + l1_resource_bounds: Optional[ResourceBounds] async def invoke( diff --git a/starknet_py/tests/e2e/docs/code_examples/test_contract_function.py b/starknet_py/tests/e2e/docs/code_examples/test_contract_function.py index 84d49e81f..b0b2dbf11 100644 --- a/starknet_py/tests/e2e/docs/code_examples/test_contract_function.py +++ b/starknet_py/tests/e2e/docs/code_examples/test_contract_function.py @@ -2,14 +2,23 @@ import pytest from starknet_py.contract import Contract, ContractFunction +from starknet_py.net.client_models import ResourceBounds -def test_prepare(map_contract: Contract): - # docs-start: prepare +def test_prepare_invoke_v1(map_contract: Contract): + # docs-start: prepare_invoke_v1 prepared_function_call = map_contract.functions["put"].prepare_invoke_v1( key=10, value=20 ) - # docs-end: prepare + # docs-end: prepare_invoke_v1 + + +def test_prepare_invoke_v3(map_contract: Contract): + # docs-start: prepare_invoke_v3 + prepared_function_call = map_contract.functions["put"].prepare_invoke_v3( + key=10, value=20 + ) + # docs-end: prepare_invoke_v3 @pytest.mark.asyncio @@ -21,12 +30,24 @@ async def test_call(map_contract: Contract): # docs-end: call -def test_invoke(map_contract: Contract): - # docs-start: invoke +def test_invoke_v1(map_contract: Contract): + # docs-start: invoke_v1 invoke_result = map_contract.functions["put"].invoke_v1( key=10, value=20, max_fee=int(1e15) ) - # docs-end: invoke + # docs-end: invoke_v1 + + +def test_invoke_v3(map_contract: Contract): + # docs-start: invoke_v3 + invoke_result = map_contract.functions["put"].invoke_v3( + key=10, + value=20, + l1_resource_bounds=ResourceBounds( + max_amount=5000, max_price_per_unit=int(1e12) + ), + ) + # docs-end: invoke_v3 def test_get_selector(): diff --git a/starknet_py/tests/e2e/docs/code_examples/test_prepared_function_call.py b/starknet_py/tests/e2e/docs/code_examples/test_prepared_function_call.py index c91f7a722..5d6675e34 100644 --- a/starknet_py/tests/e2e/docs/code_examples/test_prepared_function_call.py +++ b/starknet_py/tests/e2e/docs/code_examples/test_prepared_function_call.py @@ -22,23 +22,3 @@ async def test_call(map_contract: Contract): # or response = await prepared_function_call.call() # docs-end: call - - -@pytest.mark.asyncio -async def test_invoke(map_contract: Contract): - prepared_function_call = map_contract.functions["put"].prepare_invoke_v1( - key=10, value=20 - ) - # docs-start: invoke - invoke_result = await prepared_function_call.invoke(max_fee=int(1e15)) - # docs-end: invoke - prepared_function_call.max_fee = None - # docs-start: invoke - # max_fee can be estimated automatically - invoke_result = await prepared_function_call.invoke(auto_estimate=True) - # or if max_fee was specified in prepared_function_call - # docs-end: invoke - prepared_function_call.max_fee = int(1e15) - # docs-start: invoke - invoke_result = await prepared_function_call.invoke() - # docs-end: invoke diff --git a/starknet_py/tests/e2e/docs/code_examples/test_prepared_function_invoke_v1.py b/starknet_py/tests/e2e/docs/code_examples/test_prepared_function_invoke_v1.py new file mode 100644 index 000000000..15bfed28d --- /dev/null +++ b/starknet_py/tests/e2e/docs/code_examples/test_prepared_function_invoke_v1.py @@ -0,0 +1,24 @@ +# pylint: disable=unused-variable +import pytest + +from starknet_py.contract import Contract + + +@pytest.mark.asyncio +async def test_invoke(map_contract: Contract): + prepared_function_call = map_contract.functions["put"].prepare_invoke_v1( + key=10, value=20 + ) + # docs-start: invoke + invoke_result = await prepared_function_call.invoke(max_fee=int(1e15)) + # docs-end: invoke + prepared_function_call.max_fee = None + # docs-start: invoke + # max_fee can be estimated automatically + invoke_result = await prepared_function_call.invoke(auto_estimate=True) + # or if max_fee was specified in prepared_function_call + # docs-end: invoke + prepared_function_call.max_fee = int(1e15) + # docs-start: invoke + invoke_result = await prepared_function_call.invoke() + # docs-end: invoke diff --git a/starknet_py/tests/e2e/docs/code_examples/test_prepared_function_invoke_v3.py b/starknet_py/tests/e2e/docs/code_examples/test_prepared_function_invoke_v3.py new file mode 100644 index 000000000..17cac8b7e --- /dev/null +++ b/starknet_py/tests/e2e/docs/code_examples/test_prepared_function_invoke_v3.py @@ -0,0 +1,28 @@ +# pylint: disable=unused-variable +import pytest + +from starknet_py.contract import Contract +from starknet_py.net.client_models import ResourceBounds + + +@pytest.mark.asyncio +async def test_invoke(map_contract: Contract): + prepared_function_call = map_contract.functions["put"].prepare_invoke_v3( + key=10, value=20 + ) + l1_resource_bounds = ResourceBounds(max_amount=5000, max_price_per_unit=int(1e12)) + # docs-start: invoke + invoke_result = await prepared_function_call.invoke( + l1_resource_bounds=ResourceBounds(max_amount=5000, max_price_per_unit=int(1e12)) + ) + # docs-end: invoke + prepared_function_call.l1_resource_bounds = None + # docs-start: invoke + # l1_resource_bounds can be estimated automatically + invoke_result = await prepared_function_call.invoke(auto_estimate=True) + # or if l1_resource_bounds was specified in prepared_function_call + # docs-end: invoke + prepared_function_call.l1_resource_bounds = l1_resource_bounds + # docs-start: invoke + invoke_result = await prepared_function_call.invoke() + # docs-end: invoke From 3f7716aea25cf23105cc563038abcbaa8ea775d2 Mon Sep 17 00:00:00 2001 From: Dariusz Doktorski Date: Wed, 31 Jan 2024 16:21:30 +0100 Subject: [PATCH 24/24] Update docstrings --- starknet_py/contract.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/starknet_py/contract.py b/starknet_py/contract.py index 942236796..d7f7d0c86 100644 --- a/starknet_py/contract.py +++ b/starknet_py/contract.py @@ -428,10 +428,10 @@ async def invoke( nonce: Optional[int] = None, ) -> InvokeResult: """ - Send an Invoke transaction version 1 for a prepared data. + Send an Invoke transaction version 1 for the prepared data. :param max_fee: Max amount of Wei to be paid when executing transaction. - :param auto_estimate: Use automatic fee estimation, not recommend as it may lead to high costs. + :param auto_estimate: Use automatic fee estimation (not recommended, as it may lead to high costs). :param nonce: Nonce of the transaction. :return: InvokeResult. """ @@ -484,11 +484,11 @@ async def invoke( nonce: Optional[int] = None, ) -> InvokeResult: """ - Send an Invoke transaction version 3 for a prepared data. + Send an Invoke transaction version 3 for the prepared data. :param l1_resource_bounds: Max amount and max price per unit of L1 gas (in Fri) used when executing this transaction. - :param auto_estimate: Use automatic fee estimation, not recommend as it may lead to high costs. + :param auto_estimate: Use automatic fee estimation (not recommended, as it may lead to high costs). :param nonce: Nonce of the transaction. :return: InvokeResult. """ @@ -647,7 +647,7 @@ async def invoke_v1( Equivalent of ``.prepare_invoke_v1(*args, **kwargs).invoke()``. :param max_fee: Max amount of Wei to be paid when executing transaction. - :param auto_estimate: Use automatic fee estimation, not recommend as it may lead to high costs. + :param auto_estimate: Use automatic fee estimation (not recommended, as it may lead to high costs). :param nonce: Nonce of the transaction. :return: InvokeResult. """ @@ -698,7 +698,7 @@ async def invoke_v3( :param l1_resource_bounds: Max amount and max price per unit of L1 gas (in Fri) used when executing this transaction. - :param auto_estimate: Use automatic fee estimation, not recommend as it may lead to high costs. + :param auto_estimate: Use automatic fee estimation (not recommended, as it may lead to high costs). :param nonce: Nonce of the transaction. :return: InvokeResult. """