Skip to content
This repository has been archived by the owner on Jun 6, 2024. It is now read-only.

Commit

Permalink
chore: upgrade to 0.6 (#128)
Browse files Browse the repository at this point in the history
  • Loading branch information
dtdang authored Feb 1, 2023
1 parent 4b084c5 commit f90f96b
Show file tree
Hide file tree
Showing 11 changed files with 75 additions and 58 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/commitlint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
- name: Install Dependencies
run: |
python -m pip install --upgrade pip
pip install .[dev]
pip install commitizen
- name: Check commit history
run: cz check --rev-range $(git rev-list --all --reverse | head -1)..HEAD
5 changes: 3 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
.DS_Store

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
Expand Down Expand Up @@ -125,3 +123,6 @@ version.py
# IDEs stuff
.vscode

**/.DS_Store
*.swp
*.swo
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ repos:
- id: isort

- repo: https://github.com/psf/black
rev: 22.10.0
rev: 22.12.0
hooks:
- id: black
name: black
Expand Down
10 changes: 2 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ This only works if the account has been funded.
For convenience purposes, if you have another account with funds, you can use that account to fund the deployment of this one using the `--funder` option:

```bash
ape starknet account deploy <NEWLY-ALIAS> --network testnet --funder <EXISTING-FUNDED-ALIAS>
ape starknet accounts deploy <NEW-ALIAS> --network testnet --funder <EXISTING-FUNDED-ALIAS>
```

**NOTE**: You cannot use an Ethereum account to send funds to a Starknet account directly.
Expand Down Expand Up @@ -152,7 +152,7 @@ The `delete` command differs based on its values of `--network` and `--address`:

- To delete all deployments on a given network, use the `--network` option without `--address`.
- To delete all deployments matching an address (regardless of network), use the `--address` option without `--network`.
- To delete a deployment on a network with a particular address, use both `--network` and \`--address.
- To delete a deployment on a network with a particular address, use both `--network` and `--address`.
- Exclude both options to delete the whole account.

Note you can also specify multiple networks, the same as `import`.
Expand Down Expand Up @@ -385,12 +385,6 @@ Via an Environment Variable:
export ALPHA_MAINNET_WL_DEPLOY_TOKEN="MY_TOKEN"
```

Or, via the `--token` flag when deploying an account:

```bash
ape starknet accounts create MY_ACCOUNT --token MY_TOKEN
```

## Development

This project is in development and should be considered a beta.
Expand Down
29 changes: 20 additions & 9 deletions ape_starknet/accounts/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -538,7 +538,9 @@ def __repr__(self):
def add_deployment(self, network_name: str, contract_address: int, salt: int):
pass

def call(self, txn: TransactionAPI, send_everything: bool = False) -> ReceiptAPI:
def call(
self, txn: TransactionAPI, send_everything: bool = False, **signer_options
) -> ReceiptAPI:
if send_everything:
raise NotImplementedError("send_everything currently isn't implemented in Starknet.")

Expand Down Expand Up @@ -612,8 +614,9 @@ def deploy_account(

if balance < txn.max_fee:
# Use funder to provide the rest.
amount = txn.max_fee - balance
amount = ceil((txn.max_fee - balance) * FEE_MARGIN_OF_ESTIMATION)
self.tokens.transfer(funder, txn.contract_address, amount)
logger.success("Account has been funded.")

elif balance < txn.max_fee:
raise StarknetAccountsError("Unable to afford transaction.")
Expand All @@ -629,7 +632,9 @@ def prepare_transaction(self, txn: TransactionAPI) -> TransactionAPI:

txn.max_fee = txn.max_fee or MAX_FEE
txn = self._prepare_transaction(txn)
txn.signature = self.sign_transaction(txn)
signed_txn = self.sign_transaction(txn)
if signed_txn is not None:
return cast(TransactionAPI, signed_txn)
return txn

def _prepare_transaction(self, txn: TransactionAPI) -> TransactionAPI:
Expand All @@ -647,15 +652,15 @@ def _prepare_transaction(self, txn: TransactionAPI) -> TransactionAPI:
def get_fee_estimate(self, txn: TransactionAPI) -> int:
return self.provider.estimate_gas_cost(txn)

def handle_signature(self, sign_result, txn: TransactionAPI) -> TransactionSignature:
def handle_signature(self, sign_result, txn: TransactionAPI) -> TransactionAPI:
if not sign_result:
raise SignatureError("Failed to sign transaction.")

r = to_bytes(sign_result[0])
s = to_bytes(sign_result[1])
txn.signature = TransactionSignature(v=0, r=r, s=s)
self.check_signature(txn)
return txn.signature
return txn

def transfer(
self,
Expand Down Expand Up @@ -790,7 +795,7 @@ def deployments(self) -> List[StarknetAccountDeployment]:
def constructor_calldata(self) -> List[Any]:
return self.custom_constructor_calldata or super().constructor_calldata

def sign_transaction(self, txn: TransactionAPI) -> TransactionSignature:
def sign_transaction(self, txn: TransactionAPI, **signer_options) -> Optional[TransactionAPI]:
if not isinstance(txn, AccountTransaction):
raise StarknetAccountsError(
f"This account can only sign Starknet transactions (received={type(txn)}."
Expand Down Expand Up @@ -908,7 +913,9 @@ def public_key(self) -> str:
def nonce(self) -> int:
return super().nonce if self.deployed else 0

def sign_transaction(self, txn: TransactionAPI) -> TransactionSignature:
def sign_transaction(
self, txn: TransactionAPI, **signer_optins: Any
) -> Optional[TransactionAPI]:
if not isinstance(txn, AccountTransaction):
raise StarknetAccountsError(
f"This account can only sign Starknet transactions (received={type(txn)}."
Expand Down Expand Up @@ -972,14 +979,18 @@ def prepare_transaction(self, txn: TransactionAPI) -> TransactionAPI:
logger.debug("Fee-estimation related autosign enabled temporarily.")
original_value = self.__autosign
self.__autosign = True
txn.signature = self.sign_transaction(txn)
signed_txn = self.sign_transaction(txn)
if signed_txn is not None:
signed_txn = cast(TransactionAPI, signed_txn)
self.__autosign = original_value

txn.max_fee = ceil(self.get_fee_estimate(txn) * FEE_MARGIN_OF_ESTIMATION)

# Only sign the transaction if not aborting.
# This is the real and final signature.
txn.signature = self.sign_transaction(txn)
signed_txn = self.sign_transaction(txn)
if signed_txn is not None:
return cast(TransactionAPI, signed_txn)
return txn

def write(
Expand Down
29 changes: 17 additions & 12 deletions ape_starknet/ecosystems.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,22 +134,24 @@ def decode_returndata(self, abi: MethodABI, raw_data: List[int]) -> Any: # type

return decoded

def encode_calldata(
def encode_calldata(self, abi: Union[ConstructorABI, MethodABI], *args) -> List: # type: ignore
full_abi = abi.contract_type.abi if abi.contract_type is not None else [abi]
return self._encode_calldata(full_abi=full_abi, abi=abi, call_args=args)

def _encode_calldata(
self,
full_abi: List,
method_abi: Union[ConstructorABI, MethodABI],
call_args: Union[List, Tuple],
abi: Union[ConstructorABI, MethodABI],
call_args,
) -> List:
full_abi = [abi.dict() if hasattr(abi, "dict") else abi for abi in full_abi]
call_serializer = FunctionCallSerializer(
method_abi.dict(), identifier_manager_from_abi(full_abi)
)
call_serializer = FunctionCallSerializer(abi.dict(), identifier_manager_from_abi(full_abi))
pre_encoded_args: List[Any] = []
index = 0
last_index = min(len(method_abi.inputs), len(call_args)) - 1
last_index = min(len(abi.inputs), len(call_args)) - 1
did_process_array_during_arr_len = False

for call_arg, input_type in zip(call_args, method_abi.inputs):
for call_arg, input_type in zip(call_args, abi.inputs):
if str(input_type.type).endswith("*"):
if did_process_array_during_arr_len:
did_process_array_during_arr_len = False
Expand All @@ -161,7 +163,7 @@ def encode_calldata(
input_type.name is not None
and input_type.name.endswith("_len")
and index < last_index
and str(method_abi.inputs[index + 1].type).endswith("*")
and str(abi.inputs[index + 1].type).endswith("*")
):
pre_encoded_arg = self._pre_encode_value(call_arg)

Expand All @@ -180,7 +182,7 @@ def encode_calldata(
index += 1

calldata, _ = call_serializer.from_python(*pre_encoded_args)
return calldata
return list(calldata)

def _pre_encode_value(self, value: Any) -> Any:
if isinstance(value, dict):
Expand Down Expand Up @@ -263,7 +265,7 @@ def encode_deployment(
"Unable to encode deployment - missing full contract type for constructor."
)

constructor_arguments = self.encode_calldata(contract_type.abi, abi, args)
constructor_arguments = self._encode_calldata(contract_type.abi, abi, args)
return self.universal_deployer.create_deploy(class_hash, constructor_arguments, **kwargs)

def encode_transaction(
Expand All @@ -275,7 +277,7 @@ def encode_transaction(
raise ContractTypeNotFoundError(address)

arguments = list(args)
encoded_calldata = self.encode_calldata(contract_type.abi, abi, arguments)
encoded_calldata = self._encode_calldata(contract_type.abi, abi, arguments)
return InvokeFunctionTransaction(
receiver=address,
method_abi=abi,
Expand Down Expand Up @@ -450,3 +452,6 @@ def _get_proxy_info(

def decode_primitive_value(self, value: Any, output_type: Union[str, Tuple, List]) -> int:
return to_int(value)

def decode_calldata(self, abi: Union[ConstructorABI, MethodABI], calldata: bytes) -> Dict:
raise NotImplementedError()
21 changes: 13 additions & 8 deletions ape_starknet/provider.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import os
from dataclasses import asdict
from typing import Dict, Iterator, List, Optional, Union, cast
from typing import Any, Dict, Iterator, List, Optional, Union, cast
from urllib.error import HTTPError
from urllib.parse import urlparse
from urllib.request import urlopen

from ape.api import BlockAPI, ProviderAPI, ReceiptAPI, SubprocessProvider, TransactionAPI
from ape.api.networks import LOCAL_NETWORK_NAME
from ape.exceptions import ProviderNotConnectedError, TransactionError
from ape.exceptions import ProviderNotConnectedError, TransactionError, VirtualMachineError
from ape.logging import logger
from ape.types import AddressType, BlockID, ContractLog, LogFilter, RawAddress
from ape.utils import DEFAULT_NUMBER_OF_TEST_ACCOUNTS, cached_property, raises_not_implemented
from hexbytes import HexBytes
from requests import Session
from starknet_py.net.client_errors import ContractNotFoundError
from starknet_py.net.client_models import (
Expand Down Expand Up @@ -42,7 +43,6 @@
get_chain_id,
get_class_hash,
get_dict_from_tx_info,
handle_client_error,
handle_client_errors,
run_until_complete,
to_checksum_address,
Expand Down Expand Up @@ -157,9 +157,10 @@ def get_balance(self, address: AddressType) -> int:
return self.tokens.get_balance(address)

@handle_client_errors
def get_code(self, address: str) -> List[int]:
def get_code(self, address: str) -> bytes:
# NOTE: Always return truthy value for code so that Ape core works properly
return self.get_code_and_abi(address).bytecode or [ord(c) for c in "PROXY"]
code = self.get_code_and_abi(address).bytecode or [ord(c) for c in "PROXY"]
return b"".join([HexBytes(x) for x in code])

@handle_client_errors
def get_abi(self, address: str) -> List[Dict]:
Expand Down Expand Up @@ -189,7 +190,9 @@ def estimate_gas_cost(self, txn: StarknetTransaction) -> int:
if not txn.signature:
# Signature is required to estimate gas, unfortunately.
# the transaction is typically signed by this point, but not always.
txn.signature = self.account_manager[txn.receiver].sign_transaction(txn)
signed_txn = self.account_manager[txn.receiver].sign_transaction(txn)
if signed_txn is not None:
txn = cast(StarknetTransaction, signed_txn)

starknet_object = txn.as_starknet_object()
estimated_fee = self.connected_client.estimate_fee_sync(starknet_object)
Expand Down Expand Up @@ -263,6 +266,7 @@ def get_receipt(
self.starknet_client.get_transaction(txn_hash),
self.starknet_client.get_transaction_receipt(tx_hash=txn_hash),
)

data = {**asdict(receipt), **get_dict_from_tx_info(txn_info)}
was_deploy = False
# Handle __execute__ overhead. User only cares for target ABI.
Expand Down Expand Up @@ -389,8 +393,9 @@ def prepare_transaction(self, txn: TransactionAPI) -> TransactionAPI:

return txn

def get_virtual_machine_error(self, exception: Exception):
return handle_client_error(exception)
def get_virtual_machine_error(self, exception: Exception, **kwargs: Any) -> VirtualMachineError:
txn = kwargs.get("txn")
return VirtualMachineError(base_err=exception, txn=txn)

def get_code_and_abi(self, address: Union[str, AddressType, int]) -> ContractCode:
address_int = parse_address(address)
Expand Down
6 changes: 2 additions & 4 deletions ape_starknet/tokens.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,9 +248,6 @@ def get_balance(
address = cast(AddressType, account)

address_int = to_int(address)

# Al

network = self.provider.network.name
if not self.cache_enabled.get(network, False):
# Strictly use provider.
Expand Down Expand Up @@ -291,13 +288,14 @@ def transfer(
receiver: Union[int, AddressType, "BaseStarknetAccount"],
amount: int,
token: str = STARKNET_FEE_TOKEN_SYMBOL.lower(),
**kwargs,
):
receiver_int = to_int(receiver)
sender_account = cast(
"BaseStarknetAccount",
(self.account_container[sender] if isinstance(sender, (int, str)) else sender),
)
result = self[token].transfer(receiver_int, amount, sender=sender_account)
result = self[token].transfer(receiver_int, amount, sender=sender_account, **kwargs)

# NOTE: the fees paid by the sender get updated in `provider.send_transaction()`.
amount_int = self._convert_amount_to_int(amount)
Expand Down
15 changes: 9 additions & 6 deletions ape_starknet/transactions.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from copy import deepcopy
from dataclasses import asdict
from typing import Any, Dict, Iterator, List, Optional
from typing import Any, Dict, List, Optional

from ape.api import ReceiptAPI, TransactionAPI
from ape.exceptions import APINotImplementedError, TransactionError
Expand Down Expand Up @@ -236,7 +236,7 @@ def as_execute(self) -> "InvokeFunctionTransaction":
}
full_abi = OPEN_ZEPPELIN_ACCOUNT_CONTRACT_TYPE.abi
entire_call_data = [[account_call], stark_tx.calldata]
new_tx.data = new_tx.starknet.encode_calldata(full_abi, EXECUTE_ABI, entire_call_data)
new_tx.data = new_tx.starknet._encode_calldata(full_abi, EXECUTE_ABI, entire_call_data)

if new_tx.sender:
new_tx.receiver = new_tx.sender
Expand Down Expand Up @@ -330,7 +330,7 @@ def total_fees_paid(self) -> int:
@raises_not_implemented
def decode_logs( # type: ignore[empty-body]
self, abi: Optional[ContractEventABI] = None
) -> Iterator[ContractLog]:
) -> List[ContractLog]:
# Overriden in InvocationReceipt
pass

Expand Down Expand Up @@ -391,7 +391,7 @@ def return_value(self) -> Any:
def decode_logs(
self,
abi: Optional[ContractEventABI] = None,
) -> Iterator[ContractLog]:
) -> List[ContractLog]:

log_data_items: List[Dict] = []
for log in self.logs:
Expand All @@ -408,7 +408,7 @@ def decode_logs(
abi = [abi]

event_abis: List[EventABI] = [a.abi if not isinstance(a, EventABI) else a for a in abi]
yield from self.starknet.decode_logs(log_data_items, *event_abis)
return list(self.starknet.decode_logs(log_data_items, *event_abis))

else:
# If ABI is not provided, decode all events
Expand All @@ -422,14 +422,17 @@ def decode_logs(
address: {get_selector_from_name(e.name): e for e in contract.events}
for address, contract in contract_types.items()
}

decoded_logs: List[ContractLog] = []
for log in log_data_items:
contract_address = address_map[log["from_address"]]
if contract_address not in selectors:
continue

for event_key in log.get("keys", []):
event_abi = selectors[contract_address][event_key]
yield from self.starknet.decode_logs([log], event_abi)
decoded_logs.extend(list(self.starknet.decode_logs([log], event_abi)))
return decoded_logs


class ContractDeclaration(AccountTransactionReceipt):
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ write_to = "ape_starknet/version.py"

[tool.black]
line-length = 100
target-version = ['py38', 'py39']
target-version = ['py38', 'py39', 'py310']
include = '\.pyi?$'

[tool.pytest.ini_options]
Expand Down
Loading

0 comments on commit f90f96b

Please sign in to comment.