From e25729fffaffa16c8ea275f4d5a71ed601800f60 Mon Sep 17 00:00:00 2001 From: baitcode Date: Mon, 2 Dec 2024 15:30:28 +0300 Subject: [PATCH 01/51] Big things: - Added new Transaction models: InvokeOutsideV1, InvokeOutsideV2 - Added method for SNIP9 nonce verification - Added Account methods for population and broadcasting(execution) of InvokeOutside transactions Small fixes: - Allowed for incomplete definition of ParameterDict as `contains` fiels is often missing and linter is complaining. WIP: miss docs and tests --- starknet_py/constants.py | 3 + starknet_py/net/account/account.py | 143 +++++++++++++++++++++++++- starknet_py/net/client_models.py | 18 ++++ starknet_py/net/models/transaction.py | 128 ++++++++++++++++++++++- starknet_py/net/models/typed_data.py | 2 +- 5 files changed, 290 insertions(+), 4 deletions(-) diff --git a/starknet_py/constants.py b/starknet_py/constants.py index 00f3d9a67..1013a4b03 100644 --- a/starknet_py/constants.py +++ b/starknet_py/constants.py @@ -45,3 +45,6 @@ PUBLIC_KEY_RESPONSE_LENGTH = 65 SIGNATURE_RESPONSE_LENGTH = 65 VERSION_RESPONSE_LENGTH = 3 + +# SNIP-9 ANY_CALLER +ANY_CALLER = 0x414e595f43414c4c4552 \ No newline at end of file diff --git a/starknet_py/net/account/account.py b/starknet_py/net/account/account.py index d5b7e1213..9e2a7cfb1 100644 --- a/starknet_py/net/account/account.py +++ b/starknet_py/net/account/account.py @@ -4,7 +4,7 @@ from typing import Any, Dict, Iterable, List, Optional, Tuple, Union from starknet_py.common import create_compiled_contract, create_sierra_compiled_contract -from starknet_py.constants import FEE_CONTRACT_ADDRESS, QUERY_VERSION_BASE +from starknet_py.constants import FEE_CONTRACT_ADDRESS, QUERY_VERSION_BASE, ANY_CALLER from starknet_py.hash.address import compute_address from starknet_py.hash.selector import get_selector_from_name from starknet_py.hash.utils import verify_message_signature @@ -17,6 +17,7 @@ EstimatedFee, Hash, ResourceBounds, + ExecutionTimeBounds, ResourceBoundsMapping, SentTransactionResponse, SierraContractClass, @@ -34,6 +35,8 @@ DeployAccountV3, InvokeV1, InvokeV3, + InvokeOutsideV1, + InvokeOutsideV2, TypeAccountTransaction, ) from starknet_py.net.models.typed_data import TypedDataDict @@ -218,6 +221,54 @@ async def _prepare_invoke( return _add_max_fee_to_transaction(transaction, max_fee) + async def _prepare_invoke_outside_v1( + self, + calls: Calls, + execution_time_bounds: ExecutionTimeBounds, + caller: AddressRepresentation, + *, + nonce: Optional[int] = None, + ) -> InvokeOutsideV1: + if nonce is None: + nonce = await self.get_SNIP9_nonce() + + transaction = InvokeOutsideV1( + calls=list(ensure_iterable(calls)), + execute_after=execution_time_bounds.execute_after, + execute_before=execution_time_bounds.execute_before, + caller_address=parse_address(caller), + signer_address=self.address, + nonce=nonce, + signature=[], # TODO(baitcode): should be default + version=1, + ) + + return transaction + + async def _prepare_invoke_outside_v2( + self, + calls: Calls, + execution_time_bounds: ExecutionTimeBounds, + caller: AddressRepresentation, + *, + nonce: Optional[int] = None, + ) -> InvokeOutsideV2: + if nonce is None: + nonce = await self.get_SNIP9_nonce() + + transaction = InvokeOutsideV2( + calls=list(ensure_iterable(calls)), + execute_after=execution_time_bounds.execute_after, + execute_before=execution_time_bounds.execute_before, + caller_address=parse_address(caller), + signer_address=self.address, + nonce=nonce or await self.get_nonce(), + signature=[], + version=2, + ) + + return transaction + async def _prepare_invoke_v3( self, calls: Calls, @@ -290,6 +341,31 @@ async def get_nonce( self.address, block_hash=block_hash, block_number=block_number ) + async def check_SNIP9_nonce( + self, + nonce: int, + *, + block_hash: Optional[Union[Hash, Tag]] = None, + block_number: Optional[Union[int, Tag]] = None, + ) -> bool: + (is_valid, ) = await self._client.call_contract( + Call( + to_addr=parse_address(self.address), + selector=get_selector_from_name("is_valid_outside_execution_nonce"), + calldata=[nonce], + ), + block_hash=block_hash, block_number=block_number + ) + return bool(is_valid) + + async def get_SNIP9_nonce(self) -> int: + while True: # TODO(baitcode): add a limit to avoid infinite loop + + random_stark_address = KeyPair.generate().public_key + + if await self.check_SNIP9_nonce(random_stark_address): + return random_stark_address + async def get_balance( self, token_address: Optional[AddressRepresentation] = None, @@ -344,6 +420,40 @@ async def sign_invoke_v1( signature = self.signer.sign_transaction(execute_tx) return _add_signature_to_transaction(execute_tx, signature) + async def sign_execute_outside_v1( + self, + calls: Calls, + execution_time_bounds: ExecutionTimeBounds, + *, + caller: AddressRepresentation = ANY_CALLER, + nonce: Optional[int] = None, + ) -> InvokeOutsideV1: + execute_outside_tx = await self._prepare_invoke_outside_v1( + calls, + execution_time_bounds, + caller=caller, + nonce=nonce or await self.get_SNIP9_nonce(), + ) + signature = self.signer.sign_transaction(execute_outside_tx) + return _add_signature_to_transaction(execute_outside_tx, signature) + + async def sign_execute_outside_v2( + self, + calls: Calls, + execution_time_bounds: ExecutionTimeBounds, + *, + caller: AddressRepresentation = ANY_CALLER, + nonce: Optional[int] = None, + ) -> InvokeOutsideV2: + execute_outside_tx = await self._prepare_invoke_outside_v2( + calls, + execution_time_bounds, + caller=caller, + nonce=nonce or await self.get_SNIP9_nonce(), + ) + signature = self.signer.sign_transaction(execute_outside_tx) + return _add_signature_to_transaction(execute_outside_tx, signature) + async def sign_invoke_v3( self, calls: Calls, @@ -594,6 +704,37 @@ async def execute_v3( ) return await self._client.send_transaction(execute_transaction) + async def execute_outside_v1( + self, + calls: Calls, + execution_time_bounds: ExecutionTimeBounds, + caller: AddressRepresentation = ANY_CALLER, + nonce: Optional[int] = None, + ) -> SentTransactionResponse: + execute_transaction = await self.sign_execute_outside_v1( + calls, + execution_time_bounds, + caller=caller, + nonce=nonce, + ) + return await self._client.send_transaction(execute_transaction) + + async def execute_outside_v2( + self, + calls: Calls, + execution_time_bounds: ExecutionTimeBounds, + caller: AddressRepresentation = ANY_CALLER, + nonce: Optional[int] = None, + ) -> SentTransactionResponse: + execute_transaction = await self.sign_execute_outside_v2( + calls, + execution_time_bounds, + caller=caller, + nonce=nonce, + ) + return await self._client.send_transaction(execute_transaction) + + def sign_message(self, typed_data: Union[TypedData, TypedDataDict]) -> List[int]: if isinstance(typed_data, TypedData): return self.signer.sign_message(typed_data, self.address) diff --git a/starknet_py/net/client_models.py b/starknet_py/net/client_models.py index c198d3c97..085956deb 100644 --- a/starknet_py/net/client_models.py +++ b/starknet_py/net/client_models.py @@ -7,6 +7,7 @@ to true. Consequently, any unknown fields in response will be excluded. """ +import datetime import json from abc import ABC from dataclasses import dataclass, field @@ -114,6 +115,22 @@ class ResourceBounds: def init_with_zeros(): return ResourceBounds(max_amount=0, max_price_per_unit=0) +@dataclass +class ExecutionTimeBounds: + """ + Dataclass representing time bounds within which the given time bounds. + """ + + execute_after: datetime.datetime + execute_before: datetime.datetime + + @staticmethod + def init_without_bounds(): + return ExecutionTimeBounds( + execute_after=datetime.datetime.min, + execute_before=datetime.datetime.max, + ) + @dataclass class ResourceBoundsMapping: @@ -175,6 +192,7 @@ class TransactionType(Enum): DEPLOY_ACCOUNT = "DEPLOY_ACCOUNT" DEPLOY = "DEPLOY" L1_HANDLER = "L1_HANDLER" + OUTSIDE = "OUTSIDE" @dataclass diff --git a/starknet_py/net/models/transaction.py b/starknet_py/net/models/transaction.py index e737819a4..c4dadde07 100644 --- a/starknet_py/net/models/transaction.py +++ b/starknet_py/net/models/transaction.py @@ -6,6 +6,7 @@ import base64 import dataclasses +import datetime import gzip import json from abc import ABC, abstractmethod @@ -34,12 +35,14 @@ ResourceBoundsMapping, SierraContractClass, TransactionType, + Call, ) -from starknet_py.net.schemas.common import Felt +from starknet_py.net.schemas.common import Felt, Revision from starknet_py.net.schemas.rpc.contract import ( ContractClassSchema, SierraContractClassSchema, ) +from starknet_py.utils import typed_data as td # TODO (#1219): # consider unifying these classes with client_models @@ -306,7 +309,128 @@ def calculate_hash(self, chain_id: int) -> int: chain_id=chain_id, ) +@dataclass(frozen=True) +class InvokeOutsideV1(AccountTransaction, ABC): + caller_address: int = field(metadata={"marshmallow_field": Felt()}) + signer_address: int = field(metadata={"marshmallow_field": Felt()}) + + execute_after: datetime.datetime + execute_before: datetime.datetime + + calls: List[Call] + + @property + def type(self) -> TransactionType: + return TransactionType.OUTSIDE + + def calculate_hash(self, chain_id: int) -> int: + data = td.TypedData.from_dict({ + 'types': { + 'StarkNetDomain': [ + {'name': 'name', 'type': 'felt'}, + {'name': 'version', 'type': 'felt'}, + {'name': 'chainId', 'type': 'felt'}, + ], + 'OutsideExecution': [ + {'name': 'caller', 'type': 'felt' }, + {'name': 'nonce', 'type': 'felt' }, + {'name': 'execute_after', 'type': 'felt' }, + {'name': 'execute_before', 'type': 'felt' }, + {'name': 'calls_len', 'type': 'felt' }, + {'name': 'calls', 'type': 'OutsideCall*' }, + ], + 'OutsideCall': [ + { 'name': 'to', 'type': 'felt' }, + { 'name': 'selector', 'type': 'felt' }, + { 'name': 'calldata_len', 'type': 'felt' }, + { 'name': 'calldata', 'type': 'felt*' }, + ], + }, + 'primaryType': 'OutsideExecution', + 'domain': { + 'name': 'Account.execute_from_outside', + 'version': '1', + 'chainId': str(chain_id), + 'revision': None, + }, + 'message': { + 'caller': self.caller_address, + 'nonce': self.nonce, + 'execute_after': self.execute_after.timestamp(), + 'execute_before': self.execute_before.timestamp(), + 'calls_len': len(self.calls), + 'calls': [ + { + 'to': call.to_addr, + 'selector': call.selector, + 'calldata_len': len(call.calldata), + 'calldata': call.calldata, + } for call in self.calls + ], + }, + }) + return data.message_hash(self.signer_address) + +@dataclass(frozen=True) +class InvokeOutsideV2(AccountTransaction, ABC): + caller_address: int = field(metadata={"marshmallow_field": Felt()}) + signer_address: int = field(metadata={"marshmallow_field": Felt()}) + + execute_after: datetime.datetime + execute_before: datetime.datetime + + calls: List[Call] + + @property + def type(self) -> TransactionType: + return TransactionType.OUTSIDE + + def calculate_hash(self, chain_id: int) -> int: + data = td.TypedData.from_dict({ + 'types': { + 'StarknetDomain': [ + {'name': 'name', 'type': 'shortstring'}, + {'name': 'version', 'type': 'shortstring'}, + {'name': 'chainId', 'type': 'shortstring'}, + {'name': 'revision', 'type': 'shortstring'}, + ], + 'OutsideExecution': [ + {'name': 'Caller', 'type': 'ContractAddress' }, + {'name': 'Nonce', 'type': 'felt' }, + {'name': 'Execute After', 'type': 'u128' }, + {'name': 'Execute Before', 'type': 'u128' }, + {'name': 'Calls', 'type': 'Call*' }, + ], + 'Call': [ + { 'name': 'To', 'type': 'ContractAddress' }, + { 'name': 'Selector', 'type': 'selector' }, + { 'name': 'Calldata', 'type': 'felt*' }, + ], + }, + 'primaryType': 'OutsideExecution', + 'domain': { + 'name': 'Account.execute_from_outside', + 'version': '2', + 'chainId': str(chain_id), + 'revision': Revision.V1, + }, + 'message': { + 'Caller': self.caller_address, + 'Nonce': self.nonce, + 'Execute After': self.execute_after.timestamp(), + 'Execute Before': self.execute_before.timestamp(), + 'Calls': [ + { + 'To': call.to_addr, + 'Selector': call.selector, + 'Calldata': call.calldata, + } for call in self.calls + ], + }, + }) + return data.message_hash(self.signer_address) + @dataclass(frozen=True) class InvokeV3(_AccountTransactionV3): """ @@ -366,7 +490,7 @@ def calculate_hash(self, chain_id: int) -> int: Declare = Union[DeclareV1, DeclareV2, DeclareV3] DeployAccount = Union[DeployAccountV1, DeployAccountV3] -Invoke = Union[InvokeV1, InvokeV3] +Invoke = Union[InvokeV1, InvokeV3, InvokeOutsideV1, InvokeOutsideV2] InvokeV1Schema = marshmallow_dataclass.class_schema(InvokeV1) DeclareV1Schema = marshmallow_dataclass.class_schema(DeclareV1) diff --git a/starknet_py/net/models/typed_data.py b/starknet_py/net/models/typed_data.py index 6a2c23aba..67f3125a6 100644 --- a/starknet_py/net/models/typed_data.py +++ b/starknet_py/net/models/typed_data.py @@ -7,7 +7,7 @@ from starknet_py.net.schemas.common import Revision -class ParameterDict(TypedDict): +class ParameterDict(TypedDict, total=False): """ TypedDict representing a Parameter object """ From 40b01edb194532da5cd1dc60cd3310be0eb2fd1e Mon Sep 17 00:00:00 2001 From: baitcode Date: Mon, 2 Dec 2024 16:47:41 +0300 Subject: [PATCH 02/51] - Added outside execution model, defined hashing - Extended base account interface - Added all utilities to deal with SNIP-9 nonce and generating OutsideExecution call - Bugfix type data generation - Cleaned up as much as I could to keep changes minimal - Added docs - Added test for positive scenario - Added one test for wrong caller scenario --- .pylintrc | 2 +- docs/guide/account_and_client.rst | 9 + starknet_py/constants.py | 8 +- starknet_py/hash/outside_execution.py | 113 ++++++++++ starknet_py/net/account/account.py | 207 ++++++++---------- starknet_py/net/account/base_account.py | 39 +++- starknet_py/net/client_models.py | 49 ++++- starknet_py/net/client_utils.py | 11 +- starknet_py/net/full_node_client.py | 12 +- starknet_py/net/models/transaction.py | 128 +---------- starknet_py/net/schemas/common.py | 14 +- starknet_py/net/schemas/rpc/transactions.py | 2 +- .../data_serializers/__init__.py | 1 + starknet_py/tests/e2e/account/account_test.py | 120 ++++++++++ .../test_account_sign_outside_transaction.py | 76 +++++++ starknet_py/tests/unit/net/client_test.py | 3 +- .../tests/unit/net/models/transaction_test.py | 35 +++ 17 files changed, 554 insertions(+), 275 deletions(-) create mode 100644 starknet_py/hash/outside_execution.py create mode 100644 starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py create mode 100644 starknet_py/tests/unit/net/models/transaction_test.py diff --git a/.pylintrc b/.pylintrc index 75878ec2d..7f3c12362 100644 --- a/.pylintrc +++ b/.pylintrc @@ -561,7 +561,7 @@ max-locals=15 max-parents=7 # Maximum number of public methods for a class (see R0904). -max-public-methods=20 +max-public-methods=23 # Maximum number of return / yield for function / method body. max-returns=6 diff --git a/docs/guide/account_and_client.rst b/docs/guide/account_and_client.rst index fa111b5f1..38d9e4727 100644 --- a/docs/guide/account_and_client.rst +++ b/docs/guide/account_and_client.rst @@ -46,6 +46,15 @@ Account also provides a way of creating signed transaction without sending them. :language: python :dedent: 4 +Creating "Outside transaction" and executing it. `SNIP-9 `_ +-------------------------------------------- + +Account also provides a way of creating a call and signing to allow for another account (caller) to execute it later on original account behalf. This will also allow caller to execute calls encoded in that transaction for free (signer will pay the fee). + +.. codesnippet:: ../../starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py + :language: python + :dedent: 4 + Multicall --------- diff --git a/starknet_py/constants.py b/starknet_py/constants.py index 1013a4b03..25014fe70 100644 --- a/starknet_py/constants.py +++ b/starknet_py/constants.py @@ -1,3 +1,4 @@ +from enum import IntEnum from pathlib import Path # Address came from starkware-libs/starknet-addresses repository: https://github.com/starkware-libs/starknet-addresses @@ -47,4 +48,9 @@ VERSION_RESPONSE_LENGTH = 3 # SNIP-9 ANY_CALLER -ANY_CALLER = 0x414e595f43414c4c4552 \ No newline at end of file +ANY_CALLER = 0x414e595f43414c4c4552 + +# SNIP-9 INTERFACE_VERSION with ID +class SNIP9InterfaceVersion(IntEnum): + V1 = 0x68cfd18b92d1907b8ba3cc324900277f5a3622099431ea85dd8089255e4181 + V2 = 0x1d1144bb2138366ff28d8e9ab57456b1d332ac42196230c3a602003c89872 diff --git a/starknet_py/hash/outside_execution.py b/starknet_py/hash/outside_execution.py new file mode 100644 index 000000000..05ad21b20 --- /dev/null +++ b/starknet_py/hash/outside_execution.py @@ -0,0 +1,113 @@ +from starknet_py.net.client_models import OutsideExecution + +from starknet_py.constants import SNIP9InterfaceVersion + +from starknet_py.net.schemas.common import Revision +from starknet_py.utils import typed_data as td + +SNIP9_INTERFACE_ID_TO_SNIP12_REVISION = { + SNIP9InterfaceVersion.V1: Revision.V0, + SNIP9InterfaceVersion.V2: Revision.V1, +} + +def outside_execution_to_typed_data( + outside_execution: OutsideExecution, + snip9_version: SNIP9InterfaceVersion, + chain_id: int +) -> td.TypedData: + """ + SNIP-12 Typed Data for OutsideExecution implementation. For revision V0 and V1. + """ + + revision = SNIP9_INTERFACE_ID_TO_SNIP12_REVISION[snip9_version] + + if revision == Revision.V0: + return td.TypedData.from_dict({ + 'types': { + 'StarkNetDomain': [ + {'name': 'name', 'type': 'felt'}, + {'name': 'version', 'type': 'felt'}, + {'name': 'chainId', 'type': 'felt'}, + ], + 'OutsideExecution': [ + {'name': 'caller', 'type': 'felt' }, + {'name': 'nonce', 'type': 'felt' }, + {'name': 'execute_after', 'type': 'felt' }, + {'name': 'execute_before', 'type': 'felt' }, + {'name': 'calls_len', 'type': 'felt' }, + {'name': 'calls', 'type': 'OutsideCall*' }, + ], + 'OutsideCall': [ + { 'name': 'to', 'type': 'felt' }, + { 'name': 'selector', 'type': 'felt' }, + { 'name': 'calldata_len', 'type': 'felt' }, + { 'name': 'calldata', 'type': 'felt*' }, + ], + }, + 'primaryType': 'OutsideExecution', + 'domain': { + 'name': 'Account.execute_from_outside', + 'version': '1', + 'chainId': str(chain_id), + 'revision': Revision.V0, + }, + 'message': { + 'caller': outside_execution.caller, + 'nonce': outside_execution.nonce, + 'execute_after': outside_execution.execute_after, + 'execute_before': outside_execution.execute_before, + 'calls_len': len(outside_execution.calls), + 'calls': [ + { + 'to': call.to_addr, + 'selector': call.selector, + 'calldata_len': len(call.calldata), + 'calldata': call.calldata, + } for call in outside_execution.calls + ], + }, + }) + + # revision == Revision.V1 + return td.TypedData.from_dict({ + 'types': { + 'StarknetDomain': [ + {'name': 'name', 'type': 'shortstring'}, + {'name': 'version', 'type': 'shortstring'}, + {'name': 'chainId', 'type': 'shortstring'}, + {'name': 'revision', 'type': 'shortstring'}, + ], + 'OutsideExecution': [ + {'name': 'Caller', 'type': 'ContractAddress' }, + {'name': 'Nonce', 'type': 'felt' }, + {'name': 'Execute After', 'type': 'u128' }, + {'name': 'Execute Before', 'type': 'u128' }, + {'name': 'Calls', 'type': 'Call*' }, + ], + 'Call': [ + { 'name': 'To', 'type': 'ContractAddress' }, + { 'name': 'Selector', 'type': 'selector' }, + { 'name': 'Calldata', 'type': 'felt*' }, + ], + }, + 'primaryType': 'OutsideExecution', + 'domain': { + 'name': 'Account.execute_from_outside', + 'version': '2', + 'chainId': str(chain_id), + 'revision': Revision.V1, + }, + 'message': { + 'Caller': outside_execution.caller, + 'Nonce': outside_execution.nonce, + 'Execute After': outside_execution.execute_after, + 'Execute Before': outside_execution.execute_before, + 'Calls': [ + { + 'To': call.to_addr, + 'Selector': call.selector, + 'Calldata': call.calldata, + } for call in outside_execution.calls + ], + }, + }) diff --git a/starknet_py/net/account/account.py b/starknet_py/net/account/account.py index 9e2a7cfb1..3714c646f 100644 --- a/starknet_py/net/account/account.py +++ b/starknet_py/net/account/account.py @@ -8,9 +8,11 @@ from starknet_py.hash.address import compute_address from starknet_py.hash.selector import get_selector_from_name from starknet_py.hash.utils import verify_message_signature +from starknet_py.hash.outside_execution import outside_execution_to_typed_data from starknet_py.net.account.account_deployment_result import AccountDeploymentResult -from starknet_py.net.account.base_account import BaseAccount +from starknet_py.net.account.base_account import BaseAccount, SNIP9SupportMixin from starknet_py.net.client import Client +from starknet_py.constants import SNIP9InterfaceVersion from starknet_py.net.client_models import ( Call, Calls, @@ -21,6 +23,7 @@ ResourceBoundsMapping, SentTransactionResponse, SierraContractClass, + OutsideExecution, Tag, ) from starknet_py.net.full_node_client import FullNodeClient @@ -35,20 +38,17 @@ DeployAccountV3, InvokeV1, InvokeV3, - InvokeOutsideV1, - InvokeOutsideV2, TypeAccountTransaction, ) from starknet_py.net.models.typed_data import TypedDataDict from starknet_py.net.signer import BaseSigner from starknet_py.net.signer.stark_curve_signer import KeyPair, StarkCurveSigner -from starknet_py.serialization.data_serializers.array_serializer import ArraySerializer -from starknet_py.serialization.data_serializers.felt_serializer import FeltSerializer -from starknet_py.serialization.data_serializers.payload_serializer import ( +from starknet_py.serialization.data_serializers import ( + ArraySerializer, + FeltSerializer, PayloadSerializer, -) -from starknet_py.serialization.data_serializers.struct_serializer import ( StructSerializer, + UintSerializer, ) from starknet_py.utils.iterable import ensure_iterable from starknet_py.utils.sync import add_sync_methods @@ -56,7 +56,7 @@ @add_sync_methods -class Account(BaseAccount): +class Account(BaseAccount, SNIP9SupportMixin): """ Default Account implementation. """ @@ -221,53 +221,6 @@ async def _prepare_invoke( return _add_max_fee_to_transaction(transaction, max_fee) - async def _prepare_invoke_outside_v1( - self, - calls: Calls, - execution_time_bounds: ExecutionTimeBounds, - caller: AddressRepresentation, - *, - nonce: Optional[int] = None, - ) -> InvokeOutsideV1: - if nonce is None: - nonce = await self.get_SNIP9_nonce() - - transaction = InvokeOutsideV1( - calls=list(ensure_iterable(calls)), - execute_after=execution_time_bounds.execute_after, - execute_before=execution_time_bounds.execute_before, - caller_address=parse_address(caller), - signer_address=self.address, - nonce=nonce, - signature=[], # TODO(baitcode): should be default - version=1, - ) - - return transaction - - async def _prepare_invoke_outside_v2( - self, - calls: Calls, - execution_time_bounds: ExecutionTimeBounds, - caller: AddressRepresentation, - *, - nonce: Optional[int] = None, - ) -> InvokeOutsideV2: - if nonce is None: - nonce = await self.get_SNIP9_nonce() - - transaction = InvokeOutsideV2( - calls=list(ensure_iterable(calls)), - execute_after=execution_time_bounds.execute_after, - execute_before=execution_time_bounds.execute_before, - caller_address=parse_address(caller), - signer_address=self.address, - nonce=nonce or await self.get_nonce(), - signature=[], - version=2, - ) - - return transaction async def _prepare_invoke_v3( self, @@ -341,16 +294,16 @@ async def get_nonce( self.address, block_hash=block_hash, block_number=block_number ) - async def check_SNIP9_nonce( - self, + async def _check_snip9_nonce( + self, nonce: int, *, block_hash: Optional[Union[Hash, Tag]] = None, block_number: Optional[Union[int, Tag]] = None, ) -> bool: (is_valid, ) = await self._client.call_contract( - Call( - to_addr=parse_address(self.address), + call=Call( + to_addr=self.address, selector=get_selector_from_name("is_valid_outside_execution_nonce"), calldata=[nonce], ), @@ -358,13 +311,29 @@ async def check_SNIP9_nonce( ) return bool(is_valid) - async def get_SNIP9_nonce(self) -> int: - while True: # TODO(baitcode): add a limit to avoid infinite loop - + async def get_snip9_nonce(self, retry_count=10) -> int: + while retry_count > 0: random_stark_address = KeyPair.generate().public_key - - if await self.check_SNIP9_nonce(random_stark_address): + if await self._check_snip9_nonce(random_stark_address): return random_stark_address + retry_count -= 1 + raise RuntimeError("Failed to generate a valid nonce") + + async def _get_snip9_version(self) -> Union[SNIP9InterfaceVersion, None]: + for version in [SNIP9InterfaceVersion.V1, SNIP9InterfaceVersion.V2]: + if await self.supports_interface(version): + return version + return None + + async def supports_interface(self, interface_id: SNIP9InterfaceVersion) -> bool: + (does_support,) = await self._client.call_contract( + Call( + to_addr=self.address, + selector=get_selector_from_name("supports_interface"), + calldata=[interface_id], + ) + ) + return bool(does_support) async def get_balance( self, @@ -420,39 +389,51 @@ async def sign_invoke_v1( signature = self.signer.sign_transaction(execute_tx) return _add_signature_to_transaction(execute_tx, signature) - async def sign_execute_outside_v1( + async def sign_outside_execution_call( self, calls: Calls, execution_time_bounds: ExecutionTimeBounds, *, caller: AddressRepresentation = ANY_CALLER, - nonce: Optional[int] = None, - ) -> InvokeOutsideV1: - execute_outside_tx = await self._prepare_invoke_outside_v1( - calls, - execution_time_bounds, - caller=caller, - nonce=nonce or await self.get_SNIP9_nonce(), + nonce: Optional[int] = None, + version: Optional[SNIP9InterfaceVersion] = None, + ) -> Call: + if version is None: + version = await self._get_snip9_version() + + if version is None: + raise RuntimeError("Can't initiate outside execution SNIP-9 is unsupported.") + + if nonce is None: + nonce = await self.get_snip9_nonce() + + outside_execution = OutsideExecution( + caller=parse_address(caller), + nonce=nonce, + execute_after=execution_time_bounds.execute_after_timestamp, + execute_before=execution_time_bounds.execute_before_timestamp, + calls=list(ensure_iterable(calls)), ) - signature = self.signer.sign_transaction(execute_outside_tx) - return _add_signature_to_transaction(execute_outside_tx, signature) + chain_id = await self._get_chain_id() + signature = self.signer.sign_message( + outside_execution_to_typed_data( + outside_execution, version, chain_id + ), + self.address + ) + selector_for_version = { + SNIP9InterfaceVersion.V1: "execute_from_outside", + SNIP9InterfaceVersion.V2: "execute_from_outside_v2" + } - async def sign_execute_outside_v2( - self, - calls: Calls, - execution_time_bounds: ExecutionTimeBounds, - *, - caller: AddressRepresentation = ANY_CALLER, - nonce: Optional[int] = None, - ) -> InvokeOutsideV2: - execute_outside_tx = await self._prepare_invoke_outside_v2( - calls, - execution_time_bounds, - caller=caller, - nonce=nonce or await self.get_SNIP9_nonce(), + return Call( + to_addr=self.address, + selector=get_selector_from_name(selector_for_version[version]), + calldata=_transaction_serialiser.serialize({ + "external_execution": outside_execution.to_abi_dict(), + "signature": signature + }) ) - signature = self.signer.sign_transaction(execute_outside_tx) - return _add_signature_to_transaction(execute_outside_tx, signature) async def sign_invoke_v3( self, @@ -704,37 +685,6 @@ async def execute_v3( ) return await self._client.send_transaction(execute_transaction) - async def execute_outside_v1( - self, - calls: Calls, - execution_time_bounds: ExecutionTimeBounds, - caller: AddressRepresentation = ANY_CALLER, - nonce: Optional[int] = None, - ) -> SentTransactionResponse: - execute_transaction = await self.sign_execute_outside_v1( - calls, - execution_time_bounds, - caller=caller, - nonce=nonce, - ) - return await self._client.send_transaction(execute_transaction) - - async def execute_outside_v2( - self, - calls: Calls, - execution_time_bounds: ExecutionTimeBounds, - caller: AddressRepresentation = ANY_CALLER, - nonce: Optional[int] = None, - ) -> SentTransactionResponse: - execute_transaction = await self.sign_execute_outside_v2( - calls, - execution_time_bounds, - caller=caller, - nonce=nonce, - ) - return await self._client.send_transaction(execute_transaction) - - def sign_message(self, typed_data: Union[TypedData, TypedDataDict]) -> List[int]: if isinstance(typed_data, TypedData): return self.signer.sign_message(typed_data, self.address) @@ -1030,3 +980,18 @@ def _parse_calls_cairo_v1(calls: Iterable[Call]) -> List[Dict]: calls=ArraySerializer(_call_description_cairo_v1), ) ) + +_transaction_serialiser = StructSerializer( + OrderedDict( + external_execution=StructSerializer( + OrderedDict( + caller=FeltSerializer(), + nonce=FeltSerializer(), + execute_after=UintSerializer(bits=64), + execute_before=UintSerializer(bits=64), + calls=ArraySerializer(_call_description_cairo_v1), + ) + ), + signature=ArraySerializer(FeltSerializer()), + ) +) diff --git a/starknet_py/net/account/base_account.py b/starknet_py/net/account/base_account.py index 5542a9dad..5eaf1f270 100644 --- a/starknet_py/net/account/base_account.py +++ b/starknet_py/net/account/base_account.py @@ -1,14 +1,17 @@ from abc import ABC, abstractmethod from typing import List, Optional, Union +from starknet_py.constants import SNIP9InterfaceVersion, ANY_CALLER from starknet_py.net.client import Client from starknet_py.net.client_models import ( Calls, EstimatedFee, Hash, ResourceBounds, + ExecutionTimeBounds, SentTransactionResponse, Tag, + Call ) from starknet_py.net.models import AddressRepresentation from starknet_py.net.models.transaction import ( @@ -24,8 +27,42 @@ ) from starknet_py.net.models.typed_data import TypedDataDict +class SNIP9SupportMixin(ABC): -class BaseAccount(ABC): + @abstractmethod + async def get_snip9_nonce(self) -> int: + """ + Generate special valid nonce (passed check_snip9_nonce) for external calls execution. + """ + + @abstractmethod + async def supports_interface(self, interface_id: SNIP9InterfaceVersion) -> bool: + """ + Check if the account supports the given SNIP9 interface. Part of ISRC5 standard. + """ + + @abstractmethod + async def sign_outside_execution_call( + self, + calls: Calls, + execution_time_bounds: ExecutionTimeBounds, + *, + caller: AddressRepresentation = ANY_CALLER, + nonce: Optional[int] = None, + version: Optional[SNIP9InterfaceVersion] = None, + ) -> Call: + """ + Creates a call for an external execution (SNIP-9 specification). + + :param calls: Single call or list of calls to be executed by outside caller. + :param execution_time_bounds: Execution time bounds for the call. + :param caller: Address of the caller. IMPORTANT! By default it is ANY_CALLER. + :param nonce: Nonce for the transaction. Is populated automatically if not provided. + :param version: SNIP-9 interface version. Method will check which version account + supports and use the highest one and populate the value. + """ + +class BaseAccount(SNIP9SupportMixin, ABC): """ Base class for all account implementations. diff --git a/starknet_py/net/client_models.py b/starknet_py/net/client_models.py index 085956deb..2513b2e61 100644 --- a/starknet_py/net/client_models.py +++ b/starknet_py/net/client_models.py @@ -12,7 +12,7 @@ from abc import ABC from dataclasses import dataclass, field from enum import Enum -from typing import Any, Iterable, List, Literal, Optional, Union, cast +from typing import Any, Iterable, List, Literal, Optional, Union, cast, Dict from marshmallow import EXCLUDE @@ -27,10 +27,13 @@ ) from starknet_py.abi.v2.shape import AbiDictEntry as AbiDictEntryV2 from starknet_py.abi.v2.shape import AbiDictList as AbiDictListV2 + from starknet_py.utils.constructor_args_translator import _is_abi_v2 + # pylint: disable=too-many-lines + Hash = Union[int, str] Tag = Literal["pending", "latest"] @@ -124,12 +127,13 @@ class ExecutionTimeBounds: execute_after: datetime.datetime execute_before: datetime.datetime - @staticmethod - def init_without_bounds(): - return ExecutionTimeBounds( - execute_after=datetime.datetime.min, - execute_before=datetime.datetime.max, - ) + @property + def execute_after_timestamp(self) -> int: + return int(self.execute_after.timestamp()) + + @property + def execute_before_timestamp(self) -> int: + return int(self.execute_before.timestamp()) @dataclass @@ -192,7 +196,6 @@ class TransactionType(Enum): DEPLOY_ACCOUNT = "DEPLOY_ACCOUNT" DEPLOY = "DEPLOY" L1_HANDLER = "L1_HANDLER" - OUTSIDE = "OUTSIDE" @dataclass @@ -1124,3 +1127,33 @@ class BlockTransactionTrace: transaction_hash: int trace_root: TransactionTrace + +@dataclass +class OutsideExecution: + """ + Dataclass representing an outside execution. + (SNIP-9)[https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-9.md] + """ + + caller: int + nonce: int + execute_after: int + execute_before: int + calls: List[Call] + + def to_abi_dict(self) -> Dict: + """ + Returns a dictionary that can be serialized (compiled) into calldata + using StructSerializer + """ + return { + "caller": self.caller, + "nonce": self.nonce, + "execute_after": self.execute_after, + "execute_before": self.execute_before, + "calls": [{ + "to": call.to_addr, + "selector": call.selector, + "calldata": call.calldata + } for call in self.calls] + } diff --git a/starknet_py/net/client_utils.py b/starknet_py/net/client_utils.py index 27a86ff3b..97b3f45a2 100644 --- a/starknet_py/net/client_utils.py +++ b/starknet_py/net/client_utils.py @@ -1,12 +1,10 @@ import re -from typing import Dict, Union, cast +from typing import Union from typing_extensions import get_args from starknet_py.hash.utils import encode_uint, encode_uint_list from starknet_py.net.client_models import Hash, L1HandlerTransaction, Tag -from starknet_py.net.models.transaction import AccountTransaction -from starknet_py.net.schemas.broadcasted_txn import BroadcastedTransactionSchema def hash_to_felt(value: Hash) -> str: @@ -79,10 +77,3 @@ def _is_valid_eth_address(address: str) -> bool: A function checking if an address matches Ethereum address regex. Note that it doesn't validate any checksums etc. """ return bool(re.fullmatch("^0x[a-fA-F0-9]{40}$", address)) - - -def _create_broadcasted_txn(transaction: AccountTransaction) -> dict: - return cast( - Dict, - BroadcastedTransactionSchema().dump(obj=transaction), - ) diff --git a/starknet_py/net/full_node_client.py b/starknet_py/net/full_node_client.py index 5137d9e4c..19f49a4f6 100644 --- a/starknet_py/net/full_node_client.py +++ b/starknet_py/net/full_node_client.py @@ -1,4 +1,4 @@ -from typing import List, Optional, Tuple, Union, cast +from typing import List, Optional, Tuple, Union, cast, Dict import aiohttp @@ -37,7 +37,6 @@ TransactionTrace, ) from starknet_py.net.client_utils import ( - _create_broadcasted_txn, _is_valid_eth_address, _to_rpc_felt, _to_storage_key, @@ -81,10 +80,19 @@ TransactionStatusResponseSchema, TypesOfTransactionsSchema, ) +from starknet_py.net.schemas.broadcasted_txn import ( + BroadcastedTransactionSchema, +) from starknet_py.transaction_errors import TransactionNotReceivedError from starknet_py.utils.sync import add_sync_methods +def _create_broadcasted_txn(transaction: AccountTransaction) -> dict: + return cast( + Dict, + BroadcastedTransactionSchema().dump(obj=transaction), + ) + @add_sync_methods class FullNodeClient(Client): # pylint: disable=too-many-public-methods diff --git a/starknet_py/net/models/transaction.py b/starknet_py/net/models/transaction.py index c4dadde07..e737819a4 100644 --- a/starknet_py/net/models/transaction.py +++ b/starknet_py/net/models/transaction.py @@ -6,7 +6,6 @@ import base64 import dataclasses -import datetime import gzip import json from abc import ABC, abstractmethod @@ -35,14 +34,12 @@ ResourceBoundsMapping, SierraContractClass, TransactionType, - Call, ) -from starknet_py.net.schemas.common import Felt, Revision +from starknet_py.net.schemas.common import Felt from starknet_py.net.schemas.rpc.contract import ( ContractClassSchema, SierraContractClassSchema, ) -from starknet_py.utils import typed_data as td # TODO (#1219): # consider unifying these classes with client_models @@ -309,128 +306,7 @@ def calculate_hash(self, chain_id: int) -> int: chain_id=chain_id, ) -@dataclass(frozen=True) -class InvokeOutsideV1(AccountTransaction, ABC): - caller_address: int = field(metadata={"marshmallow_field": Felt()}) - signer_address: int = field(metadata={"marshmallow_field": Felt()}) - - execute_after: datetime.datetime - execute_before: datetime.datetime - - calls: List[Call] - - @property - def type(self) -> TransactionType: - return TransactionType.OUTSIDE - - def calculate_hash(self, chain_id: int) -> int: - data = td.TypedData.from_dict({ - 'types': { - 'StarkNetDomain': [ - {'name': 'name', 'type': 'felt'}, - {'name': 'version', 'type': 'felt'}, - {'name': 'chainId', 'type': 'felt'}, - ], - 'OutsideExecution': [ - {'name': 'caller', 'type': 'felt' }, - {'name': 'nonce', 'type': 'felt' }, - {'name': 'execute_after', 'type': 'felt' }, - {'name': 'execute_before', 'type': 'felt' }, - {'name': 'calls_len', 'type': 'felt' }, - {'name': 'calls', 'type': 'OutsideCall*' }, - ], - 'OutsideCall': [ - { 'name': 'to', 'type': 'felt' }, - { 'name': 'selector', 'type': 'felt' }, - { 'name': 'calldata_len', 'type': 'felt' }, - { 'name': 'calldata', 'type': 'felt*' }, - ], - }, - 'primaryType': 'OutsideExecution', - 'domain': { - 'name': 'Account.execute_from_outside', - 'version': '1', - 'chainId': str(chain_id), - 'revision': None, - }, - 'message': { - 'caller': self.caller_address, - 'nonce': self.nonce, - 'execute_after': self.execute_after.timestamp(), - 'execute_before': self.execute_before.timestamp(), - 'calls_len': len(self.calls), - 'calls': [ - { - 'to': call.to_addr, - 'selector': call.selector, - 'calldata_len': len(call.calldata), - 'calldata': call.calldata, - } for call in self.calls - ], - }, - }) - return data.message_hash(self.signer_address) - -@dataclass(frozen=True) -class InvokeOutsideV2(AccountTransaction, ABC): - caller_address: int = field(metadata={"marshmallow_field": Felt()}) - signer_address: int = field(metadata={"marshmallow_field": Felt()}) - - execute_after: datetime.datetime - execute_before: datetime.datetime - - calls: List[Call] - - @property - def type(self) -> TransactionType: - return TransactionType.OUTSIDE - - def calculate_hash(self, chain_id: int) -> int: - data = td.TypedData.from_dict({ - 'types': { - 'StarknetDomain': [ - {'name': 'name', 'type': 'shortstring'}, - {'name': 'version', 'type': 'shortstring'}, - {'name': 'chainId', 'type': 'shortstring'}, - {'name': 'revision', 'type': 'shortstring'}, - ], - 'OutsideExecution': [ - {'name': 'Caller', 'type': 'ContractAddress' }, - {'name': 'Nonce', 'type': 'felt' }, - {'name': 'Execute After', 'type': 'u128' }, - {'name': 'Execute Before', 'type': 'u128' }, - {'name': 'Calls', 'type': 'Call*' }, - ], - 'Call': [ - { 'name': 'To', 'type': 'ContractAddress' }, - { 'name': 'Selector', 'type': 'selector' }, - { 'name': 'Calldata', 'type': 'felt*' }, - ], - }, - 'primaryType': 'OutsideExecution', - 'domain': { - 'name': 'Account.execute_from_outside', - 'version': '2', - 'chainId': str(chain_id), - 'revision': Revision.V1, - }, - 'message': { - 'Caller': self.caller_address, - 'Nonce': self.nonce, - 'Execute After': self.execute_after.timestamp(), - 'Execute Before': self.execute_before.timestamp(), - 'Calls': [ - { - 'To': call.to_addr, - 'Selector': call.selector, - 'Calldata': call.calldata, - } for call in self.calls - ], - }, - }) - return data.message_hash(self.signer_address) - @dataclass(frozen=True) class InvokeV3(_AccountTransactionV3): """ @@ -490,7 +366,7 @@ def calculate_hash(self, chain_id: int) -> int: Declare = Union[DeclareV1, DeclareV2, DeclareV3] DeployAccount = Union[DeployAccountV1, DeployAccountV3] -Invoke = Union[InvokeV1, InvokeV3, InvokeOutsideV1, InvokeOutsideV2] +Invoke = Union[InvokeV1, InvokeV3] InvokeV1Schema = marshmallow_dataclass.class_schema(InvokeV1) DeclareV1Schema = marshmallow_dataclass.class_schema(DeclareV1) diff --git a/starknet_py/net/schemas/common.py b/starknet_py/net/schemas/common.py index d0e03d85c..322dd4331 100644 --- a/starknet_py/net/schemas/common.py +++ b/starknet_py/net/schemas/common.py @@ -79,6 +79,14 @@ def _is_str_and_valid_pattern(self, value: Any) -> bool: and re.fullmatch(self.REGEX_PATTERN, value) is not None ) +class Selector(NumberAsHex): + """ + Field used to serialize and deserialize selector type. + """ + + MAX_VALUE = 2**32 + REGEX_PATTERN = r"^0x(0|[a-fA-F1-9]{1}[a-fA-F0-9]{0,7})$" + class Felt(NumberAsHex): """ @@ -352,7 +360,6 @@ class Revision(Enum): """ Enum representing the revision of the specification to be used. """ - V0 = 0 V1 = 1 @@ -361,12 +368,15 @@ class RevisionField(fields.Field): def _serialize(self, value: Any, attr: Optional[str], obj: Any, **kwargs): if value is None or value == Revision.V0: return str(Revision.V0.value) - return value.value + return str(value.value) def _deserialize(self, value, attr, data, **kwargs) -> Revision: if isinstance(value, str): value = int(value) + if isinstance(value, Revision): + value = value.value + revisions = [revision.value for revision in Revision] if value not in revisions: allowed_revisions_str = "".join(list(map(str, revisions))) diff --git a/starknet_py/net/schemas/rpc/transactions.py b/starknet_py/net/schemas/rpc/transactions.py index f96cc82d2..3a8352a7a 100644 --- a/starknet_py/net/schemas/rpc/transactions.py +++ b/starknet_py/net/schemas/rpc/transactions.py @@ -295,7 +295,7 @@ def get_obj_type(self, obj): def get_data_type(self, data): return _extract_tx_version(data.get("version")) - + class L1HandlerTransactionSchema(TransactionSchema): contract_address = Felt(data_key="contract_address", required=True) diff --git a/starknet_py/serialization/data_serializers/__init__.py b/starknet_py/serialization/data_serializers/__init__.py index f8a711184..3d67c45fe 100644 --- a/starknet_py/serialization/data_serializers/__init__.py +++ b/starknet_py/serialization/data_serializers/__init__.py @@ -8,3 +8,4 @@ from .struct_serializer import StructSerializer from .tuple_serializer import TupleSerializer from .uint256_serializer import Uint256Serializer +from .uint_serializer import UintSerializer diff --git a/starknet_py/tests/e2e/account/account_test.py b/starknet_py/tests/e2e/account/account_test.py index 54a249841..fbe577b6e 100644 --- a/starknet_py/tests/e2e/account/account_test.py +++ b/starknet_py/tests/e2e/account/account_test.py @@ -1,9 +1,11 @@ +import datetime import sys from typing import cast from unittest.mock import AsyncMock, patch import pytest +from starknet_py.constants import SNIP9InterfaceVersion, ANY_CALLER 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 @@ -19,6 +21,7 @@ PriceUnit, ResourceBounds, ResourceBoundsMapping, + ExecutionTimeBounds, SierraContractClass, TransactionExecutionStatus, TransactionFinalityStatus, @@ -38,6 +41,9 @@ MAX_RESOURCE_BOUNDS, MAX_RESOURCE_BOUNDS_L1, ) +from starknet_py.transaction_errors import ( + TransactionRevertedError, +) @pytest.mark.run_on_devnet @@ -810,6 +816,15 @@ async def test_argent_account_execute( assert get_balance[0] == value +@pytest.mark.asyncio +async def test_argent_account_snip9_compatibility( + argent_account: BaseAccount, +): + result = await argent_account.supports_interface(SNIP9InterfaceVersion.V1) + assert result is True + result = await argent_account.supports_interface(SNIP9InterfaceVersion.V2) + assert result is False + @pytest.mark.asyncio async def test_account_execute_v3(account, deployed_balance_contract): get_balance_call = Call( @@ -843,3 +858,108 @@ async def test_account_execute_v3(account, deployed_balance_contract): call=get_balance_call ) assert initial_balance + 100 == balance_after_increase + +@pytest.mark.asyncio +async def test_account_outside_execution_any_caller( + client, + argent_account_class_hash, + deployed_balance_contract, + deploy_account_details_factory, +): + address, key_pair, salt, class_hash = await deploy_account_details_factory.get( + class_hash=argent_account_class_hash, argent_calldata=True + ) + + deploy_result = await Account.deploy_account_v1( + address=address, + class_hash=class_hash, + salt=salt, + key_pair=key_pair, + client=client, + constructor_calldata=[key_pair.public_key, 0], + max_fee=MAX_FEE, + ) + await deploy_result.wait_for_acceptance() + account = deploy_result.account + + assert any([ + await account.supports_interface(SNIP9InterfaceVersion.V1), + await account.supports_interface(SNIP9InterfaceVersion.V2), + ]) + + increase_balance_call = Call( + to_addr=deployed_balance_contract.address, + selector=get_selector_from_name("increase_balance"), + calldata=[100], + ) + + call = await account.sign_outside_execution_call( + calls=[ + increase_balance_call, + increase_balance_call, + increase_balance_call, + ], + execution_time_bounds=ExecutionTimeBounds( + execute_after=datetime.datetime.now() - datetime.timedelta(hours=1), + execute_before=datetime.datetime.now() + datetime.timedelta(hours=1), + ), + caller=ANY_CALLER, + ) + + tx = await account.execute_v1(calls=[call], max_fee=MAX_FEE) + await account.client.wait_for_tx(tx.transaction_hash) + + +@pytest.mark.asyncio +async def test_account_outside_execution_for_invalid_caller( + client, + argent_account_class_hash, + deployed_balance_contract, + deploy_account_details_factory, +): + address, key_pair, salt, class_hash = await deploy_account_details_factory.get( + class_hash=argent_account_class_hash, argent_calldata=True + ) + + deploy_result = await Account.deploy_account_v1( + address=address, + class_hash=class_hash, + salt=salt, + key_pair=key_pair, + client=client, + constructor_calldata=[key_pair.public_key, 0], + max_fee=MAX_FEE, + ) + await deploy_result.wait_for_acceptance() + account = deploy_result.account + + assert any([ + await account.supports_interface(SNIP9InterfaceVersion.V1), + await account.supports_interface(SNIP9InterfaceVersion.V2), + ]) + + increase_balance_call = Call( + to_addr=deployed_balance_contract.address, + selector=get_selector_from_name("increase_balance"), + calldata=[100], + ) + + call = await account.sign_outside_execution_call( + calls=[ + increase_balance_call, + increase_balance_call, + increase_balance_call, + ], + execution_time_bounds=ExecutionTimeBounds( + execute_after=datetime.datetime.now() - datetime.timedelta(hours=1), + execute_before=datetime.datetime.now() + datetime.timedelta(hours=1), + ), + caller=deployed_balance_contract.address, + ) + + tx = await account.execute_v1(calls=[call], max_fee=MAX_FEE) + + with pytest.raises(TransactionRevertedError) as err: + await account.client.wait_for_tx(tx.transaction_hash) + + assert 'argent/invalid-caller' in err.value.message diff --git a/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py b/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py new file mode 100644 index 000000000..5f9d0eb25 --- /dev/null +++ b/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py @@ -0,0 +1,76 @@ + +import pytest + +from starknet_py.net.account.account import Account +from starknet_py.net.client_models import ( + TransactionFinalityStatus +) + +@pytest.mark.asyncio +async def test_account_outside_execution_any_caller( + client, + argent_account_class_hash, + deployed_balance_contract, + deploy_account_details_factory, +): + # pylint: disable=import-outside-toplevel,too-many-locals + address, key_pair, salt, class_hash = await deploy_account_details_factory.get( + class_hash=argent_account_class_hash, argent_calldata=True + ) + + deploy_result = await Account.deploy_account_v1( + address=address, + class_hash=class_hash, + salt=salt, + key_pair=key_pair, + client=client, + constructor_calldata=[key_pair.public_key, 0], + max_fee=int(1e18), + ) + await deploy_result.wait_for_acceptance() + account = deploy_result.account + + # docs: start + import datetime + + from starknet_py.constants import ANY_CALLER + from starknet_py.hash.selector import get_selector_from_name + from starknet_py.net.client_models import ( + Call, + ExecutionTimeBounds, + ) + + # Create a call to increase the balance by 100. That will be executed + # as part of external execution + + increase_balance_call = Call( + to_addr=deployed_balance_contract.address, + selector=get_selector_from_name("increase_balance"), + calldata=[100], + ) + + # Create a special signed execution call. This call now be executed by + # caller specified. In this case, it is ANY_CALLER, a special constant + # that allows any caller to execute the call. + call = await account.sign_outside_execution_call( + calls=[ + increase_balance_call, + ], + execution_time_bounds=ExecutionTimeBounds( + execute_after=datetime.datetime.now() - datetime.timedelta(hours=1), + execute_before=datetime.datetime.now() + datetime.timedelta(hours=1), + ), + caller=ANY_CALLER, + ) + + # Execute the call as a normal invoke transaction + tx = await account.execute_v1(calls=[call], max_fee=int(1e18)) + await account.client.wait_for_tx(tx.transaction_hash) + + # docs: end + + receipt = await account.client.get_transaction_receipt( + tx_hash=tx.transaction_hash + ) + + assert receipt.finality_status == TransactionFinalityStatus.ACCEPTED_ON_L2 diff --git a/starknet_py/tests/unit/net/client_test.py b/starknet_py/tests/unit/net/client_test.py index e35d4152a..b09407b76 100644 --- a/starknet_py/tests/unit/net/client_test.py +++ b/starknet_py/tests/unit/net/client_test.py @@ -12,8 +12,7 @@ TransactionType, TransactionV3, ) -from starknet_py.net.client_utils import _create_broadcasted_txn -from starknet_py.net.full_node_client import _to_storage_key +from starknet_py.net.full_node_client import _to_storage_key, _create_broadcasted_txn from starknet_py.net.http_client import RpcHttpClient, ServerError from starknet_py.net.models.transaction import ( DeclareV2, diff --git a/starknet_py/tests/unit/net/models/transaction_test.py b/starknet_py/tests/unit/net/models/transaction_test.py new file mode 100644 index 000000000..1a5e5bbb3 --- /dev/null +++ b/starknet_py/tests/unit/net/models/transaction_test.py @@ -0,0 +1,35 @@ +import datetime + +from starknet_py.hash.outside_execution import outside_execution_to_typed_data +from starknet_py.net.client_models import OutsideExecution, Call +from starknet_py.net.models import StarknetChainId + +from starknet_py.constants import SNIP9InterfaceVersion + + +def test_generate_message_hash_for_execute_outside_transaction(): + now = datetime.datetime(2024, 4, 12, 0, 0, 0) + execute_after = now - datetime.timedelta(days=1) + execute_before = now - datetime.timedelta(days=1) + + execution = OutsideExecution( + caller=0x00000000000000000000000000000000011234567, + nonce=0x00000000000000000000000000000000011234567, + execute_after=int(execute_after.timestamp()), + execute_before=int(execute_before.timestamp()), + calls=[ + Call( + to_addr=0x00000000012736721676273672, + selector=0x72832873827382738273827, + calldata=[] + ) + ] + ) + + message_hash = outside_execution_to_typed_data( + execution, + SNIP9InterfaceVersion.V1, + StarknetChainId.SEPOLIA + ).message_hash(0x00000000001) + + assert message_hash == 0x54dbfd11fa2b470d9ae26560c0284aeba6545c2368c7f6d92c4c4052eb1acbc From 9137b40dc1cb9ddf87116fb70c15b0963ba17060 Mon Sep 17 00:00:00 2001 From: baitcode Date: Thu, 5 Dec 2024 05:02:02 +0300 Subject: [PATCH 03/51] * lint * test fixes --- .github/workflows/checks.yml | 4 +- .pylintrc | 2 +- README.md | 1 + docs/guide/account_and_client.rst | 2 +- starknet_py/constants.py | 7 +- starknet_py/hash/outside_execution.py | 175 ++++++++-------- starknet_py/net/account/account.py | 51 ++--- starknet_py/net/account/base_account.py | 12 +- starknet_py/net/client_models.py | 21 +- starknet_py/net/full_node_client.py | 7 +- starknet_py/net/schemas/common.py | 2 + starknet_py/net/schemas/rpc/transactions.py | 2 +- starknet_py/tests/e2e/account/account_test.py | 120 ----------- .../e2e/account/external_execution_test.py | 187 ++++++++++++++++++ .../test_account_sign_outside_transaction.py | 19 +- starknet_py/tests/unit/net/client_test.py | 2 +- .../tests/unit/net/models/transaction_test.py | 18 +- 17 files changed, 354 insertions(+), 278 deletions(-) create mode 100644 starknet_py/tests/e2e/account/external_execution_test.py diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 99d23a5b6..c86a2d222 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -200,7 +200,7 @@ jobs: cd /apps/app-starknet git checkout ${{ env.LEDGER_APP_SHA }} cargo clean - cargo ledger build nanox + cargo ledger build nanos - name: Start Speculos emulator container uses: addnab/docker-run-action@v3 @@ -213,7 +213,7 @@ jobs: --apdu-port 9999 \ --api-port 5000 \ --display headless \ - /apps/app-starknet/target/nanox/release/starknet + /apps/app-starknet/target/nanos/release/starknet - name: Wait for Speculos to start run: sleep 5 diff --git a/.pylintrc b/.pylintrc index 7f3c12362..75878ec2d 100644 --- a/.pylintrc +++ b/.pylintrc @@ -561,7 +561,7 @@ max-locals=15 max-parents=7 # Maximum number of public methods for a class (see R0904). -max-public-methods=23 +max-public-methods=20 # Maximum number of return / yield for function / method body. max-returns=6 diff --git a/README.md b/README.md index 5db31dd95..0bf226bdb 100644 --- a/README.md +++ b/README.md @@ -212,3 +212,4 @@ Contract automatically serializes values to Cairo calldata. This includes adding See more info in [Serialization](https://starknetpy.readthedocs.io/en/latest/guide/serialization.html#serialization). Quickstart in docs - click [here](https://starknetpy.rtfd.io/en/latest/quickstart.html). + diff --git a/docs/guide/account_and_client.rst b/docs/guide/account_and_client.rst index 38d9e4727..90916c513 100644 --- a/docs/guide/account_and_client.rst +++ b/docs/guide/account_and_client.rst @@ -47,7 +47,7 @@ Account also provides a way of creating signed transaction without sending them. :dedent: 4 Creating "Outside transaction" and executing it. `SNIP-9 `_ --------------------------------------------- +--------------------------------------------------------------------------------------------------------------------------- Account also provides a way of creating a call and signing to allow for another account (caller) to execute it later on original account behalf. This will also allow caller to execute calls encoded in that transaction for free (signer will pay the fee). diff --git a/starknet_py/constants.py b/starknet_py/constants.py index 25014fe70..efe09f707 100644 --- a/starknet_py/constants.py +++ b/starknet_py/constants.py @@ -48,9 +48,10 @@ VERSION_RESPONSE_LENGTH = 3 # SNIP-9 ANY_CALLER -ANY_CALLER = 0x414e595f43414c4c4552 +ANY_CALLER = 0x414E595F43414C4C4552 + # SNIP-9 INTERFACE_VERSION with ID class SNIP9InterfaceVersion(IntEnum): - V1 = 0x68cfd18b92d1907b8ba3cc324900277f5a3622099431ea85dd8089255e4181 - V2 = 0x1d1144bb2138366ff28d8e9ab57456b1d332ac42196230c3a602003c89872 + V1 = 0x68CFD18B92D1907B8BA3CC324900277F5A3622099431EA85DD8089255E4181 + V2 = 0x1D1144BB2138366FF28D8E9AB57456B1D332AC42196230C3A602003C89872 diff --git a/starknet_py/hash/outside_execution.py b/starknet_py/hash/outside_execution.py index 05ad21b20..47e481696 100644 --- a/starknet_py/hash/outside_execution.py +++ b/starknet_py/hash/outside_execution.py @@ -1,7 +1,5 @@ -from starknet_py.net.client_models import OutsideExecution - from starknet_py.constants import SNIP9InterfaceVersion - +from starknet_py.net.client_models import OutsideExecution from starknet_py.net.schemas.common import Revision from starknet_py.utils import typed_data as td @@ -10,10 +8,11 @@ SNIP9InterfaceVersion.V2: Revision.V1, } + def outside_execution_to_typed_data( outside_execution: OutsideExecution, snip9_version: SNIP9InterfaceVersion, - chain_id: int + chain_id: int, ) -> td.TypedData: """ SNIP-12 Typed Data for OutsideExecution implementation. For revision V0 and V1. @@ -22,92 +21,98 @@ def outside_execution_to_typed_data( revision = SNIP9_INTERFACE_ID_TO_SNIP12_REVISION[snip9_version] if revision == Revision.V0: - return td.TypedData.from_dict({ - 'types': { - 'StarkNetDomain': [ - {'name': 'name', 'type': 'felt'}, - {'name': 'version', 'type': 'felt'}, - {'name': 'chainId', 'type': 'felt'}, + return td.TypedData.from_dict( + { + "types": { + "StarkNetDomain": [ + {"name": "name", "type": "felt"}, + {"name": "version", "type": "felt"}, + {"name": "chainId", "type": "felt"}, + ], + "OutsideExecution": [ + {"name": "caller", "type": "felt"}, + {"name": "nonce", "type": "felt"}, + {"name": "execute_after", "type": "felt"}, + {"name": "execute_before", "type": "felt"}, + {"name": "calls_len", "type": "felt"}, + {"name": "calls", "type": "OutsideCall*"}, + ], + "OutsideCall": [ + {"name": "to", "type": "felt"}, + {"name": "selector", "type": "felt"}, + {"name": "calldata_len", "type": "felt"}, + {"name": "calldata", "type": "felt*"}, + ], + }, + "primaryType": "OutsideExecution", + "domain": { + "name": "Account.execute_from_outside", + "version": "1", + "chainId": str(chain_id), + "revision": Revision.V0, + }, + "message": { + "caller": outside_execution.caller, + "nonce": outside_execution.nonce, + "execute_after": outside_execution.execute_after, + "execute_before": outside_execution.execute_before, + "calls_len": len(outside_execution.calls), + "calls": [ + { + "to": call.to_addr, + "selector": call.selector, + "calldata_len": len(call.calldata), + "calldata": call.calldata, + } + for call in outside_execution.calls + ], + }, + } + ) + + # revision == Revision.V1 + return td.TypedData.from_dict( + { + "types": { + "StarknetDomain": [ + {"name": "name", "type": "shortstring"}, + {"name": "version", "type": "shortstring"}, + {"name": "chainId", "type": "shortstring"}, + {"name": "revision", "type": "shortstring"}, ], - 'OutsideExecution': [ - {'name': 'caller', 'type': 'felt' }, - {'name': 'nonce', 'type': 'felt' }, - {'name': 'execute_after', 'type': 'felt' }, - {'name': 'execute_before', 'type': 'felt' }, - {'name': 'calls_len', 'type': 'felt' }, - {'name': 'calls', 'type': 'OutsideCall*' }, + "OutsideExecution": [ + {"name": "Caller", "type": "ContractAddress"}, + {"name": "Nonce", "type": "felt"}, + {"name": "Execute After", "type": "u128"}, + {"name": "Execute Before", "type": "u128"}, + {"name": "Calls", "type": "Call*"}, ], - 'OutsideCall': [ - { 'name': 'to', 'type': 'felt' }, - { 'name': 'selector', 'type': 'felt' }, - { 'name': 'calldata_len', 'type': 'felt' }, - { 'name': 'calldata', 'type': 'felt*' }, + "Call": [ + {"name": "To", "type": "ContractAddress"}, + {"name": "Selector", "type": "selector"}, + {"name": "Calldata", "type": "felt*"}, ], }, - 'primaryType': 'OutsideExecution', - 'domain': { - 'name': 'Account.execute_from_outside', - 'version': '1', - 'chainId': str(chain_id), - 'revision': Revision.V0, + "primaryType": "OutsideExecution", + "domain": { + "name": "Account.execute_from_outside", + "version": "2", + "chainId": str(chain_id), + "revision": Revision.V1, }, - 'message': { - 'caller': outside_execution.caller, - 'nonce': outside_execution.nonce, - 'execute_after': outside_execution.execute_after, - 'execute_before': outside_execution.execute_before, - 'calls_len': len(outside_execution.calls), - 'calls': [ + "message": { + "Caller": outside_execution.caller, + "Nonce": outside_execution.nonce, + "Execute After": outside_execution.execute_after, + "Execute Before": outside_execution.execute_before, + "Calls": [ { - 'to': call.to_addr, - 'selector': call.selector, - 'calldata_len': len(call.calldata), - 'calldata': call.calldata, - } for call in outside_execution.calls + "To": call.to_addr, + "Selector": call.selector, + "Calldata": call.calldata, + } + for call in outside_execution.calls ], }, - }) - - # revision == Revision.V1 - return td.TypedData.from_dict({ - 'types': { - 'StarknetDomain': [ - {'name': 'name', 'type': 'shortstring'}, - {'name': 'version', 'type': 'shortstring'}, - {'name': 'chainId', 'type': 'shortstring'}, - {'name': 'revision', 'type': 'shortstring'}, - ], - 'OutsideExecution': [ - {'name': 'Caller', 'type': 'ContractAddress' }, - {'name': 'Nonce', 'type': 'felt' }, - {'name': 'Execute After', 'type': 'u128' }, - {'name': 'Execute Before', 'type': 'u128' }, - {'name': 'Calls', 'type': 'Call*' }, - ], - 'Call': [ - { 'name': 'To', 'type': 'ContractAddress' }, - { 'name': 'Selector', 'type': 'selector' }, - { 'name': 'Calldata', 'type': 'felt*' }, - ], - }, - 'primaryType': 'OutsideExecution', - 'domain': { - 'name': 'Account.execute_from_outside', - 'version': '2', - 'chainId': str(chain_id), - 'revision': Revision.V1, - }, - 'message': { - 'Caller': outside_execution.caller, - 'Nonce': outside_execution.nonce, - 'Execute After': outside_execution.execute_after, - 'Execute Before': outside_execution.execute_before, - 'Calls': [ - { - 'To': call.to_addr, - 'Selector': call.selector, - 'Calldata': call.calldata, - } for call in outside_execution.calls - ], - }, - }) + } + ) diff --git a/starknet_py/net/account/account.py b/starknet_py/net/account/account.py index 3714c646f..b930ce6b3 100644 --- a/starknet_py/net/account/account.py +++ b/starknet_py/net/account/account.py @@ -4,26 +4,30 @@ from typing import Any, Dict, Iterable, List, Optional, Tuple, Union from starknet_py.common import create_compiled_contract, create_sierra_compiled_contract -from starknet_py.constants import FEE_CONTRACT_ADDRESS, QUERY_VERSION_BASE, ANY_CALLER +from starknet_py.constants import ( + ANY_CALLER, + FEE_CONTRACT_ADDRESS, + QUERY_VERSION_BASE, + SNIP9InterfaceVersion, +) from starknet_py.hash.address import compute_address +from starknet_py.hash.outside_execution import outside_execution_to_typed_data from starknet_py.hash.selector import get_selector_from_name from starknet_py.hash.utils import verify_message_signature -from starknet_py.hash.outside_execution import outside_execution_to_typed_data from starknet_py.net.account.account_deployment_result import AccountDeploymentResult -from starknet_py.net.account.base_account import BaseAccount, SNIP9SupportMixin +from starknet_py.net.account.base_account import BaseAccount, SNIP9SupportBaseMixin from starknet_py.net.client import Client -from starknet_py.constants import SNIP9InterfaceVersion from starknet_py.net.client_models import ( Call, Calls, EstimatedFee, + ExecutionTimeBounds, Hash, + OutsideExecution, ResourceBounds, - ExecutionTimeBounds, ResourceBoundsMapping, SentTransactionResponse, SierraContractClass, - OutsideExecution, Tag, ) from starknet_py.net.full_node_client import FullNodeClient @@ -55,8 +59,9 @@ from starknet_py.utils.typed_data import TypedData +# pylint: disable=too-many-public-methods @add_sync_methods -class Account(BaseAccount, SNIP9SupportMixin): +class Account(BaseAccount, SNIP9SupportBaseMixin): """ Default Account implementation. """ @@ -216,12 +221,9 @@ async def _prepare_invoke( nonce=nonce, sender_address=self.address, ) - max_fee = await self._get_max_fee(transaction, max_fee, auto_estimate) - return _add_max_fee_to_transaction(transaction, max_fee) - async def _prepare_invoke_v3( self, calls: Calls, @@ -301,13 +303,14 @@ async def _check_snip9_nonce( block_hash: Optional[Union[Hash, Tag]] = None, block_number: Optional[Union[int, Tag]] = None, ) -> bool: - (is_valid, ) = await self._client.call_contract( + (is_valid,) = await self._client.call_contract( call=Call( to_addr=self.address, selector=get_selector_from_name("is_valid_outside_execution_nonce"), calldata=[nonce], ), - block_hash=block_hash, block_number=block_number + block_hash=block_hash, + block_number=block_number, ) return bool(is_valid) @@ -402,7 +405,9 @@ async def sign_outside_execution_call( version = await self._get_snip9_version() if version is None: - raise RuntimeError("Can't initiate outside execution SNIP-9 is unsupported.") + raise RuntimeError( + "Can't initiate outside execution SNIP-9 is unsupported." + ) if nonce is None: nonce = await self.get_snip9_nonce() @@ -416,23 +421,23 @@ async def sign_outside_execution_call( ) chain_id = await self._get_chain_id() signature = self.signer.sign_message( - outside_execution_to_typed_data( - outside_execution, version, chain_id - ), - self.address + outside_execution_to_typed_data(outside_execution, version, chain_id), + self.address, ) selector_for_version = { SNIP9InterfaceVersion.V1: "execute_from_outside", - SNIP9InterfaceVersion.V2: "execute_from_outside_v2" + SNIP9InterfaceVersion.V2: "execute_from_outside_v2", } return Call( to_addr=self.address, selector=get_selector_from_name(selector_for_version[version]), - calldata=_transaction_serialiser.serialize({ - "external_execution": outside_execution.to_abi_dict(), - "signature": signature - }) + calldata=_transaction_serialiser.serialize( + { + "external_execution": outside_execution.to_abi_dict(), + "signature": signature, + } + ), ) async def sign_invoke_v3( @@ -968,7 +973,6 @@ def _parse_calls_cairo_v1(calls: Iterable[Call]) -> List[Dict]: calldata=ArraySerializer(_felt_serializer), ) ) - _execute_payload_serializer_v0 = PayloadSerializer( OrderedDict( call_array=ArraySerializer(_call_description_cairo_v0), @@ -980,7 +984,6 @@ def _parse_calls_cairo_v1(calls: Iterable[Call]) -> List[Dict]: calls=ArraySerializer(_call_description_cairo_v1), ) ) - _transaction_serialiser = StructSerializer( OrderedDict( external_execution=StructSerializer( diff --git a/starknet_py/net/account/base_account.py b/starknet_py/net/account/base_account.py index 5eaf1f270..4142e7d72 100644 --- a/starknet_py/net/account/base_account.py +++ b/starknet_py/net/account/base_account.py @@ -1,17 +1,17 @@ from abc import ABC, abstractmethod from typing import List, Optional, Union -from starknet_py.constants import SNIP9InterfaceVersion, ANY_CALLER +from starknet_py.constants import ANY_CALLER, SNIP9InterfaceVersion from starknet_py.net.client import Client from starknet_py.net.client_models import ( + Call, Calls, EstimatedFee, + ExecutionTimeBounds, Hash, ResourceBounds, - ExecutionTimeBounds, SentTransactionResponse, Tag, - Call ) from starknet_py.net.models import AddressRepresentation from starknet_py.net.models.transaction import ( @@ -27,7 +27,8 @@ ) from starknet_py.net.models.typed_data import TypedDataDict -class SNIP9SupportMixin(ABC): + +class SNIP9SupportBaseMixin(ABC): @abstractmethod async def get_snip9_nonce(self) -> int: @@ -62,7 +63,8 @@ async def sign_outside_execution_call( supports and use the highest one and populate the value. """ -class BaseAccount(SNIP9SupportMixin, ABC): + +class BaseAccount(SNIP9SupportBaseMixin, ABC): """ Base class for all account implementations. diff --git a/starknet_py/net/client_models.py b/starknet_py/net/client_models.py index 2513b2e61..9ee436bf8 100644 --- a/starknet_py/net/client_models.py +++ b/starknet_py/net/client_models.py @@ -12,7 +12,7 @@ from abc import ABC from dataclasses import dataclass, field from enum import Enum -from typing import Any, Iterable, List, Literal, Optional, Union, cast, Dict +from typing import Any, Dict, Iterable, List, Literal, Optional, Union, cast from marshmallow import EXCLUDE @@ -27,10 +27,8 @@ ) from starknet_py.abi.v2.shape import AbiDictEntry as AbiDictEntryV2 from starknet_py.abi.v2.shape import AbiDictList as AbiDictListV2 - from starknet_py.utils.constructor_args_translator import _is_abi_v2 - # pylint: disable=too-many-lines @@ -118,6 +116,7 @@ class ResourceBounds: def init_with_zeros(): return ResourceBounds(max_amount=0, max_price_per_unit=0) + @dataclass class ExecutionTimeBounds: """ @@ -1128,10 +1127,11 @@ class BlockTransactionTrace: transaction_hash: int trace_root: TransactionTrace + @dataclass class OutsideExecution: """ - Dataclass representing an outside execution. + Dataclass representing an outside execution. (SNIP-9)[https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-9.md] """ @@ -1151,9 +1151,12 @@ def to_abi_dict(self) -> Dict: "nonce": self.nonce, "execute_after": self.execute_after, "execute_before": self.execute_before, - "calls": [{ - "to": call.to_addr, - "selector": call.selector, - "calldata": call.calldata - } for call in self.calls] + "calls": [ + { + "to": call.to_addr, + "selector": call.selector, + "calldata": call.calldata, + } + for call in self.calls + ], } diff --git a/starknet_py/net/full_node_client.py b/starknet_py/net/full_node_client.py index 19f49a4f6..36c1a4abd 100644 --- a/starknet_py/net/full_node_client.py +++ b/starknet_py/net/full_node_client.py @@ -1,4 +1,4 @@ -from typing import List, Optional, Tuple, Union, cast, Dict +from typing import Dict, List, Optional, Tuple, Union, cast import aiohttp @@ -49,6 +49,7 @@ DeployAccount, Invoke, ) +from starknet_py.net.schemas.broadcasted_txn import BroadcastedTransactionSchema from starknet_py.net.schemas.rpc.block import ( BlockHashAndNumberSchema, BlockStateUpdateSchema, @@ -80,9 +81,6 @@ TransactionStatusResponseSchema, TypesOfTransactionsSchema, ) -from starknet_py.net.schemas.broadcasted_txn import ( - BroadcastedTransactionSchema, -) from starknet_py.transaction_errors import TransactionNotReceivedError from starknet_py.utils.sync import add_sync_methods @@ -93,6 +91,7 @@ def _create_broadcasted_txn(transaction: AccountTransaction) -> dict: BroadcastedTransactionSchema().dump(obj=transaction), ) + @add_sync_methods class FullNodeClient(Client): # pylint: disable=too-many-public-methods diff --git a/starknet_py/net/schemas/common.py b/starknet_py/net/schemas/common.py index 322dd4331..5b3f6487f 100644 --- a/starknet_py/net/schemas/common.py +++ b/starknet_py/net/schemas/common.py @@ -79,6 +79,7 @@ def _is_str_and_valid_pattern(self, value: Any) -> bool: and re.fullmatch(self.REGEX_PATTERN, value) is not None ) + class Selector(NumberAsHex): """ Field used to serialize and deserialize selector type. @@ -360,6 +361,7 @@ class Revision(Enum): """ Enum representing the revision of the specification to be used. """ + V0 = 0 V1 = 1 diff --git a/starknet_py/net/schemas/rpc/transactions.py b/starknet_py/net/schemas/rpc/transactions.py index 3a8352a7a..f96cc82d2 100644 --- a/starknet_py/net/schemas/rpc/transactions.py +++ b/starknet_py/net/schemas/rpc/transactions.py @@ -295,7 +295,7 @@ def get_obj_type(self, obj): def get_data_type(self, data): return _extract_tx_version(data.get("version")) - + class L1HandlerTransactionSchema(TransactionSchema): contract_address = Felt(data_key="contract_address", required=True) diff --git a/starknet_py/tests/e2e/account/account_test.py b/starknet_py/tests/e2e/account/account_test.py index fbe577b6e..54a249841 100644 --- a/starknet_py/tests/e2e/account/account_test.py +++ b/starknet_py/tests/e2e/account/account_test.py @@ -1,11 +1,9 @@ -import datetime import sys from typing import cast from unittest.mock import AsyncMock, patch import pytest -from starknet_py.constants import SNIP9InterfaceVersion, ANY_CALLER 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,7 +19,6 @@ PriceUnit, ResourceBounds, ResourceBoundsMapping, - ExecutionTimeBounds, SierraContractClass, TransactionExecutionStatus, TransactionFinalityStatus, @@ -41,9 +38,6 @@ MAX_RESOURCE_BOUNDS, MAX_RESOURCE_BOUNDS_L1, ) -from starknet_py.transaction_errors import ( - TransactionRevertedError, -) @pytest.mark.run_on_devnet @@ -816,15 +810,6 @@ async def test_argent_account_execute( assert get_balance[0] == value -@pytest.mark.asyncio -async def test_argent_account_snip9_compatibility( - argent_account: BaseAccount, -): - result = await argent_account.supports_interface(SNIP9InterfaceVersion.V1) - assert result is True - result = await argent_account.supports_interface(SNIP9InterfaceVersion.V2) - assert result is False - @pytest.mark.asyncio async def test_account_execute_v3(account, deployed_balance_contract): get_balance_call = Call( @@ -858,108 +843,3 @@ async def test_account_execute_v3(account, deployed_balance_contract): call=get_balance_call ) assert initial_balance + 100 == balance_after_increase - -@pytest.mark.asyncio -async def test_account_outside_execution_any_caller( - client, - argent_account_class_hash, - deployed_balance_contract, - deploy_account_details_factory, -): - address, key_pair, salt, class_hash = await deploy_account_details_factory.get( - class_hash=argent_account_class_hash, argent_calldata=True - ) - - deploy_result = await Account.deploy_account_v1( - address=address, - class_hash=class_hash, - salt=salt, - key_pair=key_pair, - client=client, - constructor_calldata=[key_pair.public_key, 0], - max_fee=MAX_FEE, - ) - await deploy_result.wait_for_acceptance() - account = deploy_result.account - - assert any([ - await account.supports_interface(SNIP9InterfaceVersion.V1), - await account.supports_interface(SNIP9InterfaceVersion.V2), - ]) - - increase_balance_call = Call( - to_addr=deployed_balance_contract.address, - selector=get_selector_from_name("increase_balance"), - calldata=[100], - ) - - call = await account.sign_outside_execution_call( - calls=[ - increase_balance_call, - increase_balance_call, - increase_balance_call, - ], - execution_time_bounds=ExecutionTimeBounds( - execute_after=datetime.datetime.now() - datetime.timedelta(hours=1), - execute_before=datetime.datetime.now() + datetime.timedelta(hours=1), - ), - caller=ANY_CALLER, - ) - - tx = await account.execute_v1(calls=[call], max_fee=MAX_FEE) - await account.client.wait_for_tx(tx.transaction_hash) - - -@pytest.mark.asyncio -async def test_account_outside_execution_for_invalid_caller( - client, - argent_account_class_hash, - deployed_balance_contract, - deploy_account_details_factory, -): - address, key_pair, salt, class_hash = await deploy_account_details_factory.get( - class_hash=argent_account_class_hash, argent_calldata=True - ) - - deploy_result = await Account.deploy_account_v1( - address=address, - class_hash=class_hash, - salt=salt, - key_pair=key_pair, - client=client, - constructor_calldata=[key_pair.public_key, 0], - max_fee=MAX_FEE, - ) - await deploy_result.wait_for_acceptance() - account = deploy_result.account - - assert any([ - await account.supports_interface(SNIP9InterfaceVersion.V1), - await account.supports_interface(SNIP9InterfaceVersion.V2), - ]) - - increase_balance_call = Call( - to_addr=deployed_balance_contract.address, - selector=get_selector_from_name("increase_balance"), - calldata=[100], - ) - - call = await account.sign_outside_execution_call( - calls=[ - increase_balance_call, - increase_balance_call, - increase_balance_call, - ], - execution_time_bounds=ExecutionTimeBounds( - execute_after=datetime.datetime.now() - datetime.timedelta(hours=1), - execute_before=datetime.datetime.now() + datetime.timedelta(hours=1), - ), - caller=deployed_balance_contract.address, - ) - - tx = await account.execute_v1(calls=[call], max_fee=MAX_FEE) - - with pytest.raises(TransactionRevertedError) as err: - await account.client.wait_for_tx(tx.transaction_hash) - - assert 'argent/invalid-caller' in err.value.message diff --git a/starknet_py/tests/e2e/account/external_execution_test.py b/starknet_py/tests/e2e/account/external_execution_test.py new file mode 100644 index 000000000..7359cc2fc --- /dev/null +++ b/starknet_py/tests/e2e/account/external_execution_test.py @@ -0,0 +1,187 @@ +import datetime + +import pytest + +from starknet_py.constants import ANY_CALLER, SNIP9InterfaceVersion +from starknet_py.hash.selector import get_selector_from_name +from starknet_py.net.account.account import Account, BaseAccount +from starknet_py.net.client_models import Call, ExecutionTimeBounds +from starknet_py.tests.e2e.fixtures.constants import MAX_FEE +from starknet_py.transaction_errors import TransactionRevertedError + + +@pytest.mark.asyncio +async def test_argent_account_snip9_compatibility( + argent_account: BaseAccount, +): + result = await argent_account.supports_interface(SNIP9InterfaceVersion.V1) + assert result is True + result = await argent_account.supports_interface(SNIP9InterfaceVersion.V2) + assert result is False + + +@pytest.mark.asyncio +async def test_account_outside_execution_any_caller( + client, + argent_account_class_hash, + deployed_balance_contract, + deploy_account_details_factory, +): + address, key_pair, salt, class_hash = await deploy_account_details_factory.get( + class_hash=argent_account_class_hash, argent_calldata=True + ) + + deploy_result = await Account.deploy_account_v1( + address=address, + class_hash=class_hash, + salt=salt, + key_pair=key_pair, + client=client, + constructor_calldata=[key_pair.public_key, 0], + max_fee=MAX_FEE, + ) + await deploy_result.wait_for_acceptance() + account = deploy_result.account + + assert any( + [ + await account.supports_interface(SNIP9InterfaceVersion.V1), + await account.supports_interface(SNIP9InterfaceVersion.V2), + ] + ) + + increase_balance_call = Call( + to_addr=deployed_balance_contract.address, + selector=get_selector_from_name("increase_balance"), + calldata=[100], + ) + + call = await account.sign_outside_execution_call( + calls=[ + increase_balance_call, + increase_balance_call, + increase_balance_call, + ], + execution_time_bounds=ExecutionTimeBounds( + execute_after=datetime.datetime.now() - datetime.timedelta(hours=1), + execute_before=datetime.datetime.now() + datetime.timedelta(hours=1), + ), + caller=ANY_CALLER, + ) + + tx = await account.execute_v1(calls=[call], max_fee=MAX_FEE) + await account.client.wait_for_tx(tx.transaction_hash) + + +@pytest.mark.asyncio +async def test_account_outside_execution_for_invalid_caller( + client, + argent_account_class_hash, + deployed_balance_contract, + deploy_account_details_factory, +): + address, key_pair, salt, class_hash = await deploy_account_details_factory.get( + class_hash=argent_account_class_hash, argent_calldata=True + ) + + deploy_result = await Account.deploy_account_v1( + address=address, + class_hash=class_hash, + salt=salt, + key_pair=key_pair, + client=client, + constructor_calldata=[key_pair.public_key, 0], + max_fee=MAX_FEE, + ) + await deploy_result.wait_for_acceptance() + account = deploy_result.account + + assert any( + [ + await account.supports_interface(SNIP9InterfaceVersion.V1), + await account.supports_interface(SNIP9InterfaceVersion.V2), + ] + ) + + increase_balance_call = Call( + to_addr=deployed_balance_contract.address, + selector=get_selector_from_name("increase_balance"), + calldata=[100], + ) + + call = await account.sign_outside_execution_call( + calls=[ + increase_balance_call, + increase_balance_call, + increase_balance_call, + ], + execution_time_bounds=ExecutionTimeBounds( + execute_after=datetime.datetime.now() - datetime.timedelta(hours=1), + execute_before=datetime.datetime.now() + datetime.timedelta(hours=1), + ), + caller=deployed_balance_contract.address, + ) + + tx = await account.execute_v1(calls=[call], max_fee=MAX_FEE) + + with pytest.raises(TransactionRevertedError) as err: + await account.client.wait_for_tx(tx.transaction_hash) + + assert "argent/invalid-caller" in err.value.message + + +@pytest.mark.asyncio +async def test_account_outside_execution_for_impossible_timebounds( + client, + argent_account_class_hash, + deployed_balance_contract, + deploy_account_details_factory, +): + address, key_pair, salt, class_hash = await deploy_account_details_factory.get( + class_hash=argent_account_class_hash, argent_calldata=True + ) + + deploy_result = await Account.deploy_account_v1( + address=address, + class_hash=class_hash, + salt=salt, + key_pair=key_pair, + client=client, + constructor_calldata=[key_pair.public_key, 0], + max_fee=MAX_FEE, + ) + await deploy_result.wait_for_acceptance() + account = deploy_result.account + + assert any( + [ + await account.supports_interface(SNIP9InterfaceVersion.V1), + await account.supports_interface(SNIP9InterfaceVersion.V2), + ] + ) + + increase_balance_call = Call( + to_addr=deployed_balance_contract.address, + selector=get_selector_from_name("increase_balance"), + calldata=[100], + ) + + call = await account.sign_outside_execution_call( + calls=[ + increase_balance_call, + increase_balance_call, + increase_balance_call, + ], + execution_time_bounds=ExecutionTimeBounds( + execute_after=datetime.datetime.now() - datetime.timedelta(days=10), + execute_before=datetime.datetime.now() - datetime.timedelta(days=9), + ), + caller=ANY_CALLER, + ) + + tx = await account.execute_v1(calls=[call], max_fee=MAX_FEE) + + with pytest.raises(TransactionRevertedError) as err: + await account.client.wait_for_tx(tx.transaction_hash) + + assert "argent/invalid-timestamp" in err.value.message diff --git a/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py b/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py index 5f9d0eb25..7f3c5edaf 100644 --- a/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py +++ b/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py @@ -1,10 +1,8 @@ - import pytest from starknet_py.net.account.account import Account -from starknet_py.net.client_models import ( - TransactionFinalityStatus -) +from starknet_py.net.client_models import TransactionFinalityStatus + @pytest.mark.asyncio async def test_account_outside_execution_any_caller( @@ -35,10 +33,7 @@ async def test_account_outside_execution_any_caller( from starknet_py.constants import ANY_CALLER from starknet_py.hash.selector import get_selector_from_name - from starknet_py.net.client_models import ( - Call, - ExecutionTimeBounds, - ) + from starknet_py.net.client_models import Call, ExecutionTimeBounds # Create a call to increase the balance by 100. That will be executed # as part of external execution @@ -49,8 +44,8 @@ async def test_account_outside_execution_any_caller( calldata=[100], ) - # Create a special signed execution call. This call now be executed by - # caller specified. In this case, it is ANY_CALLER, a special constant + # Create a special signed execution call. This call can now be executed by + # the caller specified. In this case, caller is ANY_CALLER, a special constant # that allows any caller to execute the call. call = await account.sign_outside_execution_call( calls=[ @@ -69,8 +64,6 @@ async def test_account_outside_execution_any_caller( # docs: end - receipt = await account.client.get_transaction_receipt( - tx_hash=tx.transaction_hash - ) + receipt = await account.client.get_transaction_receipt(tx_hash=tx.transaction_hash) assert receipt.finality_status == TransactionFinalityStatus.ACCEPTED_ON_L2 diff --git a/starknet_py/tests/unit/net/client_test.py b/starknet_py/tests/unit/net/client_test.py index b09407b76..ae9fb87a3 100644 --- a/starknet_py/tests/unit/net/client_test.py +++ b/starknet_py/tests/unit/net/client_test.py @@ -12,7 +12,7 @@ TransactionType, TransactionV3, ) -from starknet_py.net.full_node_client import _to_storage_key, _create_broadcasted_txn +from starknet_py.net.full_node_client import _create_broadcasted_txn, _to_storage_key from starknet_py.net.http_client import RpcHttpClient, ServerError from starknet_py.net.models.transaction import ( DeclareV2, diff --git a/starknet_py/tests/unit/net/models/transaction_test.py b/starknet_py/tests/unit/net/models/transaction_test.py index 1a5e5bbb3..054d9de99 100644 --- a/starknet_py/tests/unit/net/models/transaction_test.py +++ b/starknet_py/tests/unit/net/models/transaction_test.py @@ -1,11 +1,10 @@ import datetime +from starknet_py.constants import SNIP9InterfaceVersion from starknet_py.hash.outside_execution import outside_execution_to_typed_data -from starknet_py.net.client_models import OutsideExecution, Call +from starknet_py.net.client_models import Call, OutsideExecution from starknet_py.net.models import StarknetChainId -from starknet_py.constants import SNIP9InterfaceVersion - def test_generate_message_hash_for_execute_outside_transaction(): now = datetime.datetime(2024, 4, 12, 0, 0, 0) @@ -21,15 +20,16 @@ def test_generate_message_hash_for_execute_outside_transaction(): Call( to_addr=0x00000000012736721676273672, selector=0x72832873827382738273827, - calldata=[] + calldata=[], ) - ] + ], ) message_hash = outside_execution_to_typed_data( - execution, - SNIP9InterfaceVersion.V1, - StarknetChainId.SEPOLIA + execution, SNIP9InterfaceVersion.V1, StarknetChainId.SEPOLIA ).message_hash(0x00000000001) - assert message_hash == 0x54dbfd11fa2b470d9ae26560c0284aeba6545c2368c7f6d92c4c4052eb1acbc + assert ( + message_hash + == 0x54DBFD11FA2B470D9AE26560C0284AEBA6545C2368C7F6D92C4C4052EB1ACBC + ) From ac7b2cc9da4a02dc0c0337f1466b397efac1a0d4 Mon Sep 17 00:00:00 2001 From: baitcode Date: Mon, 9 Dec 2024 01:37:25 +0300 Subject: [PATCH 04/51] ok. so I am not allowed to change ci configs --- .github/workflows/checks.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index c86a2d222..99d23a5b6 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -200,7 +200,7 @@ jobs: cd /apps/app-starknet git checkout ${{ env.LEDGER_APP_SHA }} cargo clean - cargo ledger build nanos + cargo ledger build nanox - name: Start Speculos emulator container uses: addnab/docker-run-action@v3 @@ -213,7 +213,7 @@ jobs: --apdu-port 9999 \ --api-port 5000 \ --display headless \ - /apps/app-starknet/target/nanos/release/starknet + /apps/app-starknet/target/nanox/release/starknet - name: Wait for Speculos to start run: sleep 5 From dcb044b0d01a8d6eddec9b27adfa32c11af7248c Mon Sep 17 00:00:00 2001 From: baitcode Date: Tue, 10 Dec 2024 18:54:28 +0300 Subject: [PATCH 05/51] fixing tests --- .../docs/guide/test_account_sign_outside_transaction.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py b/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py index 7f3c5edaf..9eb9d55f2 100644 --- a/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py +++ b/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py @@ -38,10 +38,10 @@ async def test_account_outside_execution_any_caller( # Create a call to increase the balance by 100. That will be executed # as part of external execution - increase_balance_call = Call( + store_something_call = Call( to_addr=deployed_balance_contract.address, - selector=get_selector_from_name("increase_balance"), - calldata=[100], + selector=get_selector_from_name("put"), + calldata=[20, 20], ) # Create a special signed execution call. This call can now be executed by @@ -49,7 +49,7 @@ async def test_account_outside_execution_any_caller( # that allows any caller to execute the call. call = await account.sign_outside_execution_call( calls=[ - increase_balance_call, + store_something_call, ], execution_time_bounds=ExecutionTimeBounds( execute_after=datetime.datetime.now() - datetime.timedelta(hours=1), From 0487bd6efa240e637f99fc380256f7f1f00ee6dc Mon Sep 17 00:00:00 2001 From: baitcode Date: Tue, 10 Dec 2024 19:01:59 +0300 Subject: [PATCH 06/51] lint --- .../e2e/docs/guide/test_account_sign_outside_transaction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py b/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py index 9eb9d55f2..7e835e048 100644 --- a/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py +++ b/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py @@ -40,7 +40,7 @@ async def test_account_outside_execution_any_caller( store_something_call = Call( to_addr=deployed_balance_contract.address, - selector=get_selector_from_name("put"), + selector=get_selector_from_name("put"), calldata=[20, 20], ) From a79ab755915867bd3eb2a04b6dfd24b29d98f9e9 Mon Sep 17 00:00:00 2001 From: baitcode Date: Tue, 10 Dec 2024 19:09:12 +0300 Subject: [PATCH 07/51] switched back to increase balance --- .../docs/guide/test_account_sign_outside_transaction.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py b/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py index 7e835e048..b132acfad 100644 --- a/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py +++ b/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py @@ -38,10 +38,10 @@ async def test_account_outside_execution_any_caller( # Create a call to increase the balance by 100. That will be executed # as part of external execution - store_something_call = Call( + increase_balance_call = Call( to_addr=deployed_balance_contract.address, - selector=get_selector_from_name("put"), - calldata=[20, 20], + selector=get_selector_from_name("increase_balance"), + calldata=[0], ) # Create a special signed execution call. This call can now be executed by @@ -49,7 +49,7 @@ async def test_account_outside_execution_any_caller( # that allows any caller to execute the call. call = await account.sign_outside_execution_call( calls=[ - store_something_call, + increase_balance_call, ], execution_time_bounds=ExecutionTimeBounds( execute_after=datetime.datetime.now() - datetime.timedelta(hours=1), From aceded7273b84fb2f7e0f327d6993545bfd3beac Mon Sep 17 00:00:00 2001 From: baitcode Date: Tue, 10 Dec 2024 19:16:40 +0300 Subject: [PATCH 08/51] tiny revert --- starknet_py/net/schemas/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/starknet_py/net/schemas/common.py b/starknet_py/net/schemas/common.py index 5b3f6487f..e2f3c5c4e 100644 --- a/starknet_py/net/schemas/common.py +++ b/starknet_py/net/schemas/common.py @@ -370,7 +370,7 @@ class RevisionField(fields.Field): def _serialize(self, value: Any, attr: Optional[str], obj: Any, **kwargs): if value is None or value == Revision.V0: return str(Revision.V0.value) - return str(value.value) + return value.value def _deserialize(self, value, attr, data, **kwargs) -> Revision: if isinstance(value, str): From 5851f12db6871627a00f24ab6df5e8e8419a8395 Mon Sep 17 00:00:00 2001 From: baitcode Date: Tue, 10 Dec 2024 19:28:05 +0300 Subject: [PATCH 09/51] comment documentation test to check if that is what affects test account balance --- .../test_account_sign_outside_transaction.py | 112 +++++++++--------- 1 file changed, 56 insertions(+), 56 deletions(-) diff --git a/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py b/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py index b132acfad..396c9db1d 100644 --- a/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py +++ b/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py @@ -1,69 +1,69 @@ -import pytest +# import pytest -from starknet_py.net.account.account import Account -from starknet_py.net.client_models import TransactionFinalityStatus +# from starknet_py.net.account.account import Account +# from starknet_py.net.client_models import TransactionFinalityStatus -@pytest.mark.asyncio -async def test_account_outside_execution_any_caller( - client, - argent_account_class_hash, - deployed_balance_contract, - deploy_account_details_factory, -): - # pylint: disable=import-outside-toplevel,too-many-locals - address, key_pair, salt, class_hash = await deploy_account_details_factory.get( - class_hash=argent_account_class_hash, argent_calldata=True - ) +# @pytest.mark.asyncio +# async def test_account_outside_execution_any_caller( +# client, +# argent_account_class_hash, +# deployed_balance_contract, +# deploy_account_details_factory, +# ): +# # pylint: disable=import-outside-toplevel,too-many-locals +# address, key_pair, salt, class_hash = await deploy_account_details_factory.get( +# class_hash=argent_account_class_hash, argent_calldata=True +# ) - deploy_result = await Account.deploy_account_v1( - address=address, - class_hash=class_hash, - salt=salt, - key_pair=key_pair, - client=client, - constructor_calldata=[key_pair.public_key, 0], - max_fee=int(1e18), - ) - await deploy_result.wait_for_acceptance() - account = deploy_result.account +# deploy_result = await Account.deploy_account_v1( +# address=address, +# class_hash=class_hash, +# salt=salt, +# key_pair=key_pair, +# client=client, +# constructor_calldata=[key_pair.public_key, 0], +# max_fee=int(1e18), +# ) +# await deploy_result.wait_for_acceptance() +# account = deploy_result.account - # docs: start - import datetime +# # docs: start +# import datetime - from starknet_py.constants import ANY_CALLER - from starknet_py.hash.selector import get_selector_from_name - from starknet_py.net.client_models import Call, ExecutionTimeBounds +# from starknet_py.constants import ANY_CALLER +# from starknet_py.hash.selector import get_selector_from_name +# from starknet_py.net.client_models import Call, ExecutionTimeBounds - # Create a call to increase the balance by 100. That will be executed - # as part of external execution +# # Create a call to increase the balance by 100. That will be executed +# # as part of external execution - increase_balance_call = Call( - to_addr=deployed_balance_contract.address, - selector=get_selector_from_name("increase_balance"), - calldata=[0], - ) +# increase_balance_call = Call( +# to_addr=deployed_balance_contract.address, +# selector=get_selector_from_name("increase_balance"), +# calldata=[0], +# ) - # Create a special signed execution call. This call can now be executed by - # the caller specified. In this case, caller is ANY_CALLER, a special constant - # that allows any caller to execute the call. - call = await account.sign_outside_execution_call( - calls=[ - increase_balance_call, - ], - execution_time_bounds=ExecutionTimeBounds( - execute_after=datetime.datetime.now() - datetime.timedelta(hours=1), - execute_before=datetime.datetime.now() + datetime.timedelta(hours=1), - ), - caller=ANY_CALLER, - ) +# # Create a special signed execution call. This call can now be executed by +# # the caller specified. In this case, caller is ANY_CALLER, a special constant +# # that allows any caller to execute the call. +# call = await account.sign_outside_execution_call( +# calls=[ +# increase_balance_call, +# ], +# execution_time_bounds=ExecutionTimeBounds( +# execute_after=datetime.datetime.now() - datetime.timedelta(hours=1), +# execute_before=datetime.datetime.now() + datetime.timedelta(hours=1), +# ), +# caller=ANY_CALLER, +# ) - # Execute the call as a normal invoke transaction - tx = await account.execute_v1(calls=[call], max_fee=int(1e18)) - await account.client.wait_for_tx(tx.transaction_hash) +# # Execute the call as a normal invoke transaction +# tx = await account.execute_v1(calls=[call], max_fee=int(1e18)) +# await account.client.wait_for_tx(tx.transaction_hash) - # docs: end +# # docs: end - receipt = await account.client.get_transaction_receipt(tx_hash=tx.transaction_hash) +# receipt = await account.client.get_transaction_receipt(tx_hash=tx.transaction_hash) - assert receipt.finality_status == TransactionFinalityStatus.ACCEPTED_ON_L2 +# assert receipt.finality_status == TransactionFinalityStatus.ACCEPTED_ON_L2 From e54e51079c46e0c91c4d86ff9e0f1b2cbefb918a Mon Sep 17 00:00:00 2001 From: baitcode Date: Tue, 10 Dec 2024 19:39:07 +0300 Subject: [PATCH 10/51] Revert "comment documentation test to check if that is what affects test account balance" This reverts commit 5851f12db6871627a00f24ab6df5e8e8419a8395. --- .../test_account_sign_outside_transaction.py | 112 +++++++++--------- 1 file changed, 56 insertions(+), 56 deletions(-) diff --git a/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py b/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py index 396c9db1d..b132acfad 100644 --- a/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py +++ b/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py @@ -1,69 +1,69 @@ -# import pytest +import pytest -# from starknet_py.net.account.account import Account -# from starknet_py.net.client_models import TransactionFinalityStatus +from starknet_py.net.account.account import Account +from starknet_py.net.client_models import TransactionFinalityStatus -# @pytest.mark.asyncio -# async def test_account_outside_execution_any_caller( -# client, -# argent_account_class_hash, -# deployed_balance_contract, -# deploy_account_details_factory, -# ): -# # pylint: disable=import-outside-toplevel,too-many-locals -# address, key_pair, salt, class_hash = await deploy_account_details_factory.get( -# class_hash=argent_account_class_hash, argent_calldata=True -# ) +@pytest.mark.asyncio +async def test_account_outside_execution_any_caller( + client, + argent_account_class_hash, + deployed_balance_contract, + deploy_account_details_factory, +): + # pylint: disable=import-outside-toplevel,too-many-locals + address, key_pair, salt, class_hash = await deploy_account_details_factory.get( + class_hash=argent_account_class_hash, argent_calldata=True + ) -# deploy_result = await Account.deploy_account_v1( -# address=address, -# class_hash=class_hash, -# salt=salt, -# key_pair=key_pair, -# client=client, -# constructor_calldata=[key_pair.public_key, 0], -# max_fee=int(1e18), -# ) -# await deploy_result.wait_for_acceptance() -# account = deploy_result.account + deploy_result = await Account.deploy_account_v1( + address=address, + class_hash=class_hash, + salt=salt, + key_pair=key_pair, + client=client, + constructor_calldata=[key_pair.public_key, 0], + max_fee=int(1e18), + ) + await deploy_result.wait_for_acceptance() + account = deploy_result.account -# # docs: start -# import datetime + # docs: start + import datetime -# from starknet_py.constants import ANY_CALLER -# from starknet_py.hash.selector import get_selector_from_name -# from starknet_py.net.client_models import Call, ExecutionTimeBounds + from starknet_py.constants import ANY_CALLER + from starknet_py.hash.selector import get_selector_from_name + from starknet_py.net.client_models import Call, ExecutionTimeBounds -# # Create a call to increase the balance by 100. That will be executed -# # as part of external execution + # Create a call to increase the balance by 100. That will be executed + # as part of external execution -# increase_balance_call = Call( -# to_addr=deployed_balance_contract.address, -# selector=get_selector_from_name("increase_balance"), -# calldata=[0], -# ) + increase_balance_call = Call( + to_addr=deployed_balance_contract.address, + selector=get_selector_from_name("increase_balance"), + calldata=[0], + ) -# # Create a special signed execution call. This call can now be executed by -# # the caller specified. In this case, caller is ANY_CALLER, a special constant -# # that allows any caller to execute the call. -# call = await account.sign_outside_execution_call( -# calls=[ -# increase_balance_call, -# ], -# execution_time_bounds=ExecutionTimeBounds( -# execute_after=datetime.datetime.now() - datetime.timedelta(hours=1), -# execute_before=datetime.datetime.now() + datetime.timedelta(hours=1), -# ), -# caller=ANY_CALLER, -# ) + # Create a special signed execution call. This call can now be executed by + # the caller specified. In this case, caller is ANY_CALLER, a special constant + # that allows any caller to execute the call. + call = await account.sign_outside_execution_call( + calls=[ + increase_balance_call, + ], + execution_time_bounds=ExecutionTimeBounds( + execute_after=datetime.datetime.now() - datetime.timedelta(hours=1), + execute_before=datetime.datetime.now() + datetime.timedelta(hours=1), + ), + caller=ANY_CALLER, + ) -# # Execute the call as a normal invoke transaction -# tx = await account.execute_v1(calls=[call], max_fee=int(1e18)) -# await account.client.wait_for_tx(tx.transaction_hash) + # Execute the call as a normal invoke transaction + tx = await account.execute_v1(calls=[call], max_fee=int(1e18)) + await account.client.wait_for_tx(tx.transaction_hash) -# # docs: end + # docs: end -# receipt = await account.client.get_transaction_receipt(tx_hash=tx.transaction_hash) + receipt = await account.client.get_transaction_receipt(tx_hash=tx.transaction_hash) -# assert receipt.finality_status == TransactionFinalityStatus.ACCEPTED_ON_L2 + assert receipt.finality_status == TransactionFinalityStatus.ACCEPTED_ON_L2 From 7ea876083eeed3a53c741ad914a3061f09a027f0 Mon Sep 17 00:00:00 2001 From: baitcode Date: Tue, 10 Dec 2024 19:42:44 +0300 Subject: [PATCH 11/51] change for another call in documentation. removed account deployment. --- .../test_account_sign_outside_transaction.py | 33 ++++++------------- 1 file changed, 10 insertions(+), 23 deletions(-) diff --git a/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py b/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py index b132acfad..fbda4c06a 100644 --- a/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py +++ b/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py @@ -1,32 +1,18 @@ import pytest -from starknet_py.net.account.account import Account from starknet_py.net.client_models import TransactionFinalityStatus @pytest.mark.asyncio async def test_account_outside_execution_any_caller( - client, - argent_account_class_hash, - deployed_balance_contract, - deploy_account_details_factory, + account, + map_contract + # client, + # argent_account_class_hash, + # deployed_balance_contract, + # deploy_account_details_factory, ): # pylint: disable=import-outside-toplevel,too-many-locals - address, key_pair, salt, class_hash = await deploy_account_details_factory.get( - class_hash=argent_account_class_hash, argent_calldata=True - ) - - deploy_result = await Account.deploy_account_v1( - address=address, - class_hash=class_hash, - salt=salt, - key_pair=key_pair, - client=client, - constructor_calldata=[key_pair.public_key, 0], - max_fee=int(1e18), - ) - await deploy_result.wait_for_acceptance() - account = deploy_result.account # docs: start import datetime @@ -39,9 +25,9 @@ async def test_account_outside_execution_any_caller( # as part of external execution increase_balance_call = Call( - to_addr=deployed_balance_contract.address, - selector=get_selector_from_name("increase_balance"), - calldata=[0], + to_addr=map_contract.address, + selector=get_selector_from_name("put"), + calldata=[20, 20], ) # Create a special signed execution call. This call can now be executed by @@ -59,6 +45,7 @@ async def test_account_outside_execution_any_caller( ) # Execute the call as a normal invoke transaction + # can be executed from any account specified in the caller field tx = await account.execute_v1(calls=[call], max_fee=int(1e18)) await account.client.wait_for_tx(tx.transaction_hash) From 14531b1a61372e0811944638132d865dc634f49c Mon Sep 17 00:00:00 2001 From: baitcode Date: Tue, 10 Dec 2024 19:46:59 +0300 Subject: [PATCH 12/51] fix --- .../e2e/docs/guide/test_account_sign_outside_transaction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py b/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py index fbda4c06a..e9aa14de6 100644 --- a/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py +++ b/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py @@ -6,7 +6,7 @@ @pytest.mark.asyncio async def test_account_outside_execution_any_caller( account, - map_contract + map_contract, # client, # argent_account_class_hash, # deployed_balance_contract, From 37f24df815061504b846cef1ef394ff00e275e57 Mon Sep 17 00:00:00 2001 From: baitcode Date: Tue, 10 Dec 2024 19:53:15 +0300 Subject: [PATCH 13/51] fix doctest --- .../docs/guide/test_account_sign_outside_transaction.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py b/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py index e9aa14de6..e9b290928 100644 --- a/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py +++ b/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py @@ -6,11 +6,8 @@ @pytest.mark.asyncio async def test_account_outside_execution_any_caller( account, + argent_account, map_contract, - # client, - # argent_account_class_hash, - # deployed_balance_contract, - # deploy_account_details_factory, ): # pylint: disable=import-outside-toplevel,too-many-locals @@ -33,7 +30,7 @@ async def test_account_outside_execution_any_caller( # Create a special signed execution call. This call can now be executed by # the caller specified. In this case, caller is ANY_CALLER, a special constant # that allows any caller to execute the call. - call = await account.sign_outside_execution_call( + call = await argent_account.sign_outside_execution_call( calls=[ increase_balance_call, ], From fb9829ea26ada3dc5dc4e718bbb73f1fb3116f2e Mon Sep 17 00:00:00 2001 From: baitcode Date: Tue, 10 Dec 2024 20:23:13 +0300 Subject: [PATCH 14/51] more fixes --- .../e2e/account/external_execution_test.py | 90 ++++--------------- 1 file changed, 19 insertions(+), 71 deletions(-) diff --git a/starknet_py/tests/e2e/account/external_execution_test.py b/starknet_py/tests/e2e/account/external_execution_test.py index 7359cc2fc..b8b0caa80 100644 --- a/starknet_py/tests/e2e/account/external_execution_test.py +++ b/starknet_py/tests/e2e/account/external_execution_test.py @@ -4,7 +4,7 @@ from starknet_py.constants import ANY_CALLER, SNIP9InterfaceVersion from starknet_py.hash.selector import get_selector_from_name -from starknet_py.net.account.account import Account, BaseAccount +from starknet_py.net.account.account import BaseAccount from starknet_py.net.client_models import Call, ExecutionTimeBounds from starknet_py.tests.e2e.fixtures.constants import MAX_FEE from starknet_py.transaction_errors import TransactionRevertedError @@ -22,31 +22,14 @@ async def test_argent_account_snip9_compatibility( @pytest.mark.asyncio async def test_account_outside_execution_any_caller( - client, - argent_account_class_hash, + argent_account: BaseAccount, deployed_balance_contract, - deploy_account_details_factory, ): - address, key_pair, salt, class_hash = await deploy_account_details_factory.get( - class_hash=argent_account_class_hash, argent_calldata=True - ) - - deploy_result = await Account.deploy_account_v1( - address=address, - class_hash=class_hash, - salt=salt, - key_pair=key_pair, - client=client, - constructor_calldata=[key_pair.public_key, 0], - max_fee=MAX_FEE, - ) - await deploy_result.wait_for_acceptance() - account = deploy_result.account assert any( [ - await account.supports_interface(SNIP9InterfaceVersion.V1), - await account.supports_interface(SNIP9InterfaceVersion.V2), + await argent_account.supports_interface(SNIP9InterfaceVersion.V1), + await argent_account.supports_interface(SNIP9InterfaceVersion.V2), ] ) @@ -56,7 +39,7 @@ async def test_account_outside_execution_any_caller( calldata=[100], ) - call = await account.sign_outside_execution_call( + call = await argent_account.sign_outside_execution_call( calls=[ increase_balance_call, increase_balance_call, @@ -69,37 +52,19 @@ async def test_account_outside_execution_any_caller( caller=ANY_CALLER, ) - tx = await account.execute_v1(calls=[call], max_fee=MAX_FEE) - await account.client.wait_for_tx(tx.transaction_hash) + tx = await argent_account.execute_v1(calls=[call], max_fee=MAX_FEE) + await argent_account.client.wait_for_tx(tx.transaction_hash) @pytest.mark.asyncio async def test_account_outside_execution_for_invalid_caller( - client, - argent_account_class_hash, + argent_account: BaseAccount, deployed_balance_contract, - deploy_account_details_factory, ): - address, key_pair, salt, class_hash = await deploy_account_details_factory.get( - class_hash=argent_account_class_hash, argent_calldata=True - ) - - deploy_result = await Account.deploy_account_v1( - address=address, - class_hash=class_hash, - salt=salt, - key_pair=key_pair, - client=client, - constructor_calldata=[key_pair.public_key, 0], - max_fee=MAX_FEE, - ) - await deploy_result.wait_for_acceptance() - account = deploy_result.account - assert any( [ - await account.supports_interface(SNIP9InterfaceVersion.V1), - await account.supports_interface(SNIP9InterfaceVersion.V2), + await argent_account.supports_interface(SNIP9InterfaceVersion.V1), + await argent_account.supports_interface(SNIP9InterfaceVersion.V2), ] ) @@ -109,7 +74,7 @@ async def test_account_outside_execution_for_invalid_caller( calldata=[100], ) - call = await account.sign_outside_execution_call( + call = await argent_account.sign_outside_execution_call( calls=[ increase_balance_call, increase_balance_call, @@ -122,41 +87,24 @@ async def test_account_outside_execution_for_invalid_caller( caller=deployed_balance_contract.address, ) - tx = await account.execute_v1(calls=[call], max_fee=MAX_FEE) + tx = await argent_account.execute_v1(calls=[call], max_fee=MAX_FEE) with pytest.raises(TransactionRevertedError) as err: - await account.client.wait_for_tx(tx.transaction_hash) + await argent_account.client.wait_for_tx(tx.transaction_hash) assert "argent/invalid-caller" in err.value.message @pytest.mark.asyncio async def test_account_outside_execution_for_impossible_timebounds( - client, - argent_account_class_hash, + argent_account: BaseAccount, deployed_balance_contract, - deploy_account_details_factory, ): - address, key_pair, salt, class_hash = await deploy_account_details_factory.get( - class_hash=argent_account_class_hash, argent_calldata=True - ) - - deploy_result = await Account.deploy_account_v1( - address=address, - class_hash=class_hash, - salt=salt, - key_pair=key_pair, - client=client, - constructor_calldata=[key_pair.public_key, 0], - max_fee=MAX_FEE, - ) - await deploy_result.wait_for_acceptance() - account = deploy_result.account assert any( [ - await account.supports_interface(SNIP9InterfaceVersion.V1), - await account.supports_interface(SNIP9InterfaceVersion.V2), + await argent_account.supports_interface(SNIP9InterfaceVersion.V1), + await argent_account.supports_interface(SNIP9InterfaceVersion.V2), ] ) @@ -166,7 +114,7 @@ async def test_account_outside_execution_for_impossible_timebounds( calldata=[100], ) - call = await account.sign_outside_execution_call( + call = await argent_account.sign_outside_execution_call( calls=[ increase_balance_call, increase_balance_call, @@ -179,9 +127,9 @@ async def test_account_outside_execution_for_impossible_timebounds( caller=ANY_CALLER, ) - tx = await account.execute_v1(calls=[call], max_fee=MAX_FEE) + tx = await argent_account.execute_v1(calls=[call], max_fee=MAX_FEE) with pytest.raises(TransactionRevertedError) as err: - await account.client.wait_for_tx(tx.transaction_hash) + await argent_account.client.wait_for_tx(tx.transaction_hash) assert "argent/invalid-timestamp" in err.value.message From 4f72882bfb8750a58d572ad41e5cdff4c8e44042 Mon Sep 17 00:00:00 2001 From: baitcode Date: Tue, 10 Dec 2024 21:16:38 +0300 Subject: [PATCH 15/51] remove balance change calls --- .../e2e/account/external_execution_test.py | 51 ++++++++++--------- .../tests/unit/net/models/transaction_test.py | 35 ------------- 2 files changed, 26 insertions(+), 60 deletions(-) delete mode 100644 starknet_py/tests/unit/net/models/transaction_test.py diff --git a/starknet_py/tests/e2e/account/external_execution_test.py b/starknet_py/tests/e2e/account/external_execution_test.py index b8b0caa80..089e0d967 100644 --- a/starknet_py/tests/e2e/account/external_execution_test.py +++ b/starknet_py/tests/e2e/account/external_execution_test.py @@ -23,7 +23,7 @@ async def test_argent_account_snip9_compatibility( @pytest.mark.asyncio async def test_account_outside_execution_any_caller( argent_account: BaseAccount, - deployed_balance_contract, + map_contract, ): assert any( @@ -33,17 +33,17 @@ async def test_account_outside_execution_any_caller( ] ) - increase_balance_call = Call( - to_addr=deployed_balance_contract.address, - selector=get_selector_from_name("increase_balance"), - calldata=[100], + put_call = Call( + to_addr=map_contract.address, + selector=get_selector_from_name("put"), + calldata=[20, 20], ) call = await argent_account.sign_outside_execution_call( calls=[ - increase_balance_call, - increase_balance_call, - increase_balance_call, + put_call, + put_call, + put_call, ], execution_time_bounds=ExecutionTimeBounds( execute_after=datetime.datetime.now() - datetime.timedelta(hours=1), @@ -59,7 +59,8 @@ async def test_account_outside_execution_any_caller( @pytest.mark.asyncio async def test_account_outside_execution_for_invalid_caller( argent_account: BaseAccount, - deployed_balance_contract, + account: BaseAccount, + map_contract, ): assert any( [ @@ -68,23 +69,23 @@ async def test_account_outside_execution_for_invalid_caller( ] ) - increase_balance_call = Call( - to_addr=deployed_balance_contract.address, - selector=get_selector_from_name("increase_balance"), - calldata=[100], + put_call = Call( + to_addr=map_contract.address, + selector=get_selector_from_name("put"), + calldata=[20, 20], ) call = await argent_account.sign_outside_execution_call( calls=[ - increase_balance_call, - increase_balance_call, - increase_balance_call, + put_call, + put_call, + put_call, ], execution_time_bounds=ExecutionTimeBounds( execute_after=datetime.datetime.now() - datetime.timedelta(hours=1), execute_before=datetime.datetime.now() + datetime.timedelta(hours=1), ), - caller=deployed_balance_contract.address, + caller=account.address, ) tx = await argent_account.execute_v1(calls=[call], max_fee=MAX_FEE) @@ -98,7 +99,7 @@ async def test_account_outside_execution_for_invalid_caller( @pytest.mark.asyncio async def test_account_outside_execution_for_impossible_timebounds( argent_account: BaseAccount, - deployed_balance_contract, + map_contract, ): assert any( @@ -108,17 +109,17 @@ async def test_account_outside_execution_for_impossible_timebounds( ] ) - increase_balance_call = Call( - to_addr=deployed_balance_contract.address, - selector=get_selector_from_name("increase_balance"), - calldata=[100], + put_call = Call( + to_addr=map_contract.address, + selector=get_selector_from_name("put"), + calldata=[20, 20], ) call = await argent_account.sign_outside_execution_call( calls=[ - increase_balance_call, - increase_balance_call, - increase_balance_call, + put_call, + put_call, + put_call, ], execution_time_bounds=ExecutionTimeBounds( execute_after=datetime.datetime.now() - datetime.timedelta(days=10), diff --git a/starknet_py/tests/unit/net/models/transaction_test.py b/starknet_py/tests/unit/net/models/transaction_test.py deleted file mode 100644 index 054d9de99..000000000 --- a/starknet_py/tests/unit/net/models/transaction_test.py +++ /dev/null @@ -1,35 +0,0 @@ -import datetime - -from starknet_py.constants import SNIP9InterfaceVersion -from starknet_py.hash.outside_execution import outside_execution_to_typed_data -from starknet_py.net.client_models import Call, OutsideExecution -from starknet_py.net.models import StarknetChainId - - -def test_generate_message_hash_for_execute_outside_transaction(): - now = datetime.datetime(2024, 4, 12, 0, 0, 0) - execute_after = now - datetime.timedelta(days=1) - execute_before = now - datetime.timedelta(days=1) - - execution = OutsideExecution( - caller=0x00000000000000000000000000000000011234567, - nonce=0x00000000000000000000000000000000011234567, - execute_after=int(execute_after.timestamp()), - execute_before=int(execute_before.timestamp()), - calls=[ - Call( - to_addr=0x00000000012736721676273672, - selector=0x72832873827382738273827, - calldata=[], - ) - ], - ) - - message_hash = outside_execution_to_typed_data( - execution, SNIP9InterfaceVersion.V1, StarknetChainId.SEPOLIA - ).message_hash(0x00000000001) - - assert ( - message_hash - == 0x54DBFD11FA2B470D9AE26560C0284AEBA6545C2368C7F6D92C4C4052EB1ACBC - ) From 1881d84559b9a30c542fc89ada841bf90d92b077 Mon Sep 17 00:00:00 2001 From: baitcode Date: Tue, 10 Dec 2024 21:19:11 +0300 Subject: [PATCH 16/51] Fixed naming --- .../e2e/docs/guide/test_account_sign_outside_transaction.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py b/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py index e9b290928..69c202c7f 100644 --- a/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py +++ b/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py @@ -21,7 +21,7 @@ async def test_account_outside_execution_any_caller( # Create a call to increase the balance by 100. That will be executed # as part of external execution - increase_balance_call = Call( + put_call = Call( to_addr=map_contract.address, selector=get_selector_from_name("put"), calldata=[20, 20], @@ -32,7 +32,7 @@ async def test_account_outside_execution_any_caller( # that allows any caller to execute the call. call = await argent_account.sign_outside_execution_call( calls=[ - increase_balance_call, + put_call, ], execution_time_bounds=ExecutionTimeBounds( execute_after=datetime.datetime.now() - datetime.timedelta(hours=1), From d36916559398cb0ae6f2596bde223a8055aa9d2a Mon Sep 17 00:00:00 2001 From: baitcode Date: Tue, 10 Dec 2024 21:20:29 +0300 Subject: [PATCH 17/51] fix documentation --- .../e2e/docs/guide/test_account_sign_outside_transaction.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py b/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py index 69c202c7f..0bf3bc35b 100644 --- a/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py +++ b/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py @@ -18,8 +18,8 @@ async def test_account_outside_execution_any_caller( from starknet_py.hash.selector import get_selector_from_name from starknet_py.net.client_models import Call, ExecutionTimeBounds - # Create a call to increase the balance by 100. That will be executed - # as part of external execution + # Create a call to put value 20 under key 20. That will be executed + # as part of external execution. put_call = Call( to_addr=map_contract.address, From 2c2be9f3e6105b5b00744356576783fa0e4ddf1f Mon Sep 17 00:00:00 2001 From: baitcode Date: Wed, 11 Dec 2024 02:34:51 +0300 Subject: [PATCH 18/51] added secrets --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 0bf226bdb..5db31dd95 100644 --- a/README.md +++ b/README.md @@ -212,4 +212,3 @@ Contract automatically serializes values to Cairo calldata. This includes adding See more info in [Serialization](https://starknetpy.readthedocs.io/en/latest/guide/serialization.html#serialization). Quickstart in docs - click [here](https://starknetpy.rtfd.io/en/latest/quickstart.html). - From f7867c168e01294741259b14cb54d48dd328ad32 Mon Sep 17 00:00:00 2001 From: baitcode Date: Wed, 11 Dec 2024 17:10:41 +0300 Subject: [PATCH 19/51] trigger build --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 508830be8..417b852fe 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -64,6 +64,7 @@ test = [ ] test_ci = ["test_ci_v1", "test_ci_v2"] + test_ci_v1 = "coverage run -a -m pytest --contract_dir=v1 starknet_py --ignore=starknet_py/tests/e2e/docs --ignore=starknet_py/tests/e2e/tests_on_networks" test_ci_v2 = "coverage run -a -m pytest --contract_dir=v2 starknet_py --ignore=starknet_py/tests/e2e/docs --ignore=starknet_py/tests/e2e/tests_on_networks" From a6b7774cde2c48d2c87c5b50611acd076d7674b5 Mon Sep 17 00:00:00 2001 From: baitcode Date: Sat, 14 Dec 2024 12:27:45 +0300 Subject: [PATCH 20/51] Review comments fixws --- docs/guide/account_and_client.rst | 2 +- pyproject.toml | 1 - starknet_py/constants.py | 2 +- starknet_py/hash/outside_execution.py | 16 +++--- starknet_py/net/account/account.py | 51 +++++++++++-------- starknet_py/net/account/base_account.py | 14 ++--- starknet_py/net/client_models.py | 3 +- starknet_py/net/schemas/common.py | 9 ---- .../e2e/account/external_execution_test.py | 34 +++++++++---- 9 files changed, 76 insertions(+), 56 deletions(-) diff --git a/docs/guide/account_and_client.rst b/docs/guide/account_and_client.rst index 90916c513..22f1661c6 100644 --- a/docs/guide/account_and_client.rst +++ b/docs/guide/account_and_client.rst @@ -49,7 +49,7 @@ Account also provides a way of creating signed transaction without sending them. Creating "Outside transaction" and executing it. `SNIP-9 `_ --------------------------------------------------------------------------------------------------------------------------- -Account also provides a way of creating a call and signing to allow for another account (caller) to execute it later on original account behalf. This will also allow caller to execute calls encoded in that transaction for free (signer will pay the fee). +Account also provides a way of creating a call and signing to allow for another account (caller) to execute it later on original account behalf. Account does not need to funded with tokens for transaction to execute as caller will pay the execution fee. .. codesnippet:: ../../starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py :language: python diff --git a/pyproject.toml b/pyproject.toml index 417b852fe..6bd5bc335 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -140,4 +140,3 @@ exclude = [ "**/__pycache__", "starknet_py/tests/e2e/docs", ] - diff --git a/starknet_py/constants.py b/starknet_py/constants.py index efe09f707..f252a5fe9 100644 --- a/starknet_py/constants.py +++ b/starknet_py/constants.py @@ -52,6 +52,6 @@ # SNIP-9 INTERFACE_VERSION with ID -class SNIP9InterfaceVersion(IntEnum): +class OutsideExecutionInterfaceVersion(IntEnum): V1 = 0x68CFD18B92D1907B8BA3CC324900277F5A3622099431EA85DD8089255E4181 V2 = 0x1D1144BB2138366FF28D8E9AB57456B1D332AC42196230C3A602003C89872 diff --git a/starknet_py/hash/outside_execution.py b/starknet_py/hash/outside_execution.py index 47e481696..6b51b8efa 100644 --- a/starknet_py/hash/outside_execution.py +++ b/starknet_py/hash/outside_execution.py @@ -1,19 +1,19 @@ -from starknet_py.constants import SNIP9InterfaceVersion +from starknet_py.constants import OutsideExecutionInterfaceVersion from starknet_py.net.client_models import OutsideExecution from starknet_py.net.schemas.common import Revision -from starknet_py.utils import typed_data as td +from starknet_py.utils.typed_data import TypedData SNIP9_INTERFACE_ID_TO_SNIP12_REVISION = { - SNIP9InterfaceVersion.V1: Revision.V0, - SNIP9InterfaceVersion.V2: Revision.V1, + OutsideExecutionInterfaceVersion.V1: Revision.V0, + OutsideExecutionInterfaceVersion.V2: Revision.V1, } def outside_execution_to_typed_data( outside_execution: OutsideExecution, - snip9_version: SNIP9InterfaceVersion, + snip9_version: OutsideExecutionInterfaceVersion, chain_id: int, -) -> td.TypedData: +) -> TypedData: """ SNIP-12 Typed Data for OutsideExecution implementation. For revision V0 and V1. """ @@ -21,7 +21,7 @@ def outside_execution_to_typed_data( revision = SNIP9_INTERFACE_ID_TO_SNIP12_REVISION[snip9_version] if revision == Revision.V0: - return td.TypedData.from_dict( + return TypedData.from_dict( { "types": { "StarkNetDomain": [ @@ -71,7 +71,7 @@ def outside_execution_to_typed_data( ) # revision == Revision.V1 - return td.TypedData.from_dict( + return TypedData.from_dict( { "types": { "StarknetDomain": [ diff --git a/starknet_py/net/account/account.py b/starknet_py/net/account/account.py index b930ce6b3..ce1b656fb 100644 --- a/starknet_py/net/account/account.py +++ b/starknet_py/net/account/account.py @@ -8,14 +8,17 @@ ANY_CALLER, FEE_CONTRACT_ADDRESS, QUERY_VERSION_BASE, - SNIP9InterfaceVersion, + OutsideExecutionInterfaceVersion, ) from starknet_py.hash.address import compute_address from starknet_py.hash.outside_execution import outside_execution_to_typed_data from starknet_py.hash.selector import get_selector_from_name from starknet_py.hash.utils import verify_message_signature from starknet_py.net.account.account_deployment_result import AccountDeploymentResult -from starknet_py.net.account.base_account import BaseAccount, SNIP9SupportBaseMixin +from starknet_py.net.account.base_account import ( + BaseAccount, + OutsideExecutionSupportBaseMixin, +) from starknet_py.net.client import Client from starknet_py.net.client_models import ( Call, @@ -58,10 +61,9 @@ from starknet_py.utils.sync import add_sync_methods from starknet_py.utils.typed_data import TypedData - -# pylint: disable=too-many-public-methods +# pylint: disable=too-many-public-methods,disable=too-many-lines @add_sync_methods -class Account(BaseAccount, SNIP9SupportBaseMixin): +class Account(BaseAccount, OutsideExecutionSupportBaseMixin): """ Default Account implementation. """ @@ -296,7 +298,7 @@ async def get_nonce( self.address, block_hash=block_hash, block_number=block_number ) - async def _check_snip9_nonce( + async def _check_outside_execution_nonce( self, nonce: int, *, @@ -314,21 +316,28 @@ async def _check_snip9_nonce( ) return bool(is_valid) - async def get_snip9_nonce(self, retry_count=10) -> int: + async def get_outside_execution_nonce(self, retry_count=10) -> int: while retry_count > 0: random_stark_address = KeyPair.generate().public_key - if await self._check_snip9_nonce(random_stark_address): + if await self._check_outside_execution_nonce(random_stark_address): return random_stark_address retry_count -= 1 raise RuntimeError("Failed to generate a valid nonce") - async def _get_snip9_version(self) -> Union[SNIP9InterfaceVersion, None]: - for version in [SNIP9InterfaceVersion.V1, SNIP9InterfaceVersion.V2]: + async def _get_outside_execution_version( + self, + ) -> Union[OutsideExecutionInterfaceVersion, None]: + for version in [ + OutsideExecutionInterfaceVersion.V1, + OutsideExecutionInterfaceVersion.V2, + ]: if await self.supports_interface(version): return version return None - async def supports_interface(self, interface_id: SNIP9InterfaceVersion) -> bool: + async def supports_interface( + self, interface_id: OutsideExecutionInterfaceVersion + ) -> bool: (does_support,) = await self._client.call_contract( Call( to_addr=self.address, @@ -399,18 +408,18 @@ async def sign_outside_execution_call( *, caller: AddressRepresentation = ANY_CALLER, nonce: Optional[int] = None, - version: Optional[SNIP9InterfaceVersion] = None, + interface_version: Optional[OutsideExecutionInterfaceVersion] = None, ) -> Call: - if version is None: - version = await self._get_snip9_version() + if interface_version is None: + interface_version = await self._get_outside_execution_version() - if version is None: + if interface_version is None: raise RuntimeError( "Can't initiate outside execution SNIP-9 is unsupported." ) if nonce is None: - nonce = await self.get_snip9_nonce() + nonce = await self.get_outside_execution_nonce() outside_execution = OutsideExecution( caller=parse_address(caller), @@ -421,17 +430,19 @@ async def sign_outside_execution_call( ) chain_id = await self._get_chain_id() signature = self.signer.sign_message( - outside_execution_to_typed_data(outside_execution, version, chain_id), + outside_execution_to_typed_data( + outside_execution, interface_version, chain_id + ), self.address, ) selector_for_version = { - SNIP9InterfaceVersion.V1: "execute_from_outside", - SNIP9InterfaceVersion.V2: "execute_from_outside_v2", + OutsideExecutionInterfaceVersion.V1: "execute_from_outside", + OutsideExecutionInterfaceVersion.V2: "execute_from_outside_v2", } return Call( to_addr=self.address, - selector=get_selector_from_name(selector_for_version[version]), + selector=get_selector_from_name(selector_for_version[interface_version]), calldata=_transaction_serialiser.serialize( { "external_execution": outside_execution.to_abi_dict(), diff --git a/starknet_py/net/account/base_account.py b/starknet_py/net/account/base_account.py index 4142e7d72..8bd734e12 100644 --- a/starknet_py/net/account/base_account.py +++ b/starknet_py/net/account/base_account.py @@ -1,7 +1,7 @@ from abc import ABC, abstractmethod from typing import List, Optional, Union -from starknet_py.constants import ANY_CALLER, SNIP9InterfaceVersion +from starknet_py.constants import ANY_CALLER, OutsideExecutionInterfaceVersion from starknet_py.net.client import Client from starknet_py.net.client_models import ( Call, @@ -28,16 +28,18 @@ from starknet_py.net.models.typed_data import TypedDataDict -class SNIP9SupportBaseMixin(ABC): +class OutsideExecutionSupportBaseMixin(ABC): @abstractmethod - async def get_snip9_nonce(self) -> int: + async def get_outside_execution_nonce(self) -> int: """ Generate special valid nonce (passed check_snip9_nonce) for external calls execution. """ @abstractmethod - async def supports_interface(self, interface_id: SNIP9InterfaceVersion) -> bool: + async def supports_interface( + self, interface_id: OutsideExecutionInterfaceVersion + ) -> bool: """ Check if the account supports the given SNIP9 interface. Part of ISRC5 standard. """ @@ -50,7 +52,7 @@ async def sign_outside_execution_call( *, caller: AddressRepresentation = ANY_CALLER, nonce: Optional[int] = None, - version: Optional[SNIP9InterfaceVersion] = None, + interface_version: Optional[OutsideExecutionInterfaceVersion] = None, ) -> Call: """ Creates a call for an external execution (SNIP-9 specification). @@ -64,7 +66,7 @@ async def sign_outside_execution_call( """ -class BaseAccount(SNIP9SupportBaseMixin, ABC): +class BaseAccount(OutsideExecutionSupportBaseMixin, ABC): """ Base class for all account implementations. diff --git a/starknet_py/net/client_models.py b/starknet_py/net/client_models.py index 9ee436bf8..4a2690d14 100644 --- a/starknet_py/net/client_models.py +++ b/starknet_py/net/client_models.py @@ -120,7 +120,8 @@ def init_with_zeros(): @dataclass class ExecutionTimeBounds: """ - Dataclass representing time bounds within which the given time bounds. + Dataclass representing time bounds within which outside execution + transaction is valid and allowed to be executed. """ execute_after: datetime.datetime diff --git a/starknet_py/net/schemas/common.py b/starknet_py/net/schemas/common.py index e2f3c5c4e..ee3584bf1 100644 --- a/starknet_py/net/schemas/common.py +++ b/starknet_py/net/schemas/common.py @@ -80,15 +80,6 @@ def _is_str_and_valid_pattern(self, value: Any) -> bool: ) -class Selector(NumberAsHex): - """ - Field used to serialize and deserialize selector type. - """ - - MAX_VALUE = 2**32 - REGEX_PATTERN = r"^0x(0|[a-fA-F1-9]{1}[a-fA-F0-9]{0,7})$" - - class Felt(NumberAsHex): """ Field used to serialize and deserialize felt type. diff --git a/starknet_py/tests/e2e/account/external_execution_test.py b/starknet_py/tests/e2e/account/external_execution_test.py index 089e0d967..888b06c0c 100644 --- a/starknet_py/tests/e2e/account/external_execution_test.py +++ b/starknet_py/tests/e2e/account/external_execution_test.py @@ -2,7 +2,7 @@ import pytest -from starknet_py.constants import ANY_CALLER, SNIP9InterfaceVersion +from starknet_py.constants import ANY_CALLER, OutsideExecutionInterfaceVersion from starknet_py.hash.selector import get_selector_from_name from starknet_py.net.account.account import BaseAccount from starknet_py.net.client_models import Call, ExecutionTimeBounds @@ -14,9 +14,13 @@ async def test_argent_account_snip9_compatibility( argent_account: BaseAccount, ): - result = await argent_account.supports_interface(SNIP9InterfaceVersion.V1) + result = await argent_account.supports_interface( + OutsideExecutionInterfaceVersion.V1 + ) assert result is True - result = await argent_account.supports_interface(SNIP9InterfaceVersion.V2) + result = await argent_account.supports_interface( + OutsideExecutionInterfaceVersion.V2 + ) assert result is False @@ -28,8 +32,12 @@ async def test_account_outside_execution_any_caller( assert any( [ - await argent_account.supports_interface(SNIP9InterfaceVersion.V1), - await argent_account.supports_interface(SNIP9InterfaceVersion.V2), + await argent_account.supports_interface( + OutsideExecutionInterfaceVersion.V1 + ), + await argent_account.supports_interface( + OutsideExecutionInterfaceVersion.V2 + ), ] ) @@ -64,8 +72,12 @@ async def test_account_outside_execution_for_invalid_caller( ): assert any( [ - await argent_account.supports_interface(SNIP9InterfaceVersion.V1), - await argent_account.supports_interface(SNIP9InterfaceVersion.V2), + await argent_account.supports_interface( + OutsideExecutionInterfaceVersion.V1 + ), + await argent_account.supports_interface( + OutsideExecutionInterfaceVersion.V2 + ), ] ) @@ -104,8 +116,12 @@ async def test_account_outside_execution_for_impossible_timebounds( assert any( [ - await argent_account.supports_interface(SNIP9InterfaceVersion.V1), - await argent_account.supports_interface(SNIP9InterfaceVersion.V2), + await argent_account.supports_interface( + OutsideExecutionInterfaceVersion.V1 + ), + await argent_account.supports_interface( + OutsideExecutionInterfaceVersion.V2 + ), ] ) From 92d651c7fb14f18b8100b304972e3fe3ca85c0f2 Mon Sep 17 00:00:00 2001 From: baitcode Date: Sat, 14 Dec 2024 12:36:56 +0300 Subject: [PATCH 21/51] linter --- starknet_py/net/account/account.py | 1 + 1 file changed, 1 insertion(+) diff --git a/starknet_py/net/account/account.py b/starknet_py/net/account/account.py index ce1b656fb..7aa5884b4 100644 --- a/starknet_py/net/account/account.py +++ b/starknet_py/net/account/account.py @@ -61,6 +61,7 @@ from starknet_py.utils.sync import add_sync_methods from starknet_py.utils.typed_data import TypedData + # pylint: disable=too-many-public-methods,disable=too-many-lines @add_sync_methods class Account(BaseAccount, OutsideExecutionSupportBaseMixin): From 585e8df5b6c199298d83b814287bc1914956438c Mon Sep 17 00:00:00 2001 From: baitcode Date: Sat, 14 Dec 2024 12:47:00 +0300 Subject: [PATCH 22/51] fixup --- pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 6bd5bc335..d492cf2bf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -64,7 +64,6 @@ test = [ ] test_ci = ["test_ci_v1", "test_ci_v2"] - test_ci_v1 = "coverage run -a -m pytest --contract_dir=v1 starknet_py --ignore=starknet_py/tests/e2e/docs --ignore=starknet_py/tests/e2e/tests_on_networks" test_ci_v2 = "coverage run -a -m pytest --contract_dir=v2 starknet_py --ignore=starknet_py/tests/e2e/docs --ignore=starknet_py/tests/e2e/tests_on_networks" From 9b1202408c1cfd164ee8791d9585067e51c07c96 Mon Sep 17 00:00:00 2001 From: baitcode Date: Sat, 14 Dec 2024 16:29:10 +0300 Subject: [PATCH 23/51] Added comment --- starknet_py/hash/outside_execution.py | 1 + 1 file changed, 1 insertion(+) diff --git a/starknet_py/hash/outside_execution.py b/starknet_py/hash/outside_execution.py index 6b51b8efa..ff8ecf58d 100644 --- a/starknet_py/hash/outside_execution.py +++ b/starknet_py/hash/outside_execution.py @@ -9,6 +9,7 @@ } +#TODO(#1537): Implement as method of OutsideExecution def outside_execution_to_typed_data( outside_execution: OutsideExecution, snip9_version: OutsideExecutionInterfaceVersion, From 81da8fe8e63e6934f03c6240a4ce428f90b8d634 Mon Sep 17 00:00:00 2001 From: baitcode Date: Sat, 14 Dec 2024 16:31:48 +0300 Subject: [PATCH 24/51] fix --- starknet_py/hash/outside_execution.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/starknet_py/hash/outside_execution.py b/starknet_py/hash/outside_execution.py index ff8ecf58d..a8ab6eeb4 100644 --- a/starknet_py/hash/outside_execution.py +++ b/starknet_py/hash/outside_execution.py @@ -9,7 +9,7 @@ } -#TODO(#1537): Implement as method of OutsideExecution +# TODO(#1537): Implement as method of OutsideExecution def outside_execution_to_typed_data( outside_execution: OutsideExecution, snip9_version: OutsideExecutionInterfaceVersion, From 2bb0282d150208e44ba312764a35ff3e5d46f0db Mon Sep 17 00:00:00 2001 From: baitcode Date: Sun, 15 Dec 2024 20:08:47 +0300 Subject: [PATCH 25/51] lost empty line --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index d492cf2bf..508830be8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -139,3 +139,4 @@ exclude = [ "**/__pycache__", "starknet_py/tests/e2e/docs", ] + From 812429d521980969db41e3e7fbf710ddc5054066 Mon Sep 17 00:00:00 2001 From: baitcode Date: Sun, 15 Dec 2024 20:29:35 +0300 Subject: [PATCH 26/51] comments fixes --- starknet_py/constants.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/starknet_py/constants.py b/starknet_py/constants.py index f252a5fe9..7955a14db 100644 --- a/starknet_py/constants.py +++ b/starknet_py/constants.py @@ -47,11 +47,11 @@ SIGNATURE_RESPONSE_LENGTH = 65 VERSION_RESPONSE_LENGTH = 3 -# SNIP-9 ANY_CALLER +# Result of `encode_shortstring("ANY_CALLER")` ANY_CALLER = 0x414E595F43414C4C4552 -# SNIP-9 INTERFACE_VERSION with ID +# OUTSIDE EXECUTION INTERFACE_VERSION with ID class OutsideExecutionInterfaceVersion(IntEnum): V1 = 0x68CFD18B92D1907B8BA3CC324900277F5A3622099431EA85DD8089255E4181 V2 = 0x1D1144BB2138366FF28D8E9AB57456B1D332AC42196230C3A602003C89872 From 6e2b7f4802b14c37a8639e60d8e036ba9033ad20 Mon Sep 17 00:00:00 2001 From: Ilia Batii Date: Sun, 15 Dec 2024 17:30:21 +0000 Subject: [PATCH 27/51] Update starknet_py/constants.py Co-authored-by: Franciszek Job <54181625+franciszekjob@users.noreply.github.com> --- starknet_py/constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/starknet_py/constants.py b/starknet_py/constants.py index 7955a14db..35199aa2c 100644 --- a/starknet_py/constants.py +++ b/starknet_py/constants.py @@ -52,6 +52,6 @@ # OUTSIDE EXECUTION INTERFACE_VERSION with ID -class OutsideExecutionInterfaceVersion(IntEnum): +class OutsideExecutionInterfaceID(IntEnum): V1 = 0x68CFD18B92D1907B8BA3CC324900277F5A3622099431EA85DD8089255E4181 V2 = 0x1D1144BB2138366FF28D8E9AB57456B1D332AC42196230C3A602003C89872 From 44faa3ba1b7fc55e823b9432479103e513b798fe Mon Sep 17 00:00:00 2001 From: baitcode Date: Sun, 15 Dec 2024 20:31:17 +0300 Subject: [PATCH 28/51] comment fixupi --- starknet_py/hash/outside_execution.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/starknet_py/hash/outside_execution.py b/starknet_py/hash/outside_execution.py index a8ab6eeb4..a0c4303b9 100644 --- a/starknet_py/hash/outside_execution.py +++ b/starknet_py/hash/outside_execution.py @@ -3,7 +3,7 @@ from starknet_py.net.schemas.common import Revision from starknet_py.utils.typed_data import TypedData -SNIP9_INTERFACE_ID_TO_SNIP12_REVISION = { +OUTSIDE_EXECUTION_INTERFACE_ID_TO_TYPED_DATA_REVISION = { OutsideExecutionInterfaceVersion.V1: Revision.V0, OutsideExecutionInterfaceVersion.V2: Revision.V1, } @@ -19,7 +19,7 @@ def outside_execution_to_typed_data( SNIP-12 Typed Data for OutsideExecution implementation. For revision V0 and V1. """ - revision = SNIP9_INTERFACE_ID_TO_SNIP12_REVISION[snip9_version] + revision = OUTSIDE_EXECUTION_INTERFACE_ID_TO_TYPED_DATA_REVISION[snip9_version] if revision == Revision.V0: return TypedData.from_dict( From 37b6ce4c2fd1c101d913db4ba517bac11151d3ac Mon Sep 17 00:00:00 2001 From: baitcode Date: Sun, 15 Dec 2024 20:39:49 +0300 Subject: [PATCH 29/51] fix wordings --- starknet_py/hash/outside_execution.py | 4 ++-- starknet_py/net/account/account.py | 10 +++++----- starknet_py/net/account/base_account.py | 8 ++++---- .../guide/test_account_sign_outside_transaction.py | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/starknet_py/hash/outside_execution.py b/starknet_py/hash/outside_execution.py index a0c4303b9..004acca76 100644 --- a/starknet_py/hash/outside_execution.py +++ b/starknet_py/hash/outside_execution.py @@ -12,14 +12,14 @@ # TODO(#1537): Implement as method of OutsideExecution def outside_execution_to_typed_data( outside_execution: OutsideExecution, - snip9_version: OutsideExecutionInterfaceVersion, + ouside_execution_version: OutsideExecutionInterfaceVersion, chain_id: int, ) -> TypedData: """ SNIP-12 Typed Data for OutsideExecution implementation. For revision V0 and V1. """ - revision = OUTSIDE_EXECUTION_INTERFACE_ID_TO_TYPED_DATA_REVISION[snip9_version] + revision = OUTSIDE_EXECUTION_INTERFACE_ID_TO_TYPED_DATA_REVISION[ouside_execution_version] if revision == Revision.V0: return TypedData.from_dict( diff --git a/starknet_py/net/account/account.py b/starknet_py/net/account/account.py index 7aa5884b4..220a7cd4b 100644 --- a/starknet_py/net/account/account.py +++ b/starknet_py/net/account/account.py @@ -416,7 +416,7 @@ async def sign_outside_execution_call( if interface_version is None: raise RuntimeError( - "Can't initiate outside execution SNIP-9 is unsupported." + "Can't initiate call, outside execution is not supported." ) if nonce is None: @@ -444,9 +444,9 @@ async def sign_outside_execution_call( return Call( to_addr=self.address, selector=get_selector_from_name(selector_for_version[interface_version]), - calldata=_transaction_serialiser.serialize( + calldata=_outside_execution_serialiser.serialize( { - "external_execution": outside_execution.to_abi_dict(), + "outside_execution": outside_execution.to_abi_dict(), "signature": signature, } ), @@ -996,9 +996,9 @@ def _parse_calls_cairo_v1(calls: Iterable[Call]) -> List[Dict]: calls=ArraySerializer(_call_description_cairo_v1), ) ) -_transaction_serialiser = StructSerializer( +_outside_execution_serialiser = StructSerializer( OrderedDict( - external_execution=StructSerializer( + outside_execution=StructSerializer( OrderedDict( caller=FeltSerializer(), nonce=FeltSerializer(), diff --git a/starknet_py/net/account/base_account.py b/starknet_py/net/account/base_account.py index 8bd734e12..0488d290c 100644 --- a/starknet_py/net/account/base_account.py +++ b/starknet_py/net/account/base_account.py @@ -33,7 +33,7 @@ class OutsideExecutionSupportBaseMixin(ABC): @abstractmethod async def get_outside_execution_nonce(self) -> int: """ - Generate special valid nonce (passed check_snip9_nonce) for external calls execution. + Generate special valid nonce for outside execution calls. """ @abstractmethod @@ -41,7 +41,7 @@ async def supports_interface( self, interface_id: OutsideExecutionInterfaceVersion ) -> bool: """ - Check if the account supports the given SNIP9 interface. Part of ISRC5 standard. + Check if the account supports the given outside execution interface. Part of ISRC5 standard. """ @abstractmethod @@ -55,13 +55,13 @@ async def sign_outside_execution_call( interface_version: Optional[OutsideExecutionInterfaceVersion] = None, ) -> Call: """ - Creates a call for an external execution (SNIP-9 specification). + Creates a call for an outcide execution (SNIP-9 specification). :param calls: Single call or list of calls to be executed by outside caller. :param execution_time_bounds: Execution time bounds for the call. :param caller: Address of the caller. IMPORTANT! By default it is ANY_CALLER. :param nonce: Nonce for the transaction. Is populated automatically if not provided. - :param version: SNIP-9 interface version. Method will check which version account + :param interface_version: SNIP-9 interface version. Method will check which version account supports and use the highest one and populate the value. """ diff --git a/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py b/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py index 0bf3bc35b..e6c9136a0 100644 --- a/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py +++ b/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py @@ -19,7 +19,7 @@ async def test_account_outside_execution_any_caller( from starknet_py.net.client_models import Call, ExecutionTimeBounds # Create a call to put value 20 under key 20. That will be executed - # as part of external execution. + # as part of outside execution. put_call = Call( to_addr=map_contract.address, From 6cacd8bce34964a38650769eb595f38ed11aeffd Mon Sep 17 00:00:00 2001 From: baitcode Date: Sun, 15 Dec 2024 20:40:38 +0300 Subject: [PATCH 30/51] fix --- starknet_py/hash/outside_execution.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/starknet_py/hash/outside_execution.py b/starknet_py/hash/outside_execution.py index 004acca76..f7689a5cd 100644 --- a/starknet_py/hash/outside_execution.py +++ b/starknet_py/hash/outside_execution.py @@ -19,7 +19,9 @@ def outside_execution_to_typed_data( SNIP-12 Typed Data for OutsideExecution implementation. For revision V0 and V1. """ - revision = OUTSIDE_EXECUTION_INTERFACE_ID_TO_TYPED_DATA_REVISION[ouside_execution_version] + revision = OUTSIDE_EXECUTION_INTERFACE_ID_TO_TYPED_DATA_REVISION[ + ouside_execution_version + ] if revision == Revision.V0: return TypedData.from_dict( From 11069b4919973fbaace39feefb0280084931dc94 Mon Sep 17 00:00:00 2001 From: baitcode Date: Sun, 15 Dec 2024 20:52:38 +0300 Subject: [PATCH 31/51] fixes --- starknet_py/hash/outside_execution.py | 10 +++++----- starknet_py/net/account/account.py | 20 +++++++++---------- starknet_py/net/account/base_account.py | 6 +++--- .../e2e/account/external_execution_test.py | 18 ++++++++--------- 4 files changed, 27 insertions(+), 27 deletions(-) diff --git a/starknet_py/hash/outside_execution.py b/starknet_py/hash/outside_execution.py index f7689a5cd..a5c5f8f38 100644 --- a/starknet_py/hash/outside_execution.py +++ b/starknet_py/hash/outside_execution.py @@ -1,18 +1,18 @@ -from starknet_py.constants import OutsideExecutionInterfaceVersion +from starknet_py.constants import OutsideExecutionInterfaceID from starknet_py.net.client_models import OutsideExecution from starknet_py.net.schemas.common import Revision from starknet_py.utils.typed_data import TypedData OUTSIDE_EXECUTION_INTERFACE_ID_TO_TYPED_DATA_REVISION = { - OutsideExecutionInterfaceVersion.V1: Revision.V0, - OutsideExecutionInterfaceVersion.V2: Revision.V1, + OutsideExecutionInterfaceID.V1: Revision.V0, + OutsideExecutionInterfaceID.V2: Revision.V1, } # TODO(#1537): Implement as method of OutsideExecution def outside_execution_to_typed_data( outside_execution: OutsideExecution, - ouside_execution_version: OutsideExecutionInterfaceVersion, + outside_execution_version: OutsideExecutionInterfaceID, chain_id: int, ) -> TypedData: """ @@ -20,7 +20,7 @@ def outside_execution_to_typed_data( """ revision = OUTSIDE_EXECUTION_INTERFACE_ID_TO_TYPED_DATA_REVISION[ - ouside_execution_version + outside_execution_version ] if revision == Revision.V0: diff --git a/starknet_py/net/account/account.py b/starknet_py/net/account/account.py index 220a7cd4b..8e9bd6428 100644 --- a/starknet_py/net/account/account.py +++ b/starknet_py/net/account/account.py @@ -8,7 +8,7 @@ ANY_CALLER, FEE_CONTRACT_ADDRESS, QUERY_VERSION_BASE, - OutsideExecutionInterfaceVersion, + OutsideExecutionInterfaceID, ) from starknet_py.hash.address import compute_address from starknet_py.hash.outside_execution import outside_execution_to_typed_data @@ -327,17 +327,17 @@ async def get_outside_execution_nonce(self, retry_count=10) -> int: async def _get_outside_execution_version( self, - ) -> Union[OutsideExecutionInterfaceVersion, None]: + ) -> Union[OutsideExecutionInterfaceID, None]: for version in [ - OutsideExecutionInterfaceVersion.V1, - OutsideExecutionInterfaceVersion.V2, + OutsideExecutionInterfaceID.V1, + OutsideExecutionInterfaceID.V2, ]: if await self.supports_interface(version): return version return None async def supports_interface( - self, interface_id: OutsideExecutionInterfaceVersion + self, interface_id: OutsideExecutionInterfaceID ) -> bool: (does_support,) = await self._client.call_contract( Call( @@ -409,7 +409,7 @@ async def sign_outside_execution_call( *, caller: AddressRepresentation = ANY_CALLER, nonce: Optional[int] = None, - interface_version: Optional[OutsideExecutionInterfaceVersion] = None, + interface_version: Optional[OutsideExecutionInterfaceID] = None, ) -> Call: if interface_version is None: interface_version = await self._get_outside_execution_version() @@ -437,14 +437,14 @@ async def sign_outside_execution_call( self.address, ) selector_for_version = { - OutsideExecutionInterfaceVersion.V1: "execute_from_outside", - OutsideExecutionInterfaceVersion.V2: "execute_from_outside_v2", + OutsideExecutionInterfaceID.V1: "execute_from_outside", + OutsideExecutionInterfaceID.V2: "execute_from_outside_v2", } return Call( to_addr=self.address, selector=get_selector_from_name(selector_for_version[interface_version]), - calldata=_outside_execution_serialiser.serialize( + calldata=_outside_transaction_serialiser.serialize( { "outside_execution": outside_execution.to_abi_dict(), "signature": signature, @@ -996,7 +996,7 @@ def _parse_calls_cairo_v1(calls: Iterable[Call]) -> List[Dict]: calls=ArraySerializer(_call_description_cairo_v1), ) ) -_outside_execution_serialiser = StructSerializer( +_outside_transaction_serialiser = StructSerializer( OrderedDict( outside_execution=StructSerializer( OrderedDict( diff --git a/starknet_py/net/account/base_account.py b/starknet_py/net/account/base_account.py index 0488d290c..77d10e455 100644 --- a/starknet_py/net/account/base_account.py +++ b/starknet_py/net/account/base_account.py @@ -1,7 +1,7 @@ from abc import ABC, abstractmethod from typing import List, Optional, Union -from starknet_py.constants import ANY_CALLER, OutsideExecutionInterfaceVersion +from starknet_py.constants import ANY_CALLER, OutsideExecutionInterfaceID from starknet_py.net.client import Client from starknet_py.net.client_models import ( Call, @@ -38,7 +38,7 @@ async def get_outside_execution_nonce(self) -> int: @abstractmethod async def supports_interface( - self, interface_id: OutsideExecutionInterfaceVersion + self, interface_id: OutsideExecutionInterfaceID ) -> bool: """ Check if the account supports the given outside execution interface. Part of ISRC5 standard. @@ -52,7 +52,7 @@ async def sign_outside_execution_call( *, caller: AddressRepresentation = ANY_CALLER, nonce: Optional[int] = None, - interface_version: Optional[OutsideExecutionInterfaceVersion] = None, + interface_version: Optional[OutsideExecutionInterfaceID] = None, ) -> Call: """ Creates a call for an outcide execution (SNIP-9 specification). diff --git a/starknet_py/tests/e2e/account/external_execution_test.py b/starknet_py/tests/e2e/account/external_execution_test.py index 888b06c0c..6ccd92d55 100644 --- a/starknet_py/tests/e2e/account/external_execution_test.py +++ b/starknet_py/tests/e2e/account/external_execution_test.py @@ -2,7 +2,7 @@ import pytest -from starknet_py.constants import ANY_CALLER, OutsideExecutionInterfaceVersion +from starknet_py.constants import ANY_CALLER, OutsideExecutionInterfaceID from starknet_py.hash.selector import get_selector_from_name from starknet_py.net.account.account import BaseAccount from starknet_py.net.client_models import Call, ExecutionTimeBounds @@ -15,11 +15,11 @@ async def test_argent_account_snip9_compatibility( argent_account: BaseAccount, ): result = await argent_account.supports_interface( - OutsideExecutionInterfaceVersion.V1 + OutsideExecutionInterfaceID.V1 ) assert result is True result = await argent_account.supports_interface( - OutsideExecutionInterfaceVersion.V2 + OutsideExecutionInterfaceID.V2 ) assert result is False @@ -33,10 +33,10 @@ async def test_account_outside_execution_any_caller( assert any( [ await argent_account.supports_interface( - OutsideExecutionInterfaceVersion.V1 + OutsideExecutionInterfaceID.V1 ), await argent_account.supports_interface( - OutsideExecutionInterfaceVersion.V2 + OutsideExecutionInterfaceID.V2 ), ] ) @@ -73,10 +73,10 @@ async def test_account_outside_execution_for_invalid_caller( assert any( [ await argent_account.supports_interface( - OutsideExecutionInterfaceVersion.V1 + OutsideExecutionInterfaceID.V1 ), await argent_account.supports_interface( - OutsideExecutionInterfaceVersion.V2 + OutsideExecutionInterfaceID.V2 ), ] ) @@ -117,10 +117,10 @@ async def test_account_outside_execution_for_impossible_timebounds( assert any( [ await argent_account.supports_interface( - OutsideExecutionInterfaceVersion.V1 + OutsideExecutionInterfaceID.V1 ), await argent_account.supports_interface( - OutsideExecutionInterfaceVersion.V2 + OutsideExecutionInterfaceID.V2 ), ] ) From 45644f90924ca02cc865e92ef785c3b7345ca8fa Mon Sep 17 00:00:00 2001 From: baitcode Date: Sun, 15 Dec 2024 20:57:46 +0300 Subject: [PATCH 32/51] more fixes --- starknet_py/net/account/account.py | 7 +++- starknet_py/net/account/base_account.py | 4 +- starknet_py/net/client_models.py | 3 +- .../e2e/account/external_execution_test.py | 40 ++++++------------- 4 files changed, 20 insertions(+), 34 deletions(-) diff --git a/starknet_py/net/account/account.py b/starknet_py/net/account/account.py index 8e9bd6428..0efb4986a 100644 --- a/starknet_py/net/account/account.py +++ b/starknet_py/net/account/account.py @@ -24,7 +24,7 @@ Call, Calls, EstimatedFee, - ExecutionTimeBounds, + OutsideExecutionTimeBounds, Hash, OutsideExecution, ResourceBounds, @@ -224,7 +224,9 @@ async def _prepare_invoke( nonce=nonce, sender_address=self.address, ) + max_fee = await self._get_max_fee(transaction, max_fee, auto_estimate) + return _add_max_fee_to_transaction(transaction, max_fee) async def _prepare_invoke_v3( @@ -405,7 +407,7 @@ async def sign_invoke_v1( async def sign_outside_execution_call( self, calls: Calls, - execution_time_bounds: ExecutionTimeBounds, + execution_time_bounds: OutsideExecutionTimeBounds, *, caller: AddressRepresentation = ANY_CALLER, nonce: Optional[int] = None, @@ -985,6 +987,7 @@ def _parse_calls_cairo_v1(calls: Iterable[Call]) -> List[Dict]: calldata=ArraySerializer(_felt_serializer), ) ) + _execute_payload_serializer_v0 = PayloadSerializer( OrderedDict( call_array=ArraySerializer(_call_description_cairo_v0), diff --git a/starknet_py/net/account/base_account.py b/starknet_py/net/account/base_account.py index 77d10e455..eff0d4564 100644 --- a/starknet_py/net/account/base_account.py +++ b/starknet_py/net/account/base_account.py @@ -7,7 +7,7 @@ Call, Calls, EstimatedFee, - ExecutionTimeBounds, + OutsideExecutionTimeBounds, Hash, ResourceBounds, SentTransactionResponse, @@ -48,7 +48,7 @@ async def supports_interface( async def sign_outside_execution_call( self, calls: Calls, - execution_time_bounds: ExecutionTimeBounds, + execution_time_bounds: OutsideExecutionTimeBounds, *, caller: AddressRepresentation = ANY_CALLER, nonce: Optional[int] = None, diff --git a/starknet_py/net/client_models.py b/starknet_py/net/client_models.py index 4a2690d14..3e2540b88 100644 --- a/starknet_py/net/client_models.py +++ b/starknet_py/net/client_models.py @@ -31,7 +31,6 @@ # pylint: disable=too-many-lines - Hash = Union[int, str] Tag = Literal["pending", "latest"] @@ -118,7 +117,7 @@ def init_with_zeros(): @dataclass -class ExecutionTimeBounds: +class OutsideExecutionTimeBounds: """ Dataclass representing time bounds within which outside execution transaction is valid and allowed to be executed. diff --git a/starknet_py/tests/e2e/account/external_execution_test.py b/starknet_py/tests/e2e/account/external_execution_test.py index 6ccd92d55..d519d3bb5 100644 --- a/starknet_py/tests/e2e/account/external_execution_test.py +++ b/starknet_py/tests/e2e/account/external_execution_test.py @@ -5,7 +5,7 @@ from starknet_py.constants import ANY_CALLER, OutsideExecutionInterfaceID from starknet_py.hash.selector import get_selector_from_name from starknet_py.net.account.account import BaseAccount -from starknet_py.net.client_models import Call, ExecutionTimeBounds +from starknet_py.net.client_models import Call, OutsideExecutionTimeBounds from starknet_py.tests.e2e.fixtures.constants import MAX_FEE from starknet_py.transaction_errors import TransactionRevertedError @@ -14,13 +14,9 @@ async def test_argent_account_snip9_compatibility( argent_account: BaseAccount, ): - result = await argent_account.supports_interface( - OutsideExecutionInterfaceID.V1 - ) + result = await argent_account.supports_interface(OutsideExecutionInterfaceID.V1) assert result is True - result = await argent_account.supports_interface( - OutsideExecutionInterfaceID.V2 - ) + result = await argent_account.supports_interface(OutsideExecutionInterfaceID.V2) assert result is False @@ -32,12 +28,8 @@ async def test_account_outside_execution_any_caller( assert any( [ - await argent_account.supports_interface( - OutsideExecutionInterfaceID.V1 - ), - await argent_account.supports_interface( - OutsideExecutionInterfaceID.V2 - ), + await argent_account.supports_interface(OutsideExecutionInterfaceID.V1), + await argent_account.supports_interface(OutsideExecutionInterfaceID.V2), ] ) @@ -53,7 +45,7 @@ async def test_account_outside_execution_any_caller( put_call, put_call, ], - execution_time_bounds=ExecutionTimeBounds( + execution_time_bounds=OutsideExecutionTimeBounds( execute_after=datetime.datetime.now() - datetime.timedelta(hours=1), execute_before=datetime.datetime.now() + datetime.timedelta(hours=1), ), @@ -72,12 +64,8 @@ async def test_account_outside_execution_for_invalid_caller( ): assert any( [ - await argent_account.supports_interface( - OutsideExecutionInterfaceID.V1 - ), - await argent_account.supports_interface( - OutsideExecutionInterfaceID.V2 - ), + await argent_account.supports_interface(OutsideExecutionInterfaceID.V1), + await argent_account.supports_interface(OutsideExecutionInterfaceID.V2), ] ) @@ -93,7 +81,7 @@ async def test_account_outside_execution_for_invalid_caller( put_call, put_call, ], - execution_time_bounds=ExecutionTimeBounds( + execution_time_bounds=OutsideExecutionTimeBounds( execute_after=datetime.datetime.now() - datetime.timedelta(hours=1), execute_before=datetime.datetime.now() + datetime.timedelta(hours=1), ), @@ -116,12 +104,8 @@ async def test_account_outside_execution_for_impossible_timebounds( assert any( [ - await argent_account.supports_interface( - OutsideExecutionInterfaceID.V1 - ), - await argent_account.supports_interface( - OutsideExecutionInterfaceID.V2 - ), + await argent_account.supports_interface(OutsideExecutionInterfaceID.V1), + await argent_account.supports_interface(OutsideExecutionInterfaceID.V2), ] ) @@ -137,7 +121,7 @@ async def test_account_outside_execution_for_impossible_timebounds( put_call, put_call, ], - execution_time_bounds=ExecutionTimeBounds( + execution_time_bounds=OutsideExecutionTimeBounds( execute_after=datetime.datetime.now() - datetime.timedelta(days=10), execute_before=datetime.datetime.now() - datetime.timedelta(days=9), ), From 51f0f7f93b7d1178bb8fa3cb088206d524baa558 Mon Sep 17 00:00:00 2001 From: baitcode Date: Sun, 15 Dec 2024 20:58:39 +0300 Subject: [PATCH 33/51] more fixes --- starknet_py/net/account/account.py | 6 +++--- starknet_py/net/account/base_account.py | 2 +- .../e2e/docs/guide/test_account_sign_outside_transaction.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/starknet_py/net/account/account.py b/starknet_py/net/account/account.py index 0efb4986a..d17dba029 100644 --- a/starknet_py/net/account/account.py +++ b/starknet_py/net/account/account.py @@ -24,9 +24,9 @@ Call, Calls, EstimatedFee, - OutsideExecutionTimeBounds, Hash, OutsideExecution, + OutsideExecutionTimeBounds, ResourceBounds, ResourceBoundsMapping, SentTransactionResponse, @@ -224,9 +224,9 @@ async def _prepare_invoke( nonce=nonce, sender_address=self.address, ) - + max_fee = await self._get_max_fee(transaction, max_fee, auto_estimate) - + return _add_max_fee_to_transaction(transaction, max_fee) async def _prepare_invoke_v3( diff --git a/starknet_py/net/account/base_account.py b/starknet_py/net/account/base_account.py index eff0d4564..86e40c722 100644 --- a/starknet_py/net/account/base_account.py +++ b/starknet_py/net/account/base_account.py @@ -7,8 +7,8 @@ Call, Calls, EstimatedFee, - OutsideExecutionTimeBounds, Hash, + OutsideExecutionTimeBounds, ResourceBounds, SentTransactionResponse, Tag, diff --git a/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py b/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py index e6c9136a0..e3861c6eb 100644 --- a/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py +++ b/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py @@ -16,7 +16,7 @@ async def test_account_outside_execution_any_caller( from starknet_py.constants import ANY_CALLER from starknet_py.hash.selector import get_selector_from_name - from starknet_py.net.client_models import Call, ExecutionTimeBounds + from starknet_py.net.client_models import Call, OutsideExecutionTimeBounds # Create a call to put value 20 under key 20. That will be executed # as part of outside execution. @@ -34,7 +34,7 @@ async def test_account_outside_execution_any_caller( calls=[ put_call, ], - execution_time_bounds=ExecutionTimeBounds( + execution_time_bounds=OutsideExecutionTimeBounds( execute_after=datetime.datetime.now() - datetime.timedelta(hours=1), execute_before=datetime.datetime.now() + datetime.timedelta(hours=1), ), From a449d8c137387cb88ced479a6197280980ce4c08 Mon Sep 17 00:00:00 2001 From: baitcode Date: Sun, 15 Dec 2024 20:59:46 +0300 Subject: [PATCH 34/51] rename --- .../{external_execution_test.py => outside_execution_test.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename starknet_py/tests/e2e/account/{external_execution_test.py => outside_execution_test.py} (100%) diff --git a/starknet_py/tests/e2e/account/external_execution_test.py b/starknet_py/tests/e2e/account/outside_execution_test.py similarity index 100% rename from starknet_py/tests/e2e/account/external_execution_test.py rename to starknet_py/tests/e2e/account/outside_execution_test.py From a4e75c478c010ac9a6e7dd088f7222307c875eef Mon Sep 17 00:00:00 2001 From: baitcode Date: Sun, 15 Dec 2024 23:09:42 +0300 Subject: [PATCH 35/51] Fix --- starknet_py/net/models/typed_data.py | 12 +++++++++--- .../tests/e2e/account/outside_execution_test.py | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/starknet_py/net/models/typed_data.py b/starknet_py/net/models/typed_data.py index 67f3125a6..8b2259428 100644 --- a/starknet_py/net/models/typed_data.py +++ b/starknet_py/net/models/typed_data.py @@ -2,19 +2,25 @@ TypedDict structures for TypedData """ +import sys from typing import Any, Dict, List, Optional, TypedDict - from starknet_py.net.schemas.common import Revision +if sys.version_info < (3, 11): + from typing_extensions import NotRequired +else: + from typing import NotRequired + + -class ParameterDict(TypedDict, total=False): +class ParameterDict(TypedDict): """ TypedDict representing a Parameter object """ name: str type: str - contains: Optional[str] + contains: NotRequired[str] class DomainDict(TypedDict): diff --git a/starknet_py/tests/e2e/account/outside_execution_test.py b/starknet_py/tests/e2e/account/outside_execution_test.py index d519d3bb5..097c51860 100644 --- a/starknet_py/tests/e2e/account/outside_execution_test.py +++ b/starknet_py/tests/e2e/account/outside_execution_test.py @@ -11,7 +11,7 @@ @pytest.mark.asyncio -async def test_argent_account_snip9_compatibility( +async def test_argent_account_outside_execution_compatibility( argent_account: BaseAccount, ): result = await argent_account.supports_interface(OutsideExecutionInterfaceID.V1) From e3a3bed3e4a468ccf86e3f58ac31cbcb7ce7e7cc Mon Sep 17 00:00:00 2001 From: baitcode Date: Sun, 15 Dec 2024 23:12:03 +0300 Subject: [PATCH 36/51] linter --- starknet_py/net/models/typed_data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/starknet_py/net/models/typed_data.py b/starknet_py/net/models/typed_data.py index 8b2259428..972391b29 100644 --- a/starknet_py/net/models/typed_data.py +++ b/starknet_py/net/models/typed_data.py @@ -4,6 +4,7 @@ import sys from typing import Any, Dict, List, Optional, TypedDict + from starknet_py.net.schemas.common import Revision if sys.version_info < (3, 11): @@ -12,7 +13,6 @@ from typing import NotRequired - class ParameterDict(TypedDict): """ TypedDict representing a Parameter object From 0ac184535774c5a6f991972357d0ff01c9d5e838 Mon Sep 17 00:00:00 2001 From: Ilia Batii Date: Sun, 15 Dec 2024 21:28:22 +0000 Subject: [PATCH 37/51] Update starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py Co-authored-by: Franciszek Job <54181625+franciszekjob@users.noreply.github.com> --- .../e2e/docs/guide/test_account_sign_outside_transaction.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py b/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py index e3861c6eb..3ab81dc4c 100644 --- a/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py +++ b/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py @@ -18,13 +18,13 @@ async def test_account_outside_execution_any_caller( from starknet_py.hash.selector import get_selector_from_name from starknet_py.net.client_models import Call, OutsideExecutionTimeBounds - # Create a call to put value 20 under key 20. That will be executed + # Create a call to put value 1 under key 100. That will be executed # as part of outside execution. put_call = Call( to_addr=map_contract.address, selector=get_selector_from_name("put"), - calldata=[20, 20], + calldata=[1, 100], ) # Create a special signed execution call. This call can now be executed by From 8d98dbd1f6dc2da498d70544dcc882896ffca75a Mon Sep 17 00:00:00 2001 From: Ilia Batii Date: Sun, 15 Dec 2024 21:28:45 +0000 Subject: [PATCH 38/51] Update starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py Co-authored-by: Franciszek Job <54181625+franciszekjob@users.noreply.github.com> --- .../e2e/docs/guide/test_account_sign_outside_transaction.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py b/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py index 3ab81dc4c..a142263bf 100644 --- a/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py +++ b/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py @@ -28,8 +28,8 @@ async def test_account_outside_execution_any_caller( ) # Create a special signed execution call. This call can now be executed by - # the caller specified. In this case, caller is ANY_CALLER, a special constant - # that allows any caller to execute the call. + # the specified caller. In this case, caller is ANY_CALLER, a special constant + # that allows anyone to execute the call. call = await argent_account.sign_outside_execution_call( calls=[ put_call, From dfdc2527e79e4ccafe98a22bd4f67cdfc9d12946 Mon Sep 17 00:00:00 2001 From: Ilia Batii Date: Sun, 15 Dec 2024 21:28:59 +0000 Subject: [PATCH 39/51] Update starknet_py/net/account/base_account.py Co-authored-by: Franciszek Job <54181625+franciszekjob@users.noreply.github.com> --- starknet_py/net/account/base_account.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/starknet_py/net/account/base_account.py b/starknet_py/net/account/base_account.py index 86e40c722..36838be24 100644 --- a/starknet_py/net/account/base_account.py +++ b/starknet_py/net/account/base_account.py @@ -61,7 +61,7 @@ async def sign_outside_execution_call( :param execution_time_bounds: Execution time bounds for the call. :param caller: Address of the caller. IMPORTANT! By default it is ANY_CALLER. :param nonce: Nonce for the transaction. Is populated automatically if not provided. - :param interface_version: SNIP-9 interface version. Method will check which version account + :param interface_version: Outside execution interface version. Method will check which version account supports and use the highest one and populate the value. """ From c97df0f549634b53afdd92a3c774959656e788a4 Mon Sep 17 00:00:00 2001 From: baitcode Date: Mon, 16 Dec 2024 15:03:34 +0300 Subject: [PATCH 40/51] a bit more review comment fixes --- starknet_py/net/client_models.py | 1 + .../tests/e2e/account/outside_execution_test.py | 15 +++++---------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/starknet_py/net/client_models.py b/starknet_py/net/client_models.py index 3e2540b88..a9394b1a9 100644 --- a/starknet_py/net/client_models.py +++ b/starknet_py/net/client_models.py @@ -1141,6 +1141,7 @@ class OutsideExecution: execute_before: int calls: List[Call] + # TODO(#1537): Use serialiser to convert to ABI dict. def to_abi_dict(self) -> Dict: """ Returns a dictionary that can be serialized (compiled) into calldata diff --git a/starknet_py/tests/e2e/account/outside_execution_test.py b/starknet_py/tests/e2e/account/outside_execution_test.py index 097c51860..89c50786f 100644 --- a/starknet_py/tests/e2e/account/outside_execution_test.py +++ b/starknet_py/tests/e2e/account/outside_execution_test.py @@ -6,7 +6,6 @@ from starknet_py.hash.selector import get_selector_from_name from starknet_py.net.account.account import BaseAccount from starknet_py.net.client_models import Call, OutsideExecutionTimeBounds -from starknet_py.tests.e2e.fixtures.constants import MAX_FEE from starknet_py.transaction_errors import TransactionRevertedError @@ -52,7 +51,7 @@ async def test_account_outside_execution_any_caller( caller=ANY_CALLER, ) - tx = await argent_account.execute_v1(calls=[call], max_fee=MAX_FEE) + tx = await argent_account.execute_v3(calls=[call], auto_estimate=True) await argent_account.client.wait_for_tx(tx.transaction_hash) @@ -88,7 +87,7 @@ async def test_account_outside_execution_for_invalid_caller( caller=account.address, ) - tx = await argent_account.execute_v1(calls=[call], max_fee=MAX_FEE) + tx = await argent_account.execute_v3(calls=[call], auto_estimate=True) with pytest.raises(TransactionRevertedError) as err: await argent_account.client.wait_for_tx(tx.transaction_hash) @@ -97,7 +96,7 @@ async def test_account_outside_execution_for_invalid_caller( @pytest.mark.asyncio -async def test_account_outside_execution_for_impossible_timebounds( +async def test_account_outside_execution_for_impossible_time_bounds( argent_account: BaseAccount, map_contract, ): @@ -116,11 +115,7 @@ async def test_account_outside_execution_for_impossible_timebounds( ) call = await argent_account.sign_outside_execution_call( - calls=[ - put_call, - put_call, - put_call, - ], + calls=[put_call], execution_time_bounds=OutsideExecutionTimeBounds( execute_after=datetime.datetime.now() - datetime.timedelta(days=10), execute_before=datetime.datetime.now() - datetime.timedelta(days=9), @@ -128,7 +123,7 @@ async def test_account_outside_execution_for_impossible_timebounds( caller=ANY_CALLER, ) - tx = await argent_account.execute_v1(calls=[call], max_fee=MAX_FEE) + tx = await argent_account.execute_v3(calls=[call], auto_estimate=True) with pytest.raises(TransactionRevertedError) as err: await argent_account.client.wait_for_tx(tx.transaction_hash) From 86e8f84485b26732f852f955917b2e3457b9a84c Mon Sep 17 00:00:00 2001 From: baitcode Date: Mon, 16 Dec 2024 15:20:41 +0300 Subject: [PATCH 41/51] revert execute_v1 --- starknet_py/tests/e2e/account/outside_execution_test.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/starknet_py/tests/e2e/account/outside_execution_test.py b/starknet_py/tests/e2e/account/outside_execution_test.py index 89c50786f..04e10f8c8 100644 --- a/starknet_py/tests/e2e/account/outside_execution_test.py +++ b/starknet_py/tests/e2e/account/outside_execution_test.py @@ -51,7 +51,7 @@ async def test_account_outside_execution_any_caller( caller=ANY_CALLER, ) - tx = await argent_account.execute_v3(calls=[call], auto_estimate=True) + tx = await argent_account.execute_v1(calls=[call], auto_estimate=True) await argent_account.client.wait_for_tx(tx.transaction_hash) @@ -87,7 +87,7 @@ async def test_account_outside_execution_for_invalid_caller( caller=account.address, ) - tx = await argent_account.execute_v3(calls=[call], auto_estimate=True) + tx = await argent_account.execute_v1(calls=[call], auto_estimate=True) with pytest.raises(TransactionRevertedError) as err: await argent_account.client.wait_for_tx(tx.transaction_hash) @@ -123,7 +123,7 @@ async def test_account_outside_execution_for_impossible_time_bounds( caller=ANY_CALLER, ) - tx = await argent_account.execute_v3(calls=[call], auto_estimate=True) + tx = await argent_account.execute_v1(calls=[call], auto_estimate=True) with pytest.raises(TransactionRevertedError) as err: await argent_account.client.wait_for_tx(tx.transaction_hash) From 4b0b0a2b9d838775f41c16e52c5f8ef3dee0bba5 Mon Sep 17 00:00:00 2001 From: baitcode Date: Mon, 16 Dec 2024 15:40:51 +0300 Subject: [PATCH 42/51] remove auto fee estimation --- starknet_py/tests/e2e/account/outside_execution_test.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/starknet_py/tests/e2e/account/outside_execution_test.py b/starknet_py/tests/e2e/account/outside_execution_test.py index 04e10f8c8..e34ac2d9c 100644 --- a/starknet_py/tests/e2e/account/outside_execution_test.py +++ b/starknet_py/tests/e2e/account/outside_execution_test.py @@ -6,6 +6,7 @@ from starknet_py.hash.selector import get_selector_from_name from starknet_py.net.account.account import BaseAccount from starknet_py.net.client_models import Call, OutsideExecutionTimeBounds +from starknet_py.tests.e2e.fixtures.constants import MAX_FEE from starknet_py.transaction_errors import TransactionRevertedError @@ -51,7 +52,7 @@ async def test_account_outside_execution_any_caller( caller=ANY_CALLER, ) - tx = await argent_account.execute_v1(calls=[call], auto_estimate=True) + tx = await argent_account.execute_v1(calls=[call], max_fee=MAX_FEE) await argent_account.client.wait_for_tx(tx.transaction_hash) @@ -87,7 +88,7 @@ async def test_account_outside_execution_for_invalid_caller( caller=account.address, ) - tx = await argent_account.execute_v1(calls=[call], auto_estimate=True) + tx = await argent_account.execute_v1(calls=[call], max_fee=MAX_FEE) with pytest.raises(TransactionRevertedError) as err: await argent_account.client.wait_for_tx(tx.transaction_hash) @@ -123,7 +124,7 @@ async def test_account_outside_execution_for_impossible_time_bounds( caller=ANY_CALLER, ) - tx = await argent_account.execute_v1(calls=[call], auto_estimate=True) + tx = await argent_account.execute_v1(calls=[call], max_fee=MAX_FEE) with pytest.raises(TransactionRevertedError) as err: await argent_account.client.wait_for_tx(tx.transaction_hash) From ee9a0640a4a5086e24278d9743a7e90a0a3d0946 Mon Sep 17 00:00:00 2001 From: Ilia Batii Date: Mon, 16 Dec 2024 14:47:16 +0000 Subject: [PATCH 43/51] Update docs/guide/account_and_client.rst Co-authored-by: Franciszek Job <54181625+franciszekjob@users.noreply.github.com> --- docs/guide/account_and_client.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/guide/account_and_client.rst b/docs/guide/account_and_client.rst index 22f1661c6..baf28f75c 100644 --- a/docs/guide/account_and_client.rst +++ b/docs/guide/account_and_client.rst @@ -46,10 +46,12 @@ Account also provides a way of creating signed transaction without sending them. :language: python :dedent: 4 -Creating "Outside transaction" and executing it. `SNIP-9 `_ ---------------------------------------------------------------------------------------------------------------------------- +Outside execution +----------------- -Account also provides a way of creating a call and signing to allow for another account (caller) to execute it later on original account behalf. Account does not need to funded with tokens for transaction to execute as caller will pay the execution fee. +Outside execution allows a protocol to submit a transaction on behalf of another account. This feature is implemented according to `SNIP-9 `_. + +Account also provides a way of signing transaction which later can be execute by another account. Signer does not need to be funded with tokens as executor will pay the fee. .. codesnippet:: ../../starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py :language: python From 9776344696316550a11e6d889bf4074bcc398bd8 Mon Sep 17 00:00:00 2001 From: Ilia Batii Date: Mon, 16 Dec 2024 14:54:34 +0000 Subject: [PATCH 44/51] Update starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py Co-authored-by: Franciszek Job <54181625+franciszekjob@users.noreply.github.com> --- .../test_account_sign_outside_transaction.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py b/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py index a142263bf..815327dbb 100644 --- a/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py +++ b/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py @@ -18,31 +18,31 @@ async def test_account_outside_execution_any_caller( from starknet_py.hash.selector import get_selector_from_name from starknet_py.net.client_models import Call, OutsideExecutionTimeBounds - # Create a call to put value 1 under key 100. That will be executed - # as part of outside execution. - + # Create a call to put value 100 at key 1. put_call = Call( to_addr=map_contract.address, selector=get_selector_from_name("put"), calldata=[1, 100], ) - # Create a special signed execution call. This call can now be executed by - # the specified caller. In this case, caller is ANY_CALLER, a special constant - # that allows anyone to execute the call. - call = await argent_account.sign_outside_execution_call( + # Create an outside execution call. This call can now be executed by + # the specified caller. In this case, anyone will be able to execute it. + + # Note that signing account does not need to have any funds to sign the transaction. + call = await argent_account.sign_outside_transaction( calls=[ put_call, ], + # The transaction can be executed in specified timeframe. execution_time_bounds=OutsideExecutionTimeBounds( execute_after=datetime.datetime.now() - datetime.timedelta(hours=1), execute_before=datetime.datetime.now() + datetime.timedelta(hours=1), ), + # Use ANY_CALLER, a special constant that allows anyone to execute the call. caller=ANY_CALLER, ) - # Execute the call as a normal invoke transaction - # can be executed from any account specified in the caller field + # Now, if you're in specified timeframe, you can perform the outside execution by another account. tx = await account.execute_v1(calls=[call], max_fee=int(1e18)) await account.client.wait_for_tx(tx.transaction_hash) From ceaa8ca6eabd2f19ad8f30f05312e64baf50119b Mon Sep 17 00:00:00 2001 From: baitcode Date: Mon, 16 Dec 2024 18:23:48 +0300 Subject: [PATCH 45/51] import fix --- starknet_py/net/account/account.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/starknet_py/net/account/account.py b/starknet_py/net/account/account.py index 8b1233bd8..ee1c8cfca 100644 --- a/starknet_py/net/account/account.py +++ b/starknet_py/net/account/account.py @@ -51,9 +51,9 @@ from starknet_py.net.signer import BaseSigner from starknet_py.net.signer.key_pair import KeyPair from starknet_py.net.signer.stark_curve_signer import StarkCurveSigner -from starknet_py.serialization.data_serializers.array_serializer import ArraySerializer -from starknet_py.serialization.data_serializers.felt_serializer import FeltSerializer -from starknet_py.serialization.data_serializers.payload_serializer import ( +from starknet_py.serialization.data_serializers import ( + ArraySerializer, + FeltSerializer, PayloadSerializer, StructSerializer, UintSerializer, From a9aa417170e04b4b5ca8e7dd3bcd2be8e13261f5 Mon Sep 17 00:00:00 2001 From: baitcode Date: Mon, 16 Dec 2024 18:30:14 +0300 Subject: [PATCH 46/51] fix doctest --- .../e2e/docs/guide/test_account_sign_outside_transaction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py b/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py index 815327dbb..0d9fc4bf8 100644 --- a/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py +++ b/starknet_py/tests/e2e/docs/guide/test_account_sign_outside_transaction.py @@ -29,7 +29,7 @@ async def test_account_outside_execution_any_caller( # the specified caller. In this case, anyone will be able to execute it. # Note that signing account does not need to have any funds to sign the transaction. - call = await argent_account.sign_outside_transaction( + call = await argent_account.sign_outside_execution_call( calls=[ put_call, ], From df4079dfcef69f6b9c876a09ca0956fd8903efa7 Mon Sep 17 00:00:00 2001 From: Ilia Batii Date: Mon, 16 Dec 2024 16:32:53 +0000 Subject: [PATCH 47/51] Update starknet_py/tests/e2e/account/outside_execution_test.py Co-authored-by: Franciszek Job <54181625+franciszekjob@users.noreply.github.com> --- starknet_py/tests/e2e/account/outside_execution_test.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/starknet_py/tests/e2e/account/outside_execution_test.py b/starknet_py/tests/e2e/account/outside_execution_test.py index e34ac2d9c..5c025a8de 100644 --- a/starknet_py/tests/e2e/account/outside_execution_test.py +++ b/starknet_py/tests/e2e/account/outside_execution_test.py @@ -78,8 +78,6 @@ async def test_account_outside_execution_for_invalid_caller( call = await argent_account.sign_outside_execution_call( calls=[ put_call, - put_call, - put_call, ], execution_time_bounds=OutsideExecutionTimeBounds( execute_after=datetime.datetime.now() - datetime.timedelta(hours=1), From 817d6794f7894564d1e5dbc1ddb80fc5f291704a Mon Sep 17 00:00:00 2001 From: Ilia Batii Date: Mon, 16 Dec 2024 16:33:01 +0000 Subject: [PATCH 48/51] Update starknet_py/tests/e2e/account/outside_execution_test.py Co-authored-by: Franciszek Job <54181625+franciszekjob@users.noreply.github.com> --- starknet_py/tests/e2e/account/outside_execution_test.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/starknet_py/tests/e2e/account/outside_execution_test.py b/starknet_py/tests/e2e/account/outside_execution_test.py index 5c025a8de..00f24f6d3 100644 --- a/starknet_py/tests/e2e/account/outside_execution_test.py +++ b/starknet_py/tests/e2e/account/outside_execution_test.py @@ -42,8 +42,6 @@ async def test_account_outside_execution_any_caller( call = await argent_account.sign_outside_execution_call( calls=[ put_call, - put_call, - put_call, ], execution_time_bounds=OutsideExecutionTimeBounds( execute_after=datetime.datetime.now() - datetime.timedelta(hours=1), From b931c5c1a5373fd613782931554e8c3c3788b2e8 Mon Sep 17 00:00:00 2001 From: baitcode Date: Mon, 16 Dec 2024 19:40:43 +0300 Subject: [PATCH 49/51] changelog --- docs/migration_guide.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/migration_guide.rst b/docs/migration_guide.rst index 12d933a0f..0c827803a 100644 --- a/docs/migration_guide.rst +++ b/docs/migration_guide.rst @@ -12,6 +12,10 @@ Migration guide 1. Added :class:`NonZeroType` in order to fix parsing ABI which contains Cairo`s `core::zeroable::NonZero `_ +.. currentmodule:: starknet_py.net + +2. Added (SNIP-9)[https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-9.md] support to :class:`account.account.Account`. Now it's possible to create a :class:`client_models.Call` for outside execution using :meth:`account.account.Account.sign_outside_execution_call`. + ****************************** 0.24.3 Migration guide ****************************** From e5e76e461cbeaee6b25ec13615f0fb79f2f61336 Mon Sep 17 00:00:00 2001 From: Ilia Batii Date: Mon, 16 Dec 2024 17:49:39 +0000 Subject: [PATCH 50/51] Update docs/migration_guide.rst Co-authored-by: Franciszek Job <54181625+franciszekjob@users.noreply.github.com> --- docs/migration_guide.rst | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/docs/migration_guide.rst b/docs/migration_guide.rst index 0c827803a..671108229 100644 --- a/docs/migration_guide.rst +++ b/docs/migration_guide.rst @@ -12,10 +12,7 @@ Migration guide 1. Added :class:`NonZeroType` in order to fix parsing ABI which contains Cairo`s `core::zeroable::NonZero `_ -.. currentmodule:: starknet_py.net - -2. Added (SNIP-9)[https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-9.md] support to :class:`account.account.Account`. Now it's possible to create a :class:`client_models.Call` for outside execution using :meth:`account.account.Account.sign_outside_execution_call`. - +2. Added `SNIP-9 `_ support to :class:`~starknet_py.net.account.account.Account`. Now it's possible to create a :class:`~starknet_py.net.client_models.Call` for outside execution using :meth:`~starknet_py.net.account.account.Account.sign_outside_execution_call`. ****************************** 0.24.3 Migration guide ****************************** From 7434a2507d98ed5138f7890f8d676832758996de Mon Sep 17 00:00:00 2001 From: baitcode Date: Mon, 16 Dec 2024 21:02:21 +0300 Subject: [PATCH 51/51] fixing fixes --- docs/migration_guide.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/migration_guide.rst b/docs/migration_guide.rst index 671108229..3132611d8 100644 --- a/docs/migration_guide.rst +++ b/docs/migration_guide.rst @@ -13,6 +13,7 @@ Migration guide 1. Added :class:`NonZeroType` in order to fix parsing ABI which contains Cairo`s `core::zeroable::NonZero `_ 2. Added `SNIP-9 `_ support to :class:`~starknet_py.net.account.account.Account`. Now it's possible to create a :class:`~starknet_py.net.client_models.Call` for outside execution using :meth:`~starknet_py.net.account.account.Account.sign_outside_execution_call`. + ****************************** 0.24.3 Migration guide ******************************