Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
e7929d7
add cairo_version property, remove it from arguments in methods
tomek0123456789 Sep 14, 2023
6907be0
change cairo_version
tomek0123456789 Sep 15, 2023
5d3dcb3
update Account.deploy_account
tomek0123456789 Sep 15, 2023
278dca0
add tests
tomek0123456789 Sep 15, 2023
9b5bd4a
add precompiled contracts
tomek0123456789 Sep 15, 2023
b093c76
update docs
tomek0123456789 Sep 15, 2023
050ab91
deprecate cairo_version parameter from previous impl
tomek0123456789 Sep 18, 2023
102f8ce
Merge branch 'development' into tomek/1182-cairo1-accounts
tomek0123456789 Sep 18, 2023
47a4be4
change previous parameter into Optional
tomek0123456789 Sep 18, 2023
f139242
update BaseAccount interface
tomek0123456789 Sep 18, 2023
c194af7
update migration guide
tomek0123456789 Sep 18, 2023
a9eb581
update docs code snippets
tomek0123456789 Sep 18, 2023
fcb137b
check docs
tomek0123456789 Sep 18, 2023
0d4d114
formatting
tomek0123456789 Sep 18, 2023
e71f160
docs
tomek0123456789 Sep 18, 2023
0f2cd7a
fix devnet version on windows checks
tomek0123456789 Sep 18, 2023
6e26024
docstrings once again
tomek0123456789 Sep 18, 2023
8093f78
add todos
tomek0123456789 Sep 18, 2023
184e853
add test for codecov
tomek0123456789 Sep 18, 2023
4bdd67a
formatting
tomek0123456789 Sep 18, 2023
10147b7
add a helper comment
tomek0123456789 Sep 19, 2023
a7d05da
remove cairo_version from Account constructor, change cairo_version p…
tomek0123456789 Sep 19, 2023
60e9831
remove unnecessary pyright comment
tomek0123456789 Sep 19, 2023
d20c19d
change cairo_version into async property
tomek0123456789 Sep 19, 2023
3accdfe
fix tests
tomek0123456789 Sep 19, 2023
5c494b8
another tests fix
tomek0123456789 Sep 19, 2023
bc669b3
another tests fix
tomek0123456789 Sep 19, 2023
8a4ceed
woops
tomek0123456789 Sep 19, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ jobs:
apt-get install -y python3-pip
sudo apt install -y libgmp3-dev
sudo apt-get install -y git
pip3 install git+https://github.com/0xSpaceShard/starknet-devnet.git@v0.5.5
pip3 install git+https://github.com/0xSpaceShard/starknet-devnet.git@v0.6.2

# ====================== SETUP PYTHON ====================== #

Expand Down Expand Up @@ -492,7 +492,7 @@ jobs:
apt-get install -y python3-pip
sudo apt install -y libgmp3-dev
sudo apt-get install -y git
pip3 install git+https://github.com/0xSpaceShard/starknet-devnet.git@v0.5.5
pip3 install git+https://github.com/0xSpaceShard/starknet-devnet.git@v0.6.2

# ====================== SETUP PYTHON ====================== #

Expand Down
14 changes: 14 additions & 0 deletions docs/migration_guide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ Migration guide
**********************

Version 0.18.2 of **starknet.py** comes with support of `RPC v0.4.0 <https://github.com/starkware-libs/starknet-specs/releases/tag/v0.4.0>`_ Trace API!
Additionally, you can now `properly` use Cairo1 accounts! ``starknet.py`` automatically checks if your account is in Cairo1 and
sets the calldata encoding accordingly.

0.18.2 Targeted versions
------------------------
Expand All @@ -21,6 +23,14 @@ Version 0.18.2 of **starknet.py** comes with support of `RPC v0.4.0 <https://git
1. :meth:`Client.get_block_traces` has been renamed to :meth:`Client.trace_block_transactions` in order to match RPC specification.


0.18.2 Deprecations
-------------------

.. currentmodule:: starknet_py.net.account.account

1. ``cairo_version`` parameter in :meth:`Account.sign_invoke_transaction` and :meth:`Account.execute` has been deprecated.


0.18.2 Minor changes
--------------------

Expand All @@ -30,6 +40,10 @@ Version 0.18.2 of **starknet.py** comes with support of `RPC v0.4.0 <https://git

2. ``include_block`` parameter in :meth:`GatewayClient.get_state_update` now works on gateway mainnet.

.. currentmodule:: starknet_py.net.account.account

3. :class:`BaseAccount` interface and :class:`Account` now have an additional **async** property - ``cairo_version``.


0.18.2 Development-related changes
----------------------------------
Expand Down
53 changes: 46 additions & 7 deletions starknet_py/net/account/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,11 @@
EstimatedFee,
Hash,
SentTransactionResponse,
SierraContractClass,
Tag,
)
from starknet_py.net.full_node_client import FullNodeClient
from starknet_py.net.gateway_client import GatewayClient
from starknet_py.net.models import AddressRepresentation, StarknetChainId, parse_address
from starknet_py.net.models.transaction import (
AccountTransaction,
Expand Down Expand Up @@ -74,6 +77,7 @@ def __init__(
"""
self._address = parse_address(address)
self._client = client
self._cairo_version = None

if signer is not None and key_pair is not None:
raise ValueError("Arguments signer and key_pair are mutually exclusive.")
Expand All @@ -96,6 +100,23 @@ def __init__(
def address(self) -> int:
return self._address

@property
async def cairo_version(self) -> int:
if self._cairo_version is None:
if isinstance(self._client, GatewayClient):
contract_class = await self._client.get_full_contract(
contract_address=self._address
)
else:
assert isinstance(self._client, FullNodeClient)
contract_class = await self._client.get_class_at(
contract_address=self._address
)
self._cairo_version = (
1 if isinstance(contract_class, SierraContractClass) else 0
)
return self._cairo_version

Choose a reason for hiding this comment

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

here you could do instead something like

@property
async def cairo_version(self) -> int:
    if self._cairo_version is None:
        # Get the class hash of self.address
        class_ = await self._client.get_class_at(self.address)
        self.cairo_version = 1 if isinstance(class, Sierra) else 0
    return self._cairo_version

Copy link
Contributor Author

@tomek0123456789 tomek0123456789 Sep 19, 2023

Choose a reason for hiding this comment

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

I'm not sure if an async property would be a good UI. However, we've got a sync interface for that, so something like this would work without awaiting:

@property
def cairo_version(self) -> int:
    if self._cairo_version is None:
        if isinstance(self._client, GatewayClient):
            contract_class = self._client.get_full_contract_sync(contract_address=self._address)
        else:
            contract_class = self.client.get_class_at_sync(contract_address=self._address)
        self._cairo_version = 1 if isinstance(contract_class, SierraContractClass) else 0
    return self._cairo_version

wdyt?
edit: this breaks a lot of things, but the same question applies, is an async property a good UI?

Choose a reason for hiding this comment

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

well, just see this comment now. Async property you're right may not have been the best thing, but with sync definitely as It's quite common to use property to cache a given computed/fetched data

there is no information regarding the internal implementation for the outside world, use you still use self.cairo_version


@property
def client(self) -> Client:
return self._client
Expand Down Expand Up @@ -137,7 +158,6 @@ async def _prepare_invoke(
nonce: Optional[int] = None,
max_fee: Optional[int] = None,
auto_estimate: bool = False,
cairo_version: int = 0,
) -> Invoke:
"""
Takes calls and creates Invoke from them.
Expand All @@ -150,7 +170,7 @@ async def _prepare_invoke(
if nonce is None:
nonce = await self.get_nonce()

if cairo_version == 1:
if await self.cairo_version == 1:
parsed_calls = _parse_calls_v2(ensure_iterable(calls))
wrapped_calldata = _execute_payload_serializer_v2.serialize(
{"calls": parsed_calls}
Expand Down Expand Up @@ -253,14 +273,22 @@ async def sign_invoke_transaction(
nonce: Optional[int] = None,
max_fee: Optional[int] = None,
auto_estimate: bool = False,
cairo_version: int = 0,
# TODO (#1184): remove that
cairo_version: Optional[int] = None,
) -> Invoke:
# TODO (#1184): remove that
if cairo_version is not None:
warnings.warn(
"Parameter 'cairo_version' has been deprecated. It is calculated automatically based on your account's "
"contract class.",
category=DeprecationWarning,
)

execute_tx = await self._prepare_invoke(
calls,
nonce=nonce,
max_fee=max_fee,
auto_estimate=auto_estimate,
cairo_version=cairo_version,
)
signature = self.signer.sign_transaction(execute_tx)
return _add_signature_to_transaction(execute_tx, signature)
Expand Down Expand Up @@ -387,14 +415,22 @@ async def execute(
nonce: Optional[int] = None,
max_fee: Optional[int] = None,
auto_estimate: bool = False,
cairo_version: int = 0,
# TODO (#1184): remove that
cairo_version: Optional[int] = None,
) -> SentTransactionResponse:
# TODO (#1184): remove that
if cairo_version is not None:
warnings.warn(
"Parameter 'cairo_version' has been deprecated. It is calculated automatically based on your account's "
"contract class.",
category=DeprecationWarning,
)

execute_transaction = await self.sign_invoke_transaction(
calls,
nonce=nonce,
max_fee=max_fee,
auto_estimate=auto_estimate,
cairo_version=cairo_version,
)
return await self._client.send_transaction(execute_transaction)

Expand Down Expand Up @@ -464,7 +500,10 @@ async def deploy_account(
)

account = Account(
address=address, client=client, key_pair=key_pair, chain=chain
address=address,
client=client,
key_pair=key_pair,
chain=chain,
)

deploy_account_tx = await account.sign_deploy_account_transaction(
Expand Down
23 changes: 23 additions & 0 deletions starknet_py/net/account/base_account.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ def address(self) -> int:
Get the address of the account
"""

@property
@abstractmethod
async def cairo_version(self) -> int:
"""
Get Cairo version of the account.
"""

@property
@abstractmethod
def client(self) -> Client:
Expand Down Expand Up @@ -102,6 +109,8 @@ async def sign_invoke_transaction(
nonce: Optional[int] = None,
max_fee: Optional[int] = None,
auto_estimate: bool = False,
# TODO (#1184): remove that and docstring
cairo_version: Optional[int] = None,
) -> Invoke:
"""
Takes calls and creates signed Invoke.
Expand All @@ -110,6 +119,12 @@ async def sign_invoke_transaction(
:param nonce: Nonce of the transaction.
:param max_fee: Max amount of Wei to be paid when executing transaction.
:param auto_estimate: Use automatic fee estimation, not recommend as it may lead to high costs.
:param cairo_version:
Cairo version of the account used.

.. deprecated:: 0.18.2
Parameter `cairo_version` has been deprecated - it is calculated automatically based on
your account's contract class.
:return: Invoke created from the calls.
"""

Expand Down Expand Up @@ -188,6 +203,8 @@ async def execute(
nonce: Optional[int] = None,
max_fee: Optional[int] = None,
auto_estimate: bool = False,
# TODO (#1184): remove that and docstring
cairo_version: Optional[int] = None,
) -> SentTransactionResponse:
"""
Takes calls and executes transaction.
Expand All @@ -196,6 +213,12 @@ async def execute(
:param nonce: Nonce of the transaction.
:param max_fee: Max amount of Wei to be paid when executing transaction.
:param auto_estimate: Use automatic fee estimation, not recommend as it may lead to high costs.
:param cairo_version:
Cairo version of the account used.

.. deprecated:: 0.18.2
Parameter `cairo_version` has been deprecated - it is calculated automatically based on
your account's contract class.
:return: SentTransactionResponse.
"""

Expand Down
105 changes: 105 additions & 0 deletions starknet_py/tests/e2e/account/account_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
DeployAccountTransaction,
DeployAccountTransactionResponse,
EstimatedFee,
SierraContractClass,
TransactionStatus,
)
from starknet_py.net.full_node_client import FullNodeClient
Expand Down Expand Up @@ -591,3 +592,107 @@ async def test_sign_transaction_custom_nonce(account, cairo1_hello_starknet_clas

assert invoke_tx.nonce == deploy_tx.nonce + 1
assert result == [new_balance]


@pytest.mark.asyncio
async def test_argent_cairo1_account_deploy(
full_node_client,
argent_cairo1_account_class_hash,
deploy_account_details_factory,
):
address, key_pair, salt, class_hash = await deploy_account_details_factory.get(
class_hash=argent_cairo1_account_class_hash, argent_calldata=True
)

deploy_result = await Account.deploy_account(
address=address,
class_hash=class_hash,
salt=salt,
key_pair=key_pair,
client=full_node_client,
constructor_calldata=[key_pair.public_key, 0],
chain=StarknetChainId.TESTNET,
max_fee=int(1e16),
)
await deploy_result.wait_for_acceptance()
account = deploy_result.account

assert isinstance(account, BaseAccount)
assert await account.cairo_version == 1

account_contract_class = await full_node_client.get_class_at(
contract_address=account.address, block_number="latest"
)

assert isinstance(account_contract_class, SierraContractClass)


@pytest.mark.asyncio
async def test_argent_cairo1_account_execute(
deployed_balance_contract,
argent_cairo1_account: BaseAccount,
):
# verify that initial balance is 0
get_balance_call = Call(
to_addr=deployed_balance_contract.address,
selector=get_selector_from_name("get_balance"),
calldata=[],
)
get_balance = await argent_cairo1_account.client.call_contract(
call=get_balance_call, block_number="latest"
)

assert get_balance[0] == 0

value = 20
increase_balance_by_20_call = Call(
to_addr=deployed_balance_contract.address,
selector=get_selector_from_name("increase_balance"),
calldata=[value],
)
execute = await argent_cairo1_account.execute(
calls=increase_balance_by_20_call, max_fee=int(1e16)
)
await argent_cairo1_account.client.wait_for_tx(tx_hash=execute.transaction_hash)
receipt = await argent_cairo1_account.client.get_transaction_receipt(
tx_hash=execute.transaction_hash
)

# TODO (#1179): devnet 0.3.0 still return STATUS instead of FINALITY_STATUS
assert receipt.status == TransactionStatus.ACCEPTED_ON_L2

# verify that the previous call was executed
get_balance_call = Call(
to_addr=deployed_balance_contract.address,
selector=get_selector_from_name("get_balance"),
calldata=[],
)
get_balance = await argent_cairo1_account.client.call_contract(
call=get_balance_call, block_number="latest"
)

assert get_balance[0] == value


# TODO (#1184): remove that
@pytest.mark.asyncio
async def test_cairo1_account_deprecations(
deployed_balance_contract,
argent_cairo1_account: BaseAccount,
):
call = Call(
to_addr=deployed_balance_contract.address,
selector=get_selector_from_name("increase_balance"),
calldata=[20],
)
with pytest.warns(
DeprecationWarning,
match="Parameter 'cairo_version' has been deprecated. It is calculated automatically based on your account's "
"contract class.",
):
_ = await argent_cairo1_account.execute(
calls=call, max_fee=int(1e16), cairo_version=1
)
_ = await argent_cairo1_account.sign_invoke_transaction(
calls=call, max_fee=int(1e16), cairo_version=1
)
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,7 @@ async def test_account_sign_without_execute(account, map_compiled_contract):
call = Call(to_addr=address, selector=selector, calldata=calldata)
invoke_transaction = await account.sign_invoke_transaction(call, max_fee=max_fee)
# Or if you're using Cairo1 account with new calldata encoding
invoke_transaction = await account.sign_invoke_transaction(
call, max_fee=max_fee, cairo_version=1
)
invoke_transaction = await account.sign_invoke_transaction(call, max_fee=max_fee)

# Create a signed Declare transaction
declare_transaction = await account.sign_declare_transaction(
Expand Down
8 changes: 4 additions & 4 deletions starknet_py/tests/e2e/docs/guide/test_custom_nonce.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@


@pytest.mark.asyncio
async def test_custom_nonce(full_node_client):
async def test_custom_nonce(full_node_account):
# pylint: disable=import-outside-toplevel
address = 0x1
client = full_node_client
private_key = 0x1
client = full_node_account.client
address = full_node_account.address
private_key = full_node_account.signer.key_pair.private_key

# docs: start
from starknet_py.net.account.account import Account
Expand Down
Loading